Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Thomas Karl Pietrowski 2015-12-19 10:34:45 +01:00
commit fb18cec049
123 changed files with 20817 additions and 15793 deletions

View File

@ -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(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 "") if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
# Extract Strings # Extract Strings
add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura) add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura)
@ -60,9 +63,12 @@ install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
install(DIRECTORY plugins DESTINATION lib/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) 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) 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) install(FILES cura.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
else() 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() endif()
include(CPackConfig.cmake)

16
CPackConfig.cmake Normal file
View File

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

View File

@ -13,7 +13,7 @@ Use [this](https://github.com/Ultimaker/Uranium/wiki/Bug-Reporting-Template) tem
For crashes and similar issues, please attach the following information: For crashes and similar issues, please attach the following information:
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output) * (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
* The Cura GUI log file, located at (Windows) $User/AppData/Local/cura/cura.log, (OSX) $User/.cura/cura.log * The Cura GUI log file, located at (Windows) $User/AppData/Local/cura/cura.log, (OSX) $User/.cura/cura.log, (Ubuntu) $USER/.local/share/cura
* The Cura Engine log, using Help -> Show Engine Log * The Cura Engine log, using Help -> Show Engine Log
Dependencies Dependencies
@ -41,6 +41,7 @@ Third party plugins
------------- -------------
* [Print time calculator](https://github.com/nallath/PrintCostCalculator) * [Print time calculator](https://github.com/nallath/PrintCostCalculator)
* [Post processing plugin](https://github.com/nallath/PostProcessingPlugin) * [Post processing plugin](https://github.com/nallath/PostProcessingPlugin)
* [Barbarian Plugin](https://github.com/nallath/BarbarianPlugin) Simple scale tool for imperial to metric.
Making profiles for other printers Making profiles for other printers
---------------------------------- ----------------------------------

View File

@ -8,5 +8,6 @@ TryExec=/usr/bin/cura_app.py
Icon=/usr/share/cura/resources/images/cura-icon.png Icon=/usr/share/cura/resources/images/cura-icon.png
Terminal=false Terminal=false
Type=Application Type=Application
MimeType=application/sla
Categories=Graphics; Categories=Graphics;
Keywords=3D;Printing; Keywords=3D;Printing;

View File

@ -12,6 +12,9 @@ from UM.Math.Color import Color
from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Polygon import Polygon from UM.Math.Polygon import Polygon
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
import numpy import numpy
class BuildVolume(SceneNode): class BuildVolume(SceneNode):
@ -24,20 +27,24 @@ class BuildVolume(SceneNode):
self._height = 0 self._height = 0
self._depth = 0 self._depth = 0
self._material = None self._shader = None
self._grid_mesh = None self._grid_mesh = None
self._grid_material = None self._grid_shader = None
self._disallowed_areas = [] self._disallowed_areas = []
self._disallowed_area_mesh = None self._disallowed_area_mesh = None
self.setCalculateBoundingBox(False) self.setCalculateBoundingBox(False)
self._active_profile = None
self._active_instance = None self._active_instance = None
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveInstanceChanged) Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveInstanceChanged)
self._onActiveInstanceChanged() self._onActiveInstanceChanged()
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
self._onActiveProfileChanged()
def setWidth(self, width): def setWidth(self, width):
if width: self._width = width if width: self._width = width
@ -57,78 +64,72 @@ class BuildVolume(SceneNode):
if not self.getMeshData(): if not self.getMeshData():
return True return True
if not self._material: if not self._shader:
self._material = renderer.createMaterial( self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
Resources.getPath(Resources.Shaders, "basic.vert"), self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader"))
Resources.getPath(Resources.Shaders, "vertexcolor.frag")
)
self._grid_material = renderer.createMaterial(
Resources.getPath(Resources.Shaders, "basic.vert"),
Resources.getPath(Resources.Shaders, "grid.frag")
)
self._grid_material.setUniformValue("u_gridColor0", Color(245, 245, 245, 255))
self._grid_material.setUniformValue("u_gridColor1", Color(205, 202, 201, 255))
renderer.queueNode(self, material = self._material, mode = Renderer.RenderLines) renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines)
renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material, force_single_sided = True) renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True)
if self._disallowed_area_mesh: if self._disallowed_area_mesh:
renderer.queueNode(self, mesh = self._disallowed_area_mesh, material = self._material) renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
return True return True
def rebuild(self): def rebuild(self):
if self._width == 0 or self._height == 0 or self._depth == 0: if self._width == 0 or self._height == 0 or self._depth == 0:
return return
minW = -self._width / 2 min_w = -self._width / 2
maxW = self._width / 2 max_w = self._width / 2
minH = 0.0 min_h = 0.0
maxH = self._height max_h = self._height
minD = -self._depth / 2 min_d = -self._depth / 2
maxD = self._depth / 2 max_d = self._depth / 2
mb = MeshBuilder() mb = MeshBuilder()
mb.addLine(Vector(minW, minH, minD), Vector(maxW, minH, minD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, minH, minD), Vector(minW, maxH, minD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, maxH, minD), Vector(maxW, maxH, minD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(maxW, minH, minD), Vector(maxW, maxH, minD), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, minH, maxD), Vector(maxW, minH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, minH, maxD), Vector(minW, maxH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, maxH, maxD), Vector(maxW, maxH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(maxW, minH, maxD), Vector(maxW, maxH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, minH, minD), Vector(minW, minH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(maxW, minH, minD), Vector(maxW, minH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(minW, maxH, minD), Vector(minW, maxH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
mb.addLine(Vector(maxW, maxH, minD), Vector(maxW, maxH, maxD), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
self.setMeshData(mb.getData()) self.setMeshData(mb.getData())
mb = MeshBuilder() mb = MeshBuilder()
mb.addQuad( mb.addQuad(
Vector(minW, minH, minD), Vector(min_w, min_h - 0.2, min_d),
Vector(maxW, minH, minD), Vector(max_w, min_h - 0.2, min_d),
Vector(maxW, minH, maxD), Vector(max_w, min_h - 0.2, max_d),
Vector(minW, minH, maxD) Vector(min_w, min_h - 0.2, max_d)
) )
self._grid_mesh = mb.getData() self._grid_mesh = mb.getData()
for n in range(0, 6): for n in range(0, 6):
v = self._grid_mesh.getVertex(n) v = self._grid_mesh.getVertex(n)
self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2])
disallowed_area_height = 0.1
disallowed_area_size = 0 disallowed_area_size = 0
if self._disallowed_areas: if self._disallowed_areas:
mb = MeshBuilder() mb = MeshBuilder()
color = Color(0.0, 0.0, 0.0, 0.15)
for polygon in self._disallowed_areas: for polygon in self._disallowed_areas:
points = polygon.getPoints() points = polygon.getPoints()
mb.addQuad( first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
Vector(points[0, 0], 0.1, points[0, 1]), previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
Vector(points[1, 0], 0.1, points[1, 1]), for point in points:
Vector(points[2, 0], 0.1, points[2, 1]), new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d))
Vector(points[3, 0], 0.1, points[3, 1]), mb.addFace(first, previous_point, new_point, color = color)
color = Color(174, 174, 174, 255) previous_point = new_point
)
# Find the largest disallowed area to exclude it from the maximum scale bounds # Find the largest disallowed area to exclude it from the maximum scale bounds
size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
disallowed_area_size = max(size, disallowed_area_size) disallowed_area_size = max(size, disallowed_area_size)
@ -137,24 +138,17 @@ class BuildVolume(SceneNode):
else: else:
self._disallowed_area_mesh = None self._disallowed_area_mesh = None
self._aabb = AxisAlignedBox(minimum = Vector(minW, minH - 1.0, minD), maximum = Vector(maxW, maxH, maxD)) self._aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
skirt_size = 0.0 skirt_size = 0.0
#profile = Application.getInstance().getMachineManager().getActiveProfile() profile = Application.getInstance().getMachineManager().getActiveProfile()
#if profile: if profile:
#if profile.getSettingValue("adhesion_type") == "skirt": skirt_size = self._getSkirtSize(profile)
#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")
scale_to_max_bounds = AxisAlignedBox( scale_to_max_bounds = AxisAlignedBox(
minimum = Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size), minimum = Vector(min_w + skirt_size, min_h, min_d + skirt_size + disallowed_area_size),
maximum = Vector(maxW - skirt_size, maxH, maxD - skirt_size - disallowed_area_size) maximum = Vector(max_w - skirt_size, max_h, max_d - skirt_size - disallowed_area_size)
) )
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
@ -167,12 +161,107 @@ class BuildVolume(SceneNode):
self._height = self._active_instance.getMachineSettingValue("machine_height") self._height = self._active_instance.getMachineSettingValue("machine_height")
self._depth = self._active_instance.getMachineSettingValue("machine_depth") self._depth = self._active_instance.getMachineSettingValue("machine_depth")
disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas") self._updateDisallowedAreas()
areas = []
if disallowed_areas:
for area in disallowed_areas:
areas.append(Polygon(numpy.array(area, numpy.float32)))
self._disallowed_areas = areas
self.rebuild() 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":
skirt_size = profile.getSettingValue("brim_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_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]

View File

@ -31,6 +31,8 @@ class ConvexHullJob(Job):
self._node.callDecoration("setConvexHullJob", None) self._node.callDecoration("setConvexHullJob", None)
return return
Job.yieldThread()
else: else:
if not self._node.getMeshData(): if not self._node.getMeshData():
return return

View File

@ -7,6 +7,8 @@ from UM.Math.Color import Color
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
from UM.Mesh.MeshData import MeshData from UM.Mesh.MeshData import MeshData
from UM.View.GL.OpenGL import OpenGL
import numpy import numpy
class ConvexHullNode(SceneNode): class ConvexHullNode(SceneNode):
@ -15,21 +17,21 @@ class ConvexHullNode(SceneNode):
self.setCalculateBoundingBox(False) self.setCalculateBoundingBox(False)
self._material = None self._shader = None
self._original_parent = parent self._original_parent = parent
self._inherit_orientation = False self._inherit_orientation = False
self._inherit_scale = False self._inherit_scale = False
self._color = Color(35, 35, 35, 0.5) self._color = Color(35, 35, 35, 128)
self._node = node self._node = node
self._node.transformationChanged.connect(self._onNodePositionChanged) self._node.transformationChanged.connect(self._onNodePositionChanged)
self._node.parentChanged.connect(self._onNodeParentChanged) self._node.parentChanged.connect(self._onNodeParentChanged)
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
self._onNodeDecoratorsChanged(self._node) self._onNodeDecoratorsChanged(self._node)
self.convexHullHeadMesh = None self._convex_hull_head_mesh = None
self._hull = hull self._hull = hull
hull_points = self._hull.getPoints() hull_points = self._hull.getPoints()
@ -38,17 +40,17 @@ class ConvexHullNode(SceneNode):
self.setMeshData(hull_mesh) self.setMeshData(hull_mesh)
convex_hull_head = self._node.callDecoration("getConvexHullHead") convex_hull_head = self._node.callDecoration("getConvexHullHead")
if convex_hull_head: if convex_hull_head:
self.convexHullHeadMesh = self.createHullMesh(convex_hull_head.getPoints()) self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
def createHullMesh(self, hull_points): def createHullMesh(self, hull_points):
mesh = MeshData() mesh = MeshData()
if len(hull_points) > 3: if len(hull_points) > 3:
center = (hull_points.min(0) + hull_points.max(0)) / 2.0 center = (hull_points.min(0) + hull_points.max(0)) / 2.0
mesh.addVertex(center[0], 0.1, center[1]) mesh.addVertex(center[0], -0.1, center[1])
else: else:
return None return None
for point in hull_points: for point in hull_points:
mesh.addVertex(point[0], 0.1, point[1]) mesh.addVertex(point[0], -0.1, point[1])
indices = [] indices = []
for i in range(len(hull_points) - 1): for i in range(len(hull_points) - 1):
indices.append([0, i + 1, i + 2]) indices.append([0, i + 1, i + 2])
@ -62,14 +64,14 @@ class ConvexHullNode(SceneNode):
return self._node return self._node
def render(self, renderer): def render(self, renderer):
if not self._material: if not self._shader:
self._material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "color.frag")) self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
self._shader.setUniformValue("u_color", self._color)
if self.getParent(): if self.getParent():
self._material.setUniformValue("u_color", self._color) renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8)
renderer.queueNode(self, material = self._material, transparent = True) if self._convex_hull_head_mesh:
if self.convexHullHeadMesh: renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
renderer.queueNode(self, material = self._material,transparent = True, mesh = self.convexHullHeadMesh)
return True return True

View File

@ -8,9 +8,15 @@ from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTex
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
def show(type, value, tb): def show(exception_type, value, tb):
if not hasattr(sys, "frozen"): debug_mode = False
traceback.print_exception(type, value, tb) if QCoreApplication.instance():
debug_mode = QCoreApplication.instance().getCommandLineOption("debug-mode", False)
traceback.print_exception(exception_type, value, tb)
if not debug_mode:
return
application = QCoreApplication.instance() application = QCoreApplication.instance()
if not application: if not application:
@ -34,7 +40,7 @@ def show(type, value, tb):
except: except:
version = "Unknown" version = "Unknown"
trace = "".join(traceback.format_exception(type, value, tb)) trace = "".join(traceback.format_exception(exception_type, value, tb))
crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}" crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}"
crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace) crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace)
@ -44,7 +50,7 @@ def show(type, value, tb):
buttons = QDialogButtonBox(QDialogButtonBox.Close, dialog) buttons = QDialogButtonBox(QDialogButtonBox.Close, dialog)
layout.addWidget(buttons) layout.addWidget(buttons)
buttons.addButton(catalog.i18nc("@action:button", "Open Web Page"), QDialogButtonBox.HelpRole) buttons.addButton(catalog.i18nc("@action:button", "Open Web Page"), QDialogButtonBox.HelpRole)
buttons.rejected.connect(lambda: dialog.close()) buttons.rejected.connect(dialog.close)
buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues")) buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues"))
dialog.exec_() dialog.exec_()

View File

@ -3,11 +3,6 @@
import platform import platform
if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
if platform.linux_distribution()[0] in ("Ubuntu", ): # Just in case it also happens on Debian, so it can be added
from OpenGL import GL
from UM.Qt.QtApplication import QtApplication from UM.Qt.QtApplication import QtApplication
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera from UM.Scene.Camera import Camera
@ -15,6 +10,7 @@ from UM.Scene.Platform import Platform
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
from UM.Math.Matrix import Matrix from UM.Math.Matrix import Matrix
from UM.Math.Quaternion import Quaternion from UM.Math.Quaternion import Quaternion
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Resources import Resources from UM.Resources import Resources
from UM.Scene.ToolHandle import ToolHandle from UM.Scene.ToolHandle import ToolHandle
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -44,6 +40,8 @@ from . import CameraAnimation
from . import PrintInformation from . import PrintInformation
from . import CuraActions from . import CuraActions
from . import MultiMaterialDecorator from . import MultiMaterialDecorator
from . import ZOffsetDecorator
from . import CuraSplashScreen
from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from PyQt5.QtGui import QColor, QIcon from PyQt5.QtGui import QColor, QIcon
@ -57,6 +55,16 @@ import numpy
import copy import copy
numpy.seterr(all="ignore") numpy.seterr(all="ignore")
if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
if platform.linux_distribution()[0] in ("Ubuntu", ): # Just in case it also happens on Debian, so it can be added
from OpenGL import GL
try:
from cura.CuraVersion import CuraVersion
except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
class CuraApplication(QtApplication): class CuraApplication(QtApplication):
class ResourceTypes: class ResourceTypes:
QmlFiles = Resources.UserType + 1 QmlFiles = Resources.UserType + 1
@ -68,7 +76,7 @@ class CuraApplication(QtApplication):
if not hasattr(sys, "frozen"): if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
super().__init__(name = "cura", version = "15.09.82") super().__init__(name = "cura", version = CuraVersion)
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@ -90,6 +98,8 @@ class CuraApplication(QtApplication):
self._i18n_catalog = None self._i18n_catalog = None
self._previous_active_tool = None self._previous_active_tool = None
self._platform_activity = False self._platform_activity = False
self._scene_boundingbox = AxisAlignedBox()
self._job_name = None
self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged) self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged)
self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested) self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested)
@ -131,8 +141,12 @@ class CuraApplication(QtApplication):
def addCommandLineOptions(self, parser): def addCommandLineOptions(self, parser):
super().addCommandLineOptions(parser) super().addCommandLineOptions(parser)
parser.add_argument("file", nargs="*", help="Files to load after starting the application.") parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.")
def run(self): def run(self):
if "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" not 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"); self._i18n_catalog = i18nCatalog("cura");
i18nCatalog.setTagReplacements({ i18nCatalog.setTagReplacements({
@ -144,7 +158,7 @@ class CuraApplication(QtApplication):
controller = self.getController() controller = self.getController()
controller.setActiveView("MeshView") controller.setActiveView("SolidView")
controller.setCameraTool("CameraTool") controller.setCameraTool("CameraTool")
controller.setSelectionTool("SelectionTool") controller.setSelectionTool("SelectionTool")
@ -159,31 +173,26 @@ class CuraApplication(QtApplication):
self._volume = BuildVolume.BuildVolume(root) self._volume = BuildVolume.BuildVolume(root)
self.getRenderer().setLightPosition(Vector(0, 150, 0))
self.getRenderer().setBackgroundColor(QColor(245, 245, 245)) self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume) self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
camera = Camera("3d", root) camera = Camera("3d", root)
camera.setPosition(Vector(-150, 150, 300)) camera.setPosition(Vector(-80, 250, 700))
camera.setPerspective(True) camera.setPerspective(True)
camera.lookAt(Vector(0, 0, 0)) camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d")
self.getController().getTool("CameraTool").setOrigin(Vector(0, 100, 0))
self._camera_animation = CameraAnimation.CameraAnimation() self._camera_animation = CameraAnimation.CameraAnimation()
self._camera_animation.setCameraTool(self.getController().getTool("CameraTool")) self._camera_animation.setCameraTool(self.getController().getTool("CameraTool"))
controller.getScene().setActiveCamera("3d")
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface...")) self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml")) self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self.initializeEngine() self.initializeEngine()
manager = self.getMachineManager()
if not self.getMachineManager().getMachineInstances():
self.requestAddPrinter.emit()
if self._engine.rootObjects: if self._engine.rootObjects:
self.closeSplash() self.closeSplash()
@ -232,35 +241,58 @@ class CuraApplication(QtApplication):
requestAddPrinter = pyqtSignal() requestAddPrinter = pyqtSignal()
activityChanged = pyqtSignal() activityChanged = pyqtSignal()
sceneBoundingBoxChanged = pyqtSignal()
@pyqtProperty(bool, notify = activityChanged) @pyqtProperty(bool, notify = activityChanged)
def getPlatformActivity(self): def getPlatformActivity(self):
return self._platform_activity return self._platform_activity
@pyqtProperty(str, notify = sceneBoundingBoxChanged)
def getSceneBoundingBoxString(self):
return self._i18n_catalog.i18nc("@info", "%.1f x %.1f x %.1f mm") % (self._scene_boundingbox.width.item(), self._scene_boundingbox.depth.item(), self._scene_boundingbox.height.item())
def updatePlatformActivity(self, node = None): def updatePlatformActivity(self, node = None):
count = 0 count = 0
scene_boundingbox = AxisAlignedBox()
for node in DepthFirstIterator(self.getController().getScene().getRoot()): for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode or not node.getMeshData(): if type(node) is not SceneNode or not node.getMeshData():
continue continue
count += 1 count += 1
scene_boundingbox += node.getBoundingBox()
if repr(self._scene_boundingbox) != repr(scene_boundingbox):
self._scene_boundingbox = scene_boundingbox
self.sceneBoundingBoxChanged.emit()
self._platform_activity = True if count > 0 else False self._platform_activity = True if count > 0 else False
self.activityChanged.emit() self.activityChanged.emit()
@pyqtSlot(str)
def setJobName(self, name):
if self._job_name != name:
self._job_name = name
self.jobNameChanged.emit()
jobNameChanged = pyqtSignal()
@pyqtProperty(str, notify = jobNameChanged)
def jobName(self):
return self._job_name
## Remove an object from the scene ## Remove an object from the scene
@pyqtSlot("quint64") @pyqtSlot("quint64")
def deleteObject(self, object_id): def deleteObject(self, object_id):
object = self.getController().getScene().findObject(object_id) node = self.getController().getScene().findObject(object_id)
if not object and object_id != 0: #Workaround for tool handles overlapping the selected object if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
object = Selection.getSelectedObject(0) node = Selection.getSelectedObject(0)
if object: if node:
if object.getParent(): if node.getParent():
group_node = object.getParent() group_node = node.getParent()
if not group_node.callDecoration("isGroup"): if not group_node.callDecoration("isGroup"):
op = RemoveSceneNodeOperation(object) op = RemoveSceneNodeOperation(node)
else: else:
while group_node.getParent().callDecoration("isGroup"): while group_node.getParent().callDecoration("isGroup"):
group_node = group_node.getParent() group_node = group_node.getParent()
@ -294,10 +326,15 @@ class CuraApplication(QtApplication):
@pyqtSlot("quint64") @pyqtSlot("quint64")
def centerObject(self, object_id): def centerObject(self, object_id):
node = self.getController().getScene().findObject(object_id) node = self.getController().getScene().findObject(object_id)
if node.getParent() and node.getParent().callDecoration("isGroup"):
node = node.getParent()
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
node = Selection.getSelectedObject(0) node = Selection.getSelectedObject(0)
if not node:
return
if node.getParent() and node.getParent().callDecoration("isGroup"):
node = node.getParent()
if node: if node:
op = SetTransformOperation(node, Vector()) op = SetTransformOperation(node, Vector())
op.push() op.push()
@ -335,14 +372,12 @@ class CuraApplication(QtApplication):
continue #Grouped nodes don't need resetting as their parent (the group) is resetted) continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
nodes.append(node) nodes.append(node)
if nodes: if nodes:
op = GroupedOperation() op = GroupedOperation()
for node in nodes: for node in nodes:
# Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
move_distance = node.getBoundingBox().center.y op.addOperation(SetTransformOperation(node, Vector(0,0,0)))
if move_distance <= 0:
move_distance = -node.getBoundingBox().bottom
op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0)))
op.push() op.push()
@ -364,10 +399,8 @@ class CuraApplication(QtApplication):
for node in nodes: for node in nodes:
# Ensure that the object is above the build platform # Ensure that the object is above the build platform
move_distance = node.getBoundingBox().center.y node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
if move_distance <= 0: op.addOperation(SetTransformOperation(node, Vector(0,0,0), Quaternion(), Vector(1, 1, 1)))
move_distance = -node.getBoundingBox().bottom
op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0), Quaternion(), Vector(1, 1, 1)))
op.push() op.push()
@ -407,6 +440,7 @@ class CuraApplication(QtApplication):
return log return log
recentFilesChanged = pyqtSignal() recentFilesChanged = pyqtSignal()
@pyqtProperty("QVariantList", notify = recentFilesChanged) @pyqtProperty("QVariantList", notify = recentFilesChanged)
def recentFiles(self): def recentFiles(self):
return self._recent_files return self._recent_files
@ -421,6 +455,7 @@ class CuraApplication(QtApplication):
self.expandedCategoriesChanged.emit() self.expandedCategoriesChanged.emit()
expandedCategoriesChanged = pyqtSignal() expandedCategoriesChanged = pyqtSignal()
@pyqtProperty("QStringList", notify = expandedCategoriesChanged) @pyqtProperty("QStringList", notify = expandedCategoriesChanged)
def expandedCategories(self): def expandedCategories(self):
return Preferences.getInstance().getValue("cura/categories_expanded").split(";") return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
@ -464,17 +499,20 @@ class CuraApplication(QtApplication):
group_decorator = GroupDecorator() group_decorator = GroupDecorator()
group_node.addDecorator(group_decorator) group_node.addDecorator(group_decorator)
group_node.setParent(self.getController().getScene().getRoot()) group_node.setParent(self.getController().getScene().getRoot())
center = Selection.getSelectionCenter()
group_node.setPosition(center)
group_node.setCenterPosition(center)
for node in Selection.getAllSelectedObjects(): for node in Selection.getAllSelectedObjects():
world = node.getWorldPosition()
node.setParent(group_node) node.setParent(group_node)
group_node.setCenterPosition(group_node.getBoundingBox().center) node.setPosition(world - center)
#group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0))
group_node.translate(group_node.getBoundingBox().center)
for node in group_node.getChildren(): for node in group_node.getChildren():
Selection.remove(node) Selection.remove(node)
Selection.add(group_node) Selection.add(group_node)
@pyqtSlot() @pyqtSlot()
def ungroupSelected(self): def ungroupSelected(self):
ungrouped_nodes = [] ungrouped_nodes = []
@ -485,12 +523,11 @@ class CuraApplication(QtApplication):
for child in node.getChildren(): for child in node.getChildren():
if type(child) is SceneNode: if type(child) is SceneNode:
children_to_move.append(child) children_to_move.append(child)
for child in children_to_move: for child in children_to_move:
position = child.getWorldPosition()
child.setParent(node.getParent()) child.setParent(node.getParent())
print(node.getPosition()) child.setPosition(position - node.getParent().getWorldPosition())
child.translate(node.getPosition())
child.setPosition(child.getPosition().scale(node.getScale()))
child.scale(node.getScale()) child.scale(node.getScale())
child.rotate(node.getOrientation()) child.rotate(node.getOrientation())
@ -501,6 +538,9 @@ class CuraApplication(QtApplication):
for node in ungrouped_nodes: for node in ungrouped_nodes:
Selection.remove(node) Selection.remove(node)
def _createSplashScreen(self):
return CuraSplashScreen.CuraSplashScreen()
def _onActiveMachineChanged(self): def _onActiveMachineChanged(self):
machine = self.getMachineManager().getActiveMachineInstance() machine = self.getMachineManager().getActiveMachineInstance()
if machine: if machine:
@ -536,6 +576,8 @@ class CuraApplication(QtApplication):
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
op.push() op.push()
self.getController().getScene().sceneChanged.emit(node) #Force scene change.
def _onJobFinished(self, job): def _onJobFinished(self, job):
if type(job) is not ReadMeshJob or not job.getResult(): if type(job) is not ReadMeshJob or not job.getResult():
return return

29
cura/CuraSplashScreen.py Normal file
View File

@ -0,0 +1,29 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QColor, QFont
from PyQt5.QtWidgets import QSplashScreen
from UM.Resources import Resources
from UM.Application import Application
class CuraSplashScreen(QSplashScreen):
def __init__(self):
super().__init__()
self.setPixmap(QPixmap(Resources.getPath(Resources.Images, "cura.png")))
def drawContents(self, painter):
painter.save()
painter.setPen(QColor(0, 0, 0, 255))
version = Application.getInstance().getVersion().split("-")
painter.setFont(QFont("Proxima Nova Rg", 20))
painter.drawText(0, 0, 203, 230, Qt.AlignRight | Qt.AlignBottom, version[0])
if len(version) > 1:
painter.setFont(QFont("Proxima Nova Rg", 12))
painter.drawText(0, 0, 203, 255, Qt.AlignRight | Qt.AlignBottom, version[1])
painter.restore()
super().drawContents(painter)

4
cura/CuraVersion.py.in Normal file
View 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@"

View File

@ -63,6 +63,7 @@ class LayerData(MeshData):
offset = data.build(offset, vertices, colors, indices) offset = data.build(offset, vertices, colors, indices)
self._element_counts[layer] = data.elementCount self._element_counts[layer] = data.elementCount
self.clear()
self.addVertices(vertices) self.addVertices(vertices)
self.addColors(colors) self.addColors(colors)
self.addIndices(indices.flatten()) self.addIndices(indices.flatten())
@ -107,7 +108,7 @@ class Layer():
def build(self, offset, vertices, colors, indices): def build(self, offset, vertices, colors, indices):
result = offset result = offset
for polygon in self._polygons: for polygon in self._polygons:
if polygon._type == Polygon.InfillType or polygon._type == Polygon.SupportInfillType: if polygon.type == Polygon.InfillType or polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
continue continue
polygon.build(result, vertices, colors, indices) polygon.build(result, vertices, colors, indices)
@ -117,14 +118,27 @@ class Layer():
return result return result
def createMesh(self): def createMesh(self):
return self.createMeshOrJumps(True)
def createJumps(self):
return self.createMeshOrJumps(False)
def createMeshOrJumps(self, make_mesh):
builder = MeshBuilder() builder = MeshBuilder()
for polygon in self._polygons: for polygon in self._polygons:
if make_mesh and (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
continue
if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
continue
poly_color = polygon.getColor() poly_color = polygon.getColor()
points = numpy.copy(polygon.data) points = numpy.copy(polygon.data)
if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
points[:,1] -= 0.01 points[:,1] -= 0.01
if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
points[:,1] += 0.01
# Calculate normals for the entire polygon using numpy. # Calculate normals for the entire polygon using numpy.
normals = numpy.copy(points) normals = numpy.copy(points)
@ -175,6 +189,8 @@ class Polygon():
SkirtType = 5 SkirtType = 5
InfillType = 6 InfillType = 6
SupportInfillType = 7 SupportInfillType = 7
MoveCombingType = 8
MoveRetractionType = 9
def __init__(self, mesh, type, data, line_width): def __init__(self, mesh, type, data, line_width):
super().__init__() super().__init__()
@ -185,18 +201,14 @@ class Polygon():
def build(self, offset, vertices, colors, indices): def build(self, offset, vertices, colors, indices):
self._begin = offset self._begin = offset
self._end = self._begin + len(self._data) - 1
color = self.getColor() color = self.getColor()
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a) 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[self._begin:self._end + 1, :] = self._data[:, :]
vertices[offset + i, :] = self._data[i, :] colors[self._begin:self._end + 1, :] = color
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
for i in range(self._begin, self._end): for i in range(self._begin, self._end):
indices[i, 0] = i indices[i, 0] = i
@ -220,6 +232,10 @@ class Polygon():
return Color(1.0, 0.74, 0.0, 1.0) return Color(1.0, 0.74, 0.0, 1.0)
elif self._type == self.SupportInfillType: elif self._type == self.SupportInfillType:
return Color(0.0, 1.0, 1.0, 1.0) return Color(0.0, 1.0, 1.0, 1.0)
elif self._type == self.MoveCombingType:
return Color(0.0, 0.0, 1.0, 1.0)
elif self._type == self.MoveRetractionType:
return Color(0.5, 0.5, 1.0, 1.0)
else: else:
return Color(1.0, 1.0, 1.0, 1.0) return Color(1.0, 1.0, 1.0, 1.0)

View File

@ -45,7 +45,7 @@ class OneAtATimeIterator(Iterator.Iterator):
# This does not decrease the worst case running time, but should improve it in most cases. # This does not decrease the worst case running time, but should improve it in most cases.
sorted(node_list, key = cmp_to_key(self._calculateScore)) sorted(node_list, key = cmp_to_key(self._calculateScore))
todo_node_list = [_objectOrder([], node_list)] todo_node_list = [_ObjectOrder([], node_list)]
while len(todo_node_list) > 0: while len(todo_node_list) > 0:
current = todo_node_list.pop() current = todo_node_list.pop()
for node in current.todo: for node in current.todo:
@ -61,7 +61,7 @@ class OneAtATimeIterator(Iterator.Iterator):
self._node_stack = new_order self._node_stack = new_order
return return
todo_node_list.append(_objectOrder(new_order, new_todo_list)) todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
self._node_stack = [] #No result found! self._node_stack = [] #No result found!
@ -99,7 +99,7 @@ class OneAtATimeIterator(Iterator.Iterator):
## Internal object used to keep track of a possible order in which to print objects. ## Internal object used to keep track of a possible order in which to print objects.
class _objectOrder(): class _ObjectOrder():
def __init__(self, order, todo): def __init__(self, order, todo):
""" """
:param order: List of indexes in which to print objects, ordered by printing order. :param order: List of indexes in which to print objects, ordered by printing order.

View File

@ -17,6 +17,7 @@ from cura.ConvexHullDecorator import ConvexHullDecorator
from . import PlatformPhysicsOperation from . import PlatformPhysicsOperation
from . import ConvexHullJob from . import ConvexHullJob
from . import ZOffsetDecorator
import time import time
import threading import threading
@ -69,8 +70,12 @@ class PlatformPhysics:
# Move it downwards if bottom is above platform # Move it downwards if bottom is above platform
move_vector = Vector() move_vector = Vector()
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
if bbox.bottom > 0: if bbox.bottom > 0:
move_vector.setY(-bbox.bottom) move_vector.setY(-bbox.bottom + z_offset)
elif bbox.bottom < z_offset:
move_vector.setY((-bbox.bottom) - z_offset)
#if not Float.fuzzyCompare(bbox.bottom, 0.0): #if not Float.fuzzyCompare(bbox.bottom, 0.0):
# pass#move_vector.setY(-bbox.bottom) # pass#move_vector.setY(-bbox.bottom)
@ -127,8 +132,8 @@ class PlatformPhysics:
if overlap is None: if overlap is None:
continue continue
move_vector.setX(overlap[0] * 1.01) move_vector.setX(overlap[0] * 1.1)
move_vector.setZ(overlap[1] * 1.01) move_vector.setZ(overlap[1] * 1.1)
convex_hull = node.callDecoration("getConvexHull") convex_hull = node.callDecoration("getConvexHull")
if convex_hull: if convex_hull:
if not convex_hull.isValid(): if not convex_hull.isValid():
@ -149,5 +154,16 @@ class PlatformPhysics:
self._enabled = False self._enabled = False
def _onToolOperationStopped(self, tool): def _onToolOperationStopped(self, tool):
if tool.getPluginId() == "TranslateTool":
for node in Selection.getAllSelectedObjects():
if node.getBoundingBox().bottom < 0:
if not node.getDecorator(ZOffsetDecorator.ZOffsetDecorator):
node.addDecorator(ZOffsetDecorator.ZOffsetDecorator())
node.callDecoration("setZOffset", node.getBoundingBox().bottom)
else:
if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator):
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
self._enabled = True self._enabled = True
self._onChangeTimerFinished() self._onChangeTimerFinished()

13
cura/ZOffsetDecorator.py Normal file
View File

@ -0,0 +1,13 @@
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
## A decorator that stores the amount an object has been moved below the platform.
class ZOffsetDecorator(SceneNodeDecorator):
def __init__(self):
self._z_offset = 0
def setZOffset(self, offset):
print("setZOffset", offset)
self._z_offset = offset
def getZOffset(self):
return self._z_offset

View File

@ -4,6 +4,7 @@
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
import sys import sys
import os
def exceptHook(type, value, traceback): def exceptHook(type, value, traceback):
import cura.CrashHandler import cura.CrashHandler
@ -11,7 +12,22 @@ def exceptHook(type, value, traceback):
sys.excepthook = exceptHook sys.excepthook = exceptHook
import cura.CuraApplication try:
from google.protobuf.pyext import _message
except ImportError:
pass
else:
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp"
if True: # To make the code style checker stop complaining
import cura.CuraApplication
if sys.platform == "win32" and hasattr(sys, "frozen"):
import os
dirpath = os.path.expanduser("~/AppData/Local/cura/")
os.makedirs(dirpath, exist_ok = True)
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
app = cura.CuraApplication.CuraApplication.getInstance() app = cura.CuraApplication.CuraApplication.getInstance()
app.run() app.run()

View File

@ -10,6 +10,7 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.GroupDecorator import GroupDecorator from UM.Scene.GroupDecorator import GroupDecorator
from UM.Math.Quaternion import Quaternion from UM.Math.Quaternion import Quaternion
from UM.Job import Job
import os import os
import struct import struct
@ -36,12 +37,16 @@ class ThreeMFReader(MeshReader):
if extension.lower() == self._supported_extension: if extension.lower() == self._supported_extension:
result = SceneNode() result = SceneNode()
# The base object of 3mf is a zipped archive. # The base object of 3mf is a zipped archive.
archive = zipfile.ZipFile(file_name, 'r') archive = zipfile.ZipFile(file_name, "r")
try: try:
root = ET.parse(archive.open("3D/3dmodel.model")) root = ET.parse(archive.open("3D/3dmodel.model"))
# There can be multiple objects, try to load all of them. # There can be multiple objects, try to load all of them.
objects = root.findall("./3mf:resources/3mf:object", self._namespaces) objects = root.findall("./3mf:resources/3mf:object", self._namespaces)
if len(objects) == 0:
Logger.log("w", "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format", file_name)
return None
for object in objects: for object in objects:
mesh = MeshData() mesh = MeshData()
node = SceneNode() node = SceneNode()
@ -49,22 +54,25 @@ class ThreeMFReader(MeshReader):
#for vertex in object.mesh.vertices.vertex: #for vertex in object.mesh.vertices.vertex:
for vertex in object.findall(".//3mf:vertex", self._namespaces): for vertex in object.findall(".//3mf:vertex", self._namespaces):
vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
Job.yieldThread()
triangles = object.findall(".//3mf:triangle", self._namespaces) triangles = object.findall(".//3mf:triangle", self._namespaces)
mesh.reserveFaceCount(len(triangles)) mesh.reserveFaceCount(len(triangles))
#for triangle in object.mesh.triangles.triangle: #for triangle in object.mesh.triangles.triangle:
for triangle in triangles: for triangle in triangles:
v1 = int(triangle.get("v1")) v1 = int(triangle.get("v1"))
v2 = int(triangle.get("v2")) v2 = int(triangle.get("v2"))
v3 = int(triangle.get("v3")) 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]) 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])
#TODO: We currently do not check for normals and simply recalculate them. Job.yieldThread()
#TODO: We currently do not check for normals and simply recalculate them.
mesh.calculateNormals() mesh.calculateNormals()
node.setMeshData(mesh) node.setMeshData(mesh)
node.setSelectable(True) node.setSelectable(True)
transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(object.get("id")), self._namespaces) transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(object.get("id")), self._namespaces)
if transformation: if transformation:
transformation = transformation[0] transformation = transformation[0]
@ -88,30 +96,32 @@ class ThreeMFReader(MeshReader):
temp_mat._data[0,2] = splitted_transformation[6] temp_mat._data[0,2] = splitted_transformation[6]
temp_mat._data[1,2] = splitted_transformation[7] temp_mat._data[1,2] = splitted_transformation[7]
temp_mat._data[2,2] = splitted_transformation[8] temp_mat._data[2,2] = splitted_transformation[8]
# Translation # Translation
temp_mat._data[0,3] = splitted_transformation[9] temp_mat._data[0,3] = splitted_transformation[9]
temp_mat._data[1,3] = splitted_transformation[10] temp_mat._data[1,3] = splitted_transformation[10]
temp_mat._data[2,3] = splitted_transformation[11] temp_mat._data[2,3] = splitted_transformation[11]
node.setPosition(Vector(temp_mat.at(0,3), temp_mat.at(1,3), temp_mat.at(2,3))) node.setPosition(Vector(temp_mat.at(0,3), temp_mat.at(1,3), temp_mat.at(2,3)))
temp_quaternion = Quaternion() temp_quaternion = Quaternion()
temp_quaternion.setByMatrix(temp_mat) temp_quaternion.setByMatrix(temp_mat)
node.setOrientation(temp_quaternion) node.setOrientation(temp_quaternion)
# Magical scale extraction # Magical scale extraction
S2 = temp_mat.getTransposed().multiply(temp_mat) scale = temp_mat.getTransposed().multiply(temp_mat)
scale_x = math.sqrt(S2.at(0,0)) scale_x = math.sqrt(scale.at(0,0))
scale_y = math.sqrt(S2.at(1,1)) scale_y = math.sqrt(scale.at(1,1))
scale_z = math.sqrt(S2.at(2,2)) scale_z = math.sqrt(scale.at(2,2))
node.setScale(Vector(scale_x,scale_y,scale_z)) node.setScale(Vector(scale_x,scale_y,scale_z))
# We use a different coordinate frame, so rotate. # We use a different coordinate frame, so rotate.
rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0)) #rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0))
node.rotate(rotation) #node.rotate(rotation)
result.addChild(node) result.addChild(node)
Job.yieldThread()
#If there is more then one object, group them. #If there is more then one object, group them.
try: try:
if len(objects) > 1: if len(objects) > 1:

View File

@ -1,11 +1,11 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from . import ThreeMFReader
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
from . import ThreeMFReader
def getMetaData(): def getMetaData():
return { return {
"plugin": { "plugin": {

View File

@ -31,6 +31,7 @@ class ChangeLog(Extension, QObject,):
self._change_logs = None self._change_logs = None
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "15.05.90") #First version of CURA with uranium Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "15.05.90") #First version of CURA with uranium
self.addMenuItem(catalog.i18nc("@item:inmenu", "Show Changelog"), self.showChangelog)
#self.showChangelog() #self.showChangelog()
def getChangeLogs(self): def getChangeLogs(self):
@ -57,7 +58,7 @@ class ChangeLog(Extension, QObject,):
def loadChangeLogs(self): def loadChangeLogs(self):
self._change_logs = collections.OrderedDict() self._change_logs = collections.OrderedDict()
with open(os.path.join(PluginRegistry.getInstance().getPluginPath("ChangeLogPlugin"), "ChangeLog.txt"), 'r',-1, "utf-8") as f: with open(os.path.join(PluginRegistry.getInstance().getPluginPath("ChangeLogPlugin"), "ChangeLog.txt"), "r",-1, "utf-8") as f:
open_version = None open_version = None
open_header = None open_header = None
for line in f: for line in f:
@ -77,8 +78,8 @@ class ChangeLog(Extension, QObject,):
def _onEngineCreated(self): def _onEngineCreated(self):
if not self._version: if not self._version:
return #We're on dev branch. return #We're on dev branch.
if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"): #if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"):
self.showChangelog() #self.showChangelog()
def showChangelog(self): def showChangelog(self):
if not self._changelog_window: if not self._changelog_window:

View File

@ -11,8 +11,8 @@ import UM 1.1 as UM
UM.Dialog UM.Dialog
{ {
id: base id: base
width: 300 * Screen.devicePixelRatio; minimumWidth: 400
height: 500 * Screen.devicePixelRatio; minimumHeight: 300;
title: "Changelog" title: "Changelog"
ScrollView ScrollView

View File

@ -1,4 +1,15 @@
[15.10.0] [2.0.0]
*Naming changes
Infill prints after perimeters → Infill Before Walls
Initial layer thickness → Initial Layer Height
Structure type → Pattern
Cool head lift → Lift Head
Combine everything (Type-A) → Union Overlapping Volumes
Combine everything (Type-B) → Remove All Holes
Keep open faces Keep Disconnected → Faces
Only follow mesh surface → Surface Mode
*All at Once/One at a Time *All at Once/One at a Time
Curas default mode is set to All At Once. You can print multiple objects faster with the option print objects One At A Time. This can be changed in Advanced Settings. Please note that in One At A Time mode, grouped objects will still be printed as a single object. Curas default mode is set to All At Once. You can print multiple objects faster with the option print objects One At A Time. This can be changed in Advanced Settings. Please note that in One At A Time mode, grouped objects will still be printed as a single object.

View File

@ -9,7 +9,7 @@ catalog = i18nCatalog("cura")
def getMetaData(): def getMetaData():
return { return {
"plugin": { "plugin": {
"name": catalog.i18nc("@label", "Change Log"), "name": catalog.i18nc("@label", "Changelog"),
"author": "Ultimaker", "author": "Ultimaker",
"version": "1.0", "version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version"), "description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version"),

View File

@ -17,6 +17,7 @@ from cura.OneAtATimeIterator import OneAtATimeIterator
from . import Cura_pb2 from . import Cura_pb2
from . import ProcessSlicedObjectListJob from . import ProcessSlicedObjectListJob
from . import ProcessGCodeJob from . import ProcessGCodeJob
from . import StartSliceJob
import os import os
import sys import sys
@ -49,6 +50,7 @@ class CuraEngineBackend(Backend):
self._onActiveViewChanged() self._onActiveViewChanged()
self._stored_layer_data = None self._stored_layer_data = None
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onChanged)
self._profile = None self._profile = None
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged) Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
@ -67,22 +69,26 @@ class CuraEngineBackend(Backend):
self._slicing = False self._slicing = False
self._restart = False self._restart = False
self._save_gcode = True
self._save_polygons = True
self._report_progress = True
self._enabled = True self._enabled = True
self._always_restart = True
self._message = None self._message = None
self.backendConnected.connect(self._onBackendConnected) self.backendConnected.connect(self._onBackendConnected)
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onInstanceChanged)
## Get the command that is used to call the engine. ## Get the command that is used to call the engine.
# This is usefull for debugging and used to actually start the engine # This is usefull for debugging and used to actually start the engine
# \return list of commands and args / parameters. # \return list of commands and args / parameters.
def getEngineCommand(self): def getEngineCommand(self):
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", Resources.getPath(Resources.MachineDefinitions, "fdmprinter.json"), "-vv"] active_machine = Application.getInstance().getMachineManager().getActiveMachineInstance()
if not active_machine:
return None
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", active_machine.getMachineDefinition().getPath(), "-vv"]
## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished. ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished.
# \param time The amount of time the print will take. # \param time The amount of time the print will take.
@ -95,24 +101,12 @@ class CuraEngineBackend(Backend):
## Emitted whne the slicing process is aborted forcefully. ## Emitted whne the slicing process is aborted forcefully.
slicingCancelled = Signal() slicingCancelled = Signal()
## Perform a slice of the scene with the given set of settings. ## Perform a slice of the scene.
# def slice(self):
# \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):
if not self._enabled: if not self._enabled:
return return
if self._slicing: if self._slicing:
if not kwargs.get("force_restart", True):
return
self._slicing = False self._slicing = False
self._restart = True self._restart = True
if self._process is not None: if self._process is not None:
@ -121,110 +115,56 @@ class CuraEngineBackend(Backend):
self._process.terminate() self._process.terminate()
except: # terminating a process that is already terminating causes an exception, silently ignore this. except: # terminating a process that is already terminating causes an exception, silently ignore this.
pass 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: if self._message:
self._message.hide() self._message.hide()
self._message = None self._message = None
return #No point in slicing an empty build plate
if kwargs.get("profile", self._profile).hasErrorValue(): self.slicingCancelled.emit()
Logger.log('w', "Profile has error values. Aborting slicing") return
if self._profile.hasErrorValue():
Logger.log("w", "Profile has error values. Aborting slicing")
if self._message: if self._message:
self._message.hide() self._message.hide()
self._message = None self._message = None
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors.")) self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
self._message.show() self._message.show()
return #No slicing if we have error values since those are by definition illegal values. 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()): self.processingProgress.emit(0.0)
if type(node) is SceneNode and node.getMeshData(): if self._message:
if node.callDecoration("getLayerData"): self._message.setProgress(-1)
Application.getInstance().getController().getScene().getRoot().removeChild(node) #else:
break # self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
Application.getInstance().getController().getScene().gcode_list = None # self._message.show()
self._scene.gcode_list = []
self._slicing = True self._slicing = True
self.slicingStarted.emit()
self._report_progress = kwargs.get("report_progress", True) job = StartSliceJob.StartSliceJob(self._profile, self._socket)
if self._report_progress: job.start()
self.processingProgress.emit(0.0) job.finished.connect(self._onStartSliceCompleted)
if not self._message:
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
self._message.show()
else:
self._message.setProgress(-1)
self._sendSettings(kwargs.get("profile", self._profile)) def _onStartSliceCompleted(self, job):
if job.getError() or job.getResult() != True:
self._scene.acquireLock() if self._message:
self._message.hide()
# Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages. self._message = None
# 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 _onSceneChanged(self, source):
if (type(source) is not SceneNode) or (source is self._scene.getRoot()) or (source.getMeshData() is None):
return return
if(source.getMeshData().getVertices() is None): def _onSceneChanged(self, source):
if type(source) is not SceneNode:
return
if source is self._scene.getRoot():
return
if source.getMeshData() is None:
return
if source.getMeshData().getVertices() is None:
return return
self._onChanged() self._onChanged()
@ -242,41 +182,42 @@ class CuraEngineBackend(Backend):
self._onChanged() self._onChanged()
def _onSlicedObjectListMessage(self, message): def _onSlicedObjectListMessage(self, message):
if self._save_polygons: if self._layer_view_active:
if self._layer_view_active: job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) job.start()
job.start() else :
else : self._stored_layer_data = message
self._stored_layer_data = message
def _onProgressMessage(self, 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: if self._message:
self._message.setProgress(round(message.amount * 100)) 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): def _onGCodeLayerMessage(self, message):
if self._save_gcode: self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
job.start()
def _onGCodePrefixMessage(self, message): 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): def _onObjectPrintTimeMessage(self, message):
self.printDurationMessage.emit(message.time, message.material_amount) self.printDurationMessage.emit(message.time, message.material_amount)
self.processingProgress.emit(1.0) 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): def _createSocket(self):
super()._createSocket() super()._createSocket()
@ -298,15 +239,6 @@ class CuraEngineBackend(Backend):
self._change_timer.start() 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): def _onBackendConnected(self):
if self._restart: if self._restart:
self._onChanged() self._onChanged()
@ -327,22 +259,18 @@ class CuraEngineBackend(Backend):
if self._stored_layer_data: if self._stored_layer_data:
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data) job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
job.start() job.start()
self._stored_layer_data = None
else: else:
self._layer_view_active = False 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") def _onInstanceChanged(self):
if not object_settings: self._slicing = False
return self._restart = True
if self._process is not None:
for key, value in object_settings.items(): Logger.log("d", "Killing engine process")
setting = message.settings.add() try:
setting.name = key self._process.terminate()
setting.value = str(value).encode() except: # terminating a process that is already terminating causes an exception, silently ignore this.
pass
self.slicingCancelled.emit()

View File

@ -1,8 +1,6 @@
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: Cura.proto # source: Cura.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message from google.protobuf import message as _message
from google.protobuf import reflection as _reflection from google.protobuf import reflection as _reflection
@ -18,7 +16,8 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor( DESCRIPTOR = _descriptor.FileDescriptor(
name='Cura.proto', name='Cura.proto',
package='cura.proto', package='cura.proto',
serialized_pb=_b('\n\nCura.proto\x12\ncura.proto\"X\n\nObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.cura.proto.Object\x12%\n\x08settings\x18\x02 \x03(\x0b\x32\x13.cura.proto.Setting\"5\n\x05Slice\x12,\n\x0cobject_lists\x18\x01 \x03(\x0b\x32\x16.cura.proto.ObjectList\"o\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12%\n\x08settings\x18\x05 \x03(\x0b\x32\x13.cura.proto.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"=\n\x10SlicedObjectList\x12)\n\x07objects\x18\x01 \x03(\x0b\x32\x18.cura.proto.SlicedObject\"=\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12!\n\x06layers\x18\x02 \x03(\x0b\x32\x11.cura.proto.Layer\"]\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x11\n\tthickness\x18\x03 \x01(\x02\x12%\n\x08polygons\x18\x04 \x03(\x0b\x32\x13.cura.proto.Polygon\"\xe1\x01\n\x07Polygon\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.cura.proto.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\x89\x01\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\x12\x0e\n\nInfillType\x10\x06\x12\x15\n\x11SupportInfillType\x10\x07\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\"4\n\x0bSettingList\x12%\n\x08settings\x18\x01 \x03(\x0b\x32\x13.cura.proto.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3') syntax='proto3',
serialized_pb=b'\n\nCura.proto\x12\ncura.proto\"X\n\nObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.cura.proto.Object\x12%\n\x08settings\x18\x02 \x03(\x0b\x32\x13.cura.proto.Setting\"5\n\x05Slice\x12,\n\x0cobject_lists\x18\x01 \x03(\x0b\x32\x16.cura.proto.ObjectList\"o\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12%\n\x08settings\x18\x05 \x03(\x0b\x32\x13.cura.proto.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"=\n\x10SlicedObjectList\x12)\n\x07objects\x18\x01 \x03(\x0b\x32\x18.cura.proto.SlicedObject\"=\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12!\n\x06layers\x18\x02 \x03(\x0b\x32\x11.cura.proto.Layer\"]\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x11\n\tthickness\x18\x03 \x01(\x02\x12%\n\x08polygons\x18\x04 \x03(\x0b\x32\x13.cura.proto.Polygon\"\x8e\x02\n\x07Polygon\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.cura.proto.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\xb6\x01\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\x12\x0e\n\nInfillType\x10\x06\x12\x15\n\x11SupportInfillType\x10\x07\x12\x13\n\x0fMoveCombingType\x10\x08\x12\x16\n\x12MoveRetractionType\x10\t\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\"4\n\x0bSettingList\x12%\n\x08settings\x18\x01 \x03(\x0b\x32\x13.cura.proto.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3'
) )
_sym_db.RegisterFileDescriptor(DESCRIPTOR) _sym_db.RegisterFileDescriptor(DESCRIPTOR)
@ -62,11 +61,19 @@ _POLYGON_TYPE = _descriptor.EnumDescriptor(
name='SupportInfillType', index=7, number=7, name='SupportInfillType', index=7, number=7,
options=None, options=None,
type=None), type=None),
_descriptor.EnumValueDescriptor(
name='MoveCombingType', index=8, number=8,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MoveRetractionType', index=9, number=9,
options=None,
type=None),
], ],
containing_type=None, containing_type=None,
options=None, options=None,
serialized_start=622, serialized_start=622,
serialized_end=759, serialized_end=804,
) )
_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE) _sym_db.RegisterEnumDescriptor(_POLYGON_TYPE)
@ -100,6 +107,7 @@ _OBJECTLIST = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -130,6 +138,7 @@ _SLICE = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -155,21 +164,21 @@ _OBJECT = _descriptor.Descriptor(
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='vertices', full_name='cura.proto.Object.vertices', index=1, name='vertices', full_name='cura.proto.Object.vertices', index=1,
number=2, type=12, cpp_type=9, label=1, number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='normals', full_name='cura.proto.Object.normals', index=2, name='normals', full_name='cura.proto.Object.normals', index=2,
number=3, type=12, cpp_type=9, label=1, number=3, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='indices', full_name='cura.proto.Object.indices', index=3, name='indices', full_name='cura.proto.Object.indices', index=3,
number=4, type=12, cpp_type=9, label=1, number=4, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
@ -188,6 +197,7 @@ _OBJECT = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -218,6 +228,7 @@ _PROGRESS = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -248,6 +259,7 @@ _SLICEDOBJECTLIST = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -285,6 +297,7 @@ _SLICEDOBJECT = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -336,6 +349,7 @@ _LAYER = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
@ -361,7 +375,7 @@ _POLYGON = _descriptor.Descriptor(
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='points', full_name='cura.proto.Polygon.points', index=1, name='points', full_name='cura.proto.Polygon.points', index=1,
number=2, type=12, cpp_type=9, label=1, number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
@ -381,11 +395,12 @@ _POLYGON = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=534, serialized_start=534,
serialized_end=759, serialized_end=804,
) )
@ -406,7 +421,7 @@ _GCODELAYER = _descriptor.Descriptor(
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='data', full_name='cura.proto.GCodeLayer.data', index=1, name='data', full_name='cura.proto.GCodeLayer.data', index=1,
number=2, type=12, cpp_type=9, label=1, number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
@ -418,11 +433,12 @@ _GCODELAYER = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=761, serialized_start=806,
serialized_end=799, serialized_end=844,
) )
@ -462,11 +478,12 @@ _OBJECTPRINTTIME = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=801, serialized_start=846,
serialized_end=869, serialized_end=914,
) )
@ -492,11 +509,12 @@ _SETTINGLIST = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=871, serialized_start=916,
serialized_end=923, serialized_end=968,
) )
@ -510,14 +528,14 @@ _SETTING = _descriptor.Descriptor(
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='name', full_name='cura.proto.Setting.name', index=0, name='name', full_name='cura.proto.Setting.name', index=0,
number=1, type=9, cpp_type=9, label=1, number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'), has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='value', full_name='cura.proto.Setting.value', index=1, name='value', full_name='cura.proto.Setting.value', index=1,
number=2, type=12, cpp_type=9, label=1, number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
@ -529,11 +547,12 @@ _SETTING = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=925, serialized_start=970,
serialized_end=963, serialized_end=1008,
) )
@ -547,7 +566,7 @@ _GCODEPREFIX = _descriptor.Descriptor(
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='data', full_name='cura.proto.GCodePrefix.data', index=0, name='data', full_name='cura.proto.GCodePrefix.data', index=0,
number=2, type=12, cpp_type=9, label=1, number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""), has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
options=None), options=None),
@ -559,11 +578,12 @@ _GCODEPREFIX = _descriptor.Descriptor(
], ],
options=None, options=None,
is_extendable=False, is_extendable=False,
syntax='proto3',
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=965, serialized_start=1010,
serialized_end=992, serialized_end=1037,
) )
_OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT _OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT

View File

@ -10,8 +10,8 @@ from UM.Mesh.MeshData import MeshData
from UM.Message import Message from UM.Message import Message
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from . import LayerData from cura import LayerData
from . import LayerDataDecorator from cura import LayerDataDecorator
import numpy import numpy
import struct import struct
@ -32,19 +32,18 @@ class ProcessSlicedObjectListJob(Job):
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
objectIdMap = {} object_id_map = {}
new_node = SceneNode() new_node = SceneNode()
## Put all nodes in a dict identified by ID ## Put all nodes in a dict identified by ID
for node in DepthFirstIterator(self._scene.getRoot()): for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData(): if type(node) is SceneNode and node.getMeshData():
if node.callDecoration("getLayerData"): if node.callDecoration("getLayerData"):
#if hasattr(node.getMeshData(), "layerData"):
self._scene.getRoot().removeChild(node) self._scene.getRoot().removeChild(node)
else: else:
objectIdMap[id(node)] = node object_id_map[id(node)] = node
Job.yieldThread()
settings = Application.getInstance().getMachineManager().getActiveProfile() settings = Application.getInstance().getMachineManager().getActiveProfile()
layerHeight = settings.getSettingValue("layer_height")
center = None center = None
if not settings.getSettingValue("machine_center_is_zero"): if not settings.getSettingValue("machine_center_is_zero"):
@ -54,9 +53,15 @@ class ProcessSlicedObjectListJob(Job):
mesh = MeshData() mesh = MeshData()
layer_data = LayerData.LayerData() 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: for object in self._message.objects:
try: try:
node = objectIdMap[object.id] node = object_id_map[object.id]
except KeyError: except KeyError:
continue continue
@ -73,23 +78,34 @@ class ProcessSlicedObjectListJob(Job):
points[:,2] *= -1 points[:,2] *= -1
points -= numpy.array(center) points -= center
layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width) 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 # We are done processing all the layers we got from the engine, now create a mesh out of the data
layer_data.build() layer_data.build()
#Add layerdata decorator to scene node to indicate that the node has layerdata #Add layerdata decorator to scene node to indicate that the node has layerdata
decorator = LayerDataDecorator.LayerDataDecorator() decorator = LayerDataDecorator.LayerDataDecorator()
decorator.setLayerData(layer_data) decorator.setLayerData(layer_data)
new_node.addDecorator(decorator) new_node.addDecorator(decorator)
new_node.setMeshData(mesh) new_node.setMeshData(mesh)
new_node.setParent(self._scene.getRoot()) new_node.setParent(self._scene.getRoot())
if self._progress:
self._progress.setProgress(100)
view = Application.getInstance().getController().getActiveView() view = Application.getInstance().getController().getActiveView()
if view.getPluginId() == "LayerView": if view.getPluginId() == "LayerView":
view.resetLayerData() view.resetLayerData()

View File

@ -0,0 +1,152 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import numpy
from string import Formatter
import traceback
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
## Formatter class that handles token expansion in start/end gcod
class GcodeStartEndFormatter(Formatter):
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
if isinstance(key, str):
try:
return kwargs[key]
except KeyError:
Logger.log("w", "Unable to replace '%s' placeholder in start/end gcode", key)
return "{" + key + "}"
else:
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
return "{" + str(key) + "}"
## 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 _expandGcodeTokens(self, key, value, settings):
try:
# any setting can be used as a token
fmt = GcodeStartEndFormatter()
return str(fmt.format(value, **settings)).encode("utf-8")
except:
Logger.log("w", "Unabled to do token replacement on start/end gcode %s", traceback.format_exc())
return str(value).encode("utf-8")
def _sendSettings(self, profile):
msg = Cura_pb2.SettingList()
settings = profile.getAllSettingValues(include_machine = True)
start_gcode = settings["machine_start_gcode"]
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode
settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode
for key, value in settings.items():
s = msg.settings.add()
s.name = key
if key == "machine_start_gcode" or key == "machine_end_gcode":
s.value = self._expandGcodeTokens(key, value, settings)
else:
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()

View File

@ -0,0 +1,8 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Mesh.MeshReader import MeshReader
class GCodeReader(MeshReader):
def read(self, file_name):
pass

View File

@ -0,0 +1,25 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from . import GCodeReader
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": catalog.i18nc("@label", "GCode Reader"),
"author": "Ultimaker",
"version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading GCode files."),
"api": 2
},
"mesh_reader": {
"extension": "gcode",
"description": catalog.i18nc("@item:inlistbox", "Gcode File")
}
}
def register(app):
return { "mesh_reader": GCodeReader.GCodeReader() }

View File

@ -11,6 +11,9 @@ from UM.Scene.Selection import Selection
from UM.Math.Color import Color from UM.Math.Color import Color
from UM.Mesh.MeshData import MeshData from UM.Mesh.MeshData import MeshData
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from cura.ConvexHullNode import ConvexHullNode from cura.ConvexHullNode import ConvexHullNode
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
@ -21,7 +24,8 @@ from . import LayerViewProxy
class LayerView(View): class LayerView(View):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._material = None self._shader = None
self._selection_shader = None
self._num_layers = 0 self._num_layers = 0
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
self._proxy = LayerViewProxy.LayerViewProxy() self._proxy = LayerViewProxy.LayerViewProxy()
@ -29,6 +33,7 @@ class LayerView(View):
self._max_layers = 10 self._max_layers = 10
self._current_layer_num = 10 self._current_layer_num = 10
self._current_layer_mesh = None self._current_layer_mesh = None
self._current_layer_jumps = None
self._activity = False self._activity = False
self._solid_layers = 5 self._solid_layers = 5
@ -47,18 +52,15 @@ class LayerView(View):
def resetLayerData(self): def resetLayerData(self):
self._current_layer_mesh = None self._current_layer_mesh = None
self._current_layer_jumps = None
def beginRendering(self): def beginRendering(self):
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() renderer = self.getRenderer()
renderer.setRenderSelection(False)
if not self._material: if not self._selection_shader:
self._material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "vertexcolor.frag")) self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) self._selection_shader.setUniformValue("u_color", Color(32, 32, 32, 128))
self._selection_material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "color.frag"))
self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128))
for node in DepthFirstIterator(scene.getRoot()): for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers. # We do not want to render ConvexHullNode as it conflicts with the bottom layers.
@ -69,7 +71,7 @@ class LayerView(View):
if not node.render(renderer): if not node.render(renderer):
if node.getMeshData() and node.isVisible(): if node.getMeshData() and node.isVisible():
if Selection.isSelected(node): if Selection.isSelected(node):
renderer.queueNode(node, material = self._selection_material, transparent = True) renderer.queueNode(node, transparent = True, shader = self._selection_shader)
layer_data = node.callDecoration("getLayerData") layer_data = node.callDecoration("getLayerData")
if not layer_data: if not layer_data:
continue continue
@ -85,7 +87,7 @@ class LayerView(View):
end += counts end += counts
# This uses glDrawRangeElements internally to only draw a certain range of lines. # This uses glDrawRangeElements internally to only draw a certain range of lines.
renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) renderer.queueNode(node, mesh = layer_data, mode = RenderBatch.RenderMode.Lines, range = (start, end))
# We currently recreate the current "solid" layers every time a # We currently recreate the current "solid" layers every time a
if not self._current_layer_mesh: if not self._current_layer_mesh:
@ -100,15 +102,38 @@ class LayerView(View):
continue continue
except: except:
continue continue
if self._current_layer_mesh: #Threading thing; Switching between views can cause the current layer mesh to be deleted.
self._current_layer_mesh.addVertices(layer_mesh.getVertices()) self._current_layer_mesh.addVertices(layer_mesh.getVertices())
# Scale layer color by a brightness factor based on the current layer number # Scale layer color by a brightness factor based on the current layer number
# This will result in a range of 0.5 - 1.0 to multiply colors by. # This will result in a range of 0.5 - 1.0 to multiply colors by.
brightness = (2.0 - (i / self._solid_layers)) / 2.0 brightness = (2.0 - (i / self._solid_layers)) / 2.0
self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness) if self._current_layer_mesh:
self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness)
if self._current_layer_mesh:
renderer.queueNode(node, mesh = self._current_layer_mesh)
renderer.queueNode(node, mesh = self._current_layer_mesh, material = self._material) if not self._current_layer_jumps:
self._current_layer_jumps = MeshData()
for i in range(1):
layer = self._current_layer_num - i
if layer < 0:
continue
try:
layer_mesh = layer_data.getLayer(layer).createJumps()
if not layer_mesh or layer_mesh.getVertices() is None:
continue
except:
continue
self._current_layer_jumps.addVertices(layer_mesh.getVertices())
# Scale layer color by a brightness factor based on the current layer number
# This will result in a range of 0.5 - 1.0 to multiply colors by.
brightness = (2.0 - (i / self._solid_layers)) / 2.0
self._current_layer_jumps.addColors(layer_mesh.getColors() * brightness)
renderer.queueNode(node, mesh = self._current_layer_jumps)
def setLayer(self, value): def setLayer(self, value):
if self._current_layer_num != value: if self._current_layer_num != value:
@ -119,6 +144,7 @@ class LayerView(View):
self._current_layer_num = self._max_layers self._current_layer_num = self._max_layers
self._current_layer_mesh = None self._current_layer_mesh = None
self._current_layer_jumps = None
self.currentLayerNumChanged.emit() self.currentLayerNumChanged.emit()
currentLayerNumChanged = Signal() currentLayerNumChanged = Signal()
@ -126,31 +152,27 @@ class LayerView(View):
def calculateMaxLayers(self): def calculateMaxLayers(self):
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() renderer = self.getRenderer()
if renderer and self._material: self._activity = True
self._activity = True
renderer.setRenderSelection(False)
self._old_max_layers = self._max_layers
## Recalculate num max layers
new_max_layers = 0
for node in DepthFirstIterator(scene.getRoot()):
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue
if new_max_layers < len(layer_data.getLayers()): self._old_max_layers = self._max_layers
new_max_layers = len(layer_data.getLayers()) - 1 ## Recalculate num max layers
new_max_layers = 0
for node in DepthFirstIterator(scene.getRoot()):
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue
if new_max_layers > 0 and new_max_layers != self._old_max_layers: if new_max_layers < len(layer_data.getLayers()):
self._max_layers = new_max_layers new_max_layers = len(layer_data.getLayers()) - 1
self.maxLayersChanged.emit()
self._current_layer_num = self._max_layers
# This makes sure we update the current layer if new_max_layers > 0 and new_max_layers != self._old_max_layers:
self.setLayer(int(self._max_layers)) self._max_layers = new_max_layers
self.currentLayerNumChanged.emit() self.maxLayersChanged.emit()
self._current_layer_num = self._max_layers
# This makes sure we update the current layer
self.setLayer(int(self._max_layers))
self.currentLayerNumChanged.emit()
maxLayersChanged = Signal() maxLayersChanged = Signal()
currentLayerNumChanged = Signal() currentLayerNumChanged = Signal()

View File

@ -10,16 +10,16 @@ import UM 1.0 as UM
Item Item
{ {
width: 250 width: UM.Theme.sizes.button.width
height: 250 height: UM.Theme.sizes.slider_layerview_size.height
Slider Slider
{ {
id: slider id: slider
width: 10 width: UM.Theme.sizes.slider_layerview_size.width
height: 250 height: UM.Theme.sizes.slider_layerview_size.height
anchors.right : parent.right anchors.left: parent.left
anchors.rightMargin: UM.Theme.sizes.slider_layerview_margin.width/2 anchors.leftMargin: UM.Theme.sizes.slider_layerview_margin.width/2
orientation: Qt.Vertical orientation: Qt.Vertical
minimumValue: 0; minimumValue: 0;
maximumValue: UM.LayerView.numLayers; maximumValue: UM.LayerView.numLayers;
@ -31,15 +31,7 @@ Item
style: UM.Theme.styles.layerViewSlider style: UM.Theme.styles.layerViewSlider
} }
Rectangle { Rectangle {
anchors.right: parent.right anchors.left: parent.left
y: -UM.Theme.sizes.slider_layerview_background_extension.height
z: slider.z - 1
width: UM.Theme.sizes.button.width
height: UM.Theme.sizes.slider_layerview_background_extension.height
color: UM.Theme.colors.slider_text_background
}
Rectangle {
anchors.right : parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
z: slider.z - 1 z: slider.z - 1
width: UM.Theme.sizes.slider_layerview_background.width width: UM.Theme.sizes.slider_layerview_background.width

View File

@ -1,6 +1,8 @@
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
from UM.Application import Application from UM.Application import Application
import LayerView import LayerView
class LayerViewProxy(QObject): class LayerViewProxy(QObject):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
@ -52,4 +54,4 @@ class LayerViewProxy(QObject):
active_view = self._controller.getActiveView() active_view = self._controller.getActiveView()
if type(active_view) == LayerView.LayerView.LayerView: if type(active_view) == LayerView.LayerView.LayerView:
active_view.currentLayerNumChanged.connect(self._onLayerChanged) active_view.currentLayerNumChanged.connect(self._onLayerChanged)
active_view.maxLayersChanged.connect(self._onMaxLayersChanged) active_view.maxLayersChanged.connect(self._onMaxLayersChanged)

View File

@ -0,0 +1,109 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
from UM.Application import Application
from UM.Qt.ListModel import ListModel
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from UM.Settings.ProfileOverrideDecorator import ProfileOverrideDecorator
from . import SettingOverrideModel
class PerObjectSettingsModel(ListModel):
IdRole = Qt.UserRole + 1
XRole = Qt.UserRole + 2
YRole = Qt.UserRole + 3
MaterialRole = Qt.UserRole + 4
ProfileRole = Qt.UserRole + 5
SettingsRole = Qt.UserRole + 6
def __init__(self, parent = None):
super().__init__(parent)
self._scene = Application.getInstance().getController().getScene()
self._root = self._scene.getRoot()
self._root.transformationChanged.connect(self._updatePositions)
self._root.childrenChanged.connect(self._updateNodes)
self._updateNodes(None)
self.addRoleName(self.IdRole,"id")
self.addRoleName(self.XRole,"x")
self.addRoleName(self.YRole,"y")
self.addRoleName(self.MaterialRole, "material")
self.addRoleName(self.ProfileRole, "profile")
self.addRoleName(self.SettingsRole, "settings")
@pyqtSlot("quint64", str)
def setObjectProfile(self, object_id, profile_name):
self.setProperty(self.find("id", object_id), "profile", profile_name)
profile = None
if profile_name != "global":
profile = Application.getInstance().getMachineManager().findProfile(profile_name)
node = self._scene.findObject(object_id)
if profile:
if not node.getDecorator(ProfileOverrideDecorator):
node.addDecorator(ProfileOverrideDecorator())
node.callDecoration("setProfile", profile)
else:
if node.getDecorator(ProfileOverrideDecorator):
node.removeDecorator(ProfileOverrideDecorator)
@pyqtSlot("quint64", str)
def addSettingOverride(self, object_id, key):
machine = Application.getInstance().getMachineManager().getActiveMachineInstance()
if not machine:
return
node = self._scene.findObject(object_id)
if not node.getDecorator(SettingOverrideDecorator):
node.addDecorator(SettingOverrideDecorator())
node.callDecoration("addSetting", key)
@pyqtSlot("quint64", str)
def removeSettingOverride(self, object_id, key):
node = self._scene.findObject(object_id)
node.callDecoration("removeSetting", key)
if len(node.callDecoration("getAllSettings")) == 0:
node.removeDecorator(SettingOverrideDecorator)
def _updatePositions(self, source):
camera = Application.getInstance().getController().getScene().getActiveCamera()
for node in BreadthFirstIterator(self._root):
if type(node) is not SceneNode or not node.getMeshData():
continue
projected_position = camera.project(node.getWorldPosition())
index = self.find("id", id(node))
self.setProperty(index, "x", float(projected_position[0]))
self.setProperty(index, "y", float(projected_position[1]))
def _updateNodes(self, source):
self.clear()
camera = Application.getInstance().getController().getScene().getActiveCamera()
for node in BreadthFirstIterator(self._root):
if type(node) is not SceneNode or not node.getMeshData() or not node.isSelectable():
continue
projected_position = camera.project(node.getWorldPosition())
node_profile = node.callDecoration("getProfile")
if not node_profile:
node_profile = "global"
else:
node_profile = node_profile.getName()
self.appendItem({
"id": id(node),
"x": float(projected_position[0]),
"y": float(projected_position[1]),
"material": "",
"profile": node_profile,
"settings": SettingOverrideModel.SettingOverrideModel(node)
})

View File

@ -0,0 +1,311 @@
// Copyright (c) 2015 Ultimaker B.V.
// Uranium is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Window 2.2
import UM 1.1 as UM
Item {
id: base;
property int currentIndex: UM.ActiveTool.properties.SelectedIndex;
property string printSequence: UM.ActiveTool.properties.PrintSequence;
width: childrenRect.width;
height: childrenRect.height;
Column {
id: items
anchors.top: parent.top;
anchors.left: parent.left;
spacing: UM.Theme.sizes.default_margin.height;
Label {
width: UM.Theme.sizes.setting.width;
wrapMode: Text.Wrap;
text: catalog.i18nc("@label", "Per Object Settings behavior may be unexpected when 'Print sequence' is set to 'All at Once'.")
color: UM.Theme.colors.text;
visible: base.printSequence == "all_at_once"
}
UM.SettingItem {
id: profileSelection
width: UM.Theme.sizes.setting.width;
height: UM.Theme.sizes.setting.height;
name: catalog.i18nc("@label", "Object profile")
type: "enum"
indent: false
style: UM.Theme.styles.setting_item;
options: UM.ProfilesModel { addUseGlobal: true }
value: UM.ActiveTool.properties.Model.getItem(base.currentIndex).profile
onItemValueChanged: {
var item = UM.ActiveTool.properties.Model.getItem(base.currentIndex);
UM.ActiveTool.properties.Model.setObjectProfile(item.id, value)
}
}
Column {
id: customisedSettings
spacing: UM.Theme.sizes.default_lining.height;
width: UM.Theme.sizes.setting.width + UM.Theme.sizes.setting.height/2;
Repeater {
id: settings;
model: UM.ActiveTool.properties.Model.getItem(base.currentIndex).settings
UM.SettingItem {
width: UM.Theme.sizes.setting.width;
height: UM.Theme.sizes.setting.height;
name: model.label;
type: model.type;
value: model.value;
description: model.description;
unit: model.unit;
valid: model.valid;
options: model.options
indent: false
style: UM.Theme.styles.setting_item;
onItemValueChanged: {
settings.model.setSettingValue(model.key, value)
}
Button
{
anchors.left: parent.right;
width: UM.Theme.sizes.setting.height;
height: UM.Theme.sizes.setting.height;
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
}
}
}
}
}
}
}
Button
{
id: customise_settings_button;
anchors.right: profileSelection.right;
height: UM.Theme.sizes.setting.height;
visible: parseInt(UM.Preferences.getValue("cura/active_mode")) == 1
text: catalog.i18nc("@action:button", "Add Setting");
style: ButtonStyle
{
background: Rectangle
{
width: control.width;
height: control.height;
border.width: UM.Theme.sizes.default_lining.width;
border.color: control.pressed ? UM.Theme.colors.action_button_active_border :
control.hovered ? UM.Theme.colors.action_button_hovered_border : UM.Theme.colors.action_button_border
color: control.pressed ? UM.Theme.colors.action_button_active :
control.hovered ? UM.Theme.colors.action_button_hovered : UM.Theme.colors.action_button
}
label: Label
{
text: control.text;
color: UM.Theme.colors.setting_control_text;
anchors.centerIn: parent
}
}
onClicked: settingPickDialog.visible = true;
Connections
{
target: UM.Preferences;
onPreferenceChanged:
{
customise_settings_button.visible = parseInt(UM.Preferences.getValue("cura/active_mode"))
}
}
}
}
UM.I18nCatalog { id: catalog; name: "uranium"; }
UM.Dialog {
id: settingPickDialog
title: catalog.i18nc("@title:window", "Pick a Setting to Customize")
TextField {
id: filter;
anchors {
top: parent.top;
left: parent.left;
right: parent.right;
}
placeholderText: catalog.i18nc("@label:textbox", "Filter...");
onTextChanged: settingCategoriesModel.filter(text);
}
ScrollView {
id: view;
anchors {
top: filter.bottom;
left: parent.left;
right: parent.right;
bottom: parent.bottom;
}
Column {
width: view.width - UM.Theme.sizes.default_margin.width * 2;
height: childrenRect.height;
Repeater {
id: settingList;
model: UM.SettingCategoriesModel { id: settingCategoriesModel; }
delegate: Item {
id: delegateItem;
width: parent.width;
height: childrenRect.height;
ToolButton {
id: categoryHeader;
text: model.name;
checkable: true;
width: parent.width;
onCheckedChanged: settingsColumn.state != "" ? settingsColumn.state = "" : settingsColumn.state = "collapsed";
style: ButtonStyle {
background: Rectangle
{
width: control.width;
height: control.height;
color: control.hovered ? palette.highlight : "transparent";
}
label: Row
{
spacing: UM.Theme.sizes.default_margin.width;
Image
{
anchors.verticalCenter: parent.verticalCenter;
source: control.checked ? UM.Theme.icons.arrow_right : UM.Theme.icons.arrow_bottom;
}
Label
{
text: control.text;
font.bold: true;
color: control.hovered ? palette.highlightedText : palette.text;
}
}
}
}
property variant settingsModel: model.settings;
visible: model.visible;
Column {
id: settingsColumn;
anchors.top: categoryHeader.bottom;
property real childrenHeight:
{
var h = 0.0;
for(var i in children)
{
var item = children[i];
h += children[i].height;
if(item.settingVisible)
{
if(i > 0)
{
h += spacing;
}
}
}
return h;
}
width: childrenRect.width;
height: childrenHeight;
Repeater {
model: delegateItem.settingsModel;
delegate: ToolButton {
id: button;
x: model.depth * UM.Theme.sizes.default_margin.width;
text: model.name;
tooltip: model.description;
onClicked: {
var object_id = UM.ActiveTool.properties.Model.getItem(base.currentIndex).id;
UM.ActiveTool.properties.Model.addSettingOverride(object_id, model.key);
settingPickDialog.visible = false;
}
states: State {
name: "filtered"
when: model.filtered || !model.visible || !model.enabled
PropertyChanges { target: button; height: 0; opacity: 0; }
}
}
}
states: State {
name: "collapsed";
PropertyChanges { target: settingsColumn; opacity: 0; height: 0; }
}
}
}
}
}
}
rightButtons: [
Button {
text: catalog.i18nc("@action:button", "Cancel");
onClicked: {
settingPickDialog.visible = false;
}
}
]
}
SystemPalette { id: palette; }
}

View File

@ -0,0 +1,29 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from UM.Tool import Tool
from UM.Scene.Selection import Selection
from UM.Application import Application
from . import PerObjectSettingsModel
class PerObjectSettingsTool(Tool):
def __init__(self):
super().__init__()
self.setExposedProperties("Model", "SelectedIndex", "PrintSequence")
def event(self, event):
return False
def getModel(self):
return PerObjectSettingsModel.PerObjectSettingsModel()
def getSelectedIndex(self):
selected_object_id = id(Selection.getSelectedObject(0))
index = self.getModel().find("id", selected_object_id)
return index
def getPrintSequence(self):
settings = Application.getInstance().getMachineManager().getActiveProfile()
return settings.getSettingValue("print_sequence")

View File

@ -0,0 +1,100 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
from UM.Application import Application
from UM.Qt.ListModel import ListModel
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
class SettingOverrideModel(ListModel):
KeyRole = Qt.UserRole + 1
LabelRole = Qt.UserRole + 2
DescriptionRole = Qt.UserRole + 3
ValueRole = Qt.UserRole + 4
TypeRole = Qt.UserRole + 5
UnitRole = Qt.UserRole + 6
ValidRole = Qt.UserRole + 7
OptionsRole = Qt.UserRole + 8
WarningDescriptionRole = Qt.UserRole + 9
ErrorDescriptionRole = Qt.UserRole + 10
def __init__(self, node, parent = None):
super().__init__(parent)
self._ignore_setting_change = None
self._node = node
self._node.decoratorsChanged.connect(self._onDecoratorsChanged)
self._onDecoratorsChanged(None)
self.addRoleName(self.KeyRole, "key")
self.addRoleName(self.LabelRole, "label")
self.addRoleName(self.DescriptionRole, "description")
self.addRoleName(self.ValueRole,"value")
self.addRoleName(self.TypeRole, "type")
self.addRoleName(self.UnitRole, "unit")
self.addRoleName(self.ValidRole, "valid")
self.addRoleName(self.OptionsRole, "options")
self.addRoleName(self.WarningDescriptionRole, "warning_description")
self.addRoleName(self.ErrorDescriptionRole, "error_description")
@pyqtSlot(str, "QVariant")
def setSettingValue(self, key, value):
if not self._decorator:
return
self._decorator.setSettingValue(key, value)
def _onDecoratorsChanged(self, node):
if not self._node.getDecorator(SettingOverrideDecorator):
self.clear()
return
self._decorator = self._node.getDecorator(SettingOverrideDecorator)
self._decorator.settingAdded.connect(self._onSettingsChanged)
self._decorator.settingRemoved.connect(self._onSettingsChanged)
self._decorator.settingValueChanged.connect(self._onSettingValueChanged)
self._onSettingsChanged()
def _createOptionsModel(self, options):
if not options:
return None
model = ListModel()
model.addRoleName(Qt.UserRole + 1, "value")
model.addRoleName(Qt.UserRole + 2, "name")
for value, name in options.items():
model.appendItem({"value": str(value), "name": str(name)})
return model
def _onSettingsChanged(self):
self.clear()
items = []
for key, setting in self._decorator.getAllSettings().items():
value = self._decorator.getSettingValue(key)
items.append({
"key": key,
"label": setting.getLabel(),
"description": setting.getDescription(),
"value": str(value),
"type": setting.getType(),
"unit": setting.getUnit(),
"valid": setting.validate(value),
"options": self._createOptionsModel(setting.getOptions()),
"warning_description": setting.getWarningDescription(),
"error_description": setting.getErrorDescription()
})
items.sort(key = lambda i: i["key"])
for item in items:
self.appendItem(item)
def _onSettingValueChanged(self, setting):
index = self.find("key", setting.getKey())
value = self._decorator.getSettingValue(setting.getKey())
if index != -1:
self.setProperty(index, "value", str(value))
self.setProperty(index, "valid", setting.validate(value))

View File

@ -0,0 +1,28 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from . import PerObjectSettingsTool
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium")
def getMetaData():
return {
"plugin": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"),
"author": "Ultimaker",
"version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."),
"api": 2
},
"tool": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings"),
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"),
"icon": "setting_per_object",
"tool_panel": "PerObjectSettingsPanel.qml",
"weight": 3
},
}
def register(app):
return { "tool": PerObjectSettingsTool.PerObjectSettingsTool() }

View File

@ -22,18 +22,23 @@ class RemovableDriveOutputDevice(OutputDevice):
self.setIconName("save_sd") self.setIconName("save_sd")
self.setPriority(1) self.setPriority(1)
def requestWrite(self, node): self._writing = False
def requestWrite(self, node, file_name = None):
if self._writing:
raise OutputDeviceError.DeviceBusyError()
gcode_writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType("text/x-gcode") gcode_writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType("text/x-gcode")
if not gcode_writer: if not gcode_writer:
Logger.log("e", "Could not find GCode writer, not writing to removable drive %s", self.getName()) Logger.log("e", "Could not find GCode writer, not writing to removable drive %s", self.getName())
raise OutputDeviceError.WriteRequestFailedError() raise OutputDeviceError.WriteRequestFailedError()
file_name = None if file_name == None:
for n in BreadthFirstIterator(node): for n in BreadthFirstIterator(node):
if n.getMeshData(): if n.getMeshData():
file_name = n.getName() file_name = n.getName()
if file_name: if file_name:
break break
if not file_name: if not file_name:
Logger.log("e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName()) Logger.log("e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName())
@ -52,11 +57,16 @@ class RemovableDriveOutputDevice(OutputDevice):
message = Message(catalog.i18nc("@info:progress", "Saving to Removable Drive <filename>{0}</filename>").format(self.getName()), 0, False, -1) message = Message(catalog.i18nc("@info:progress", "Saving to Removable Drive <filename>{0}</filename>").format(self.getName()), 0, False, -1)
message.show() message.show()
self.writeStarted.emit(self)
job._message = message job._message = message
self._writing = True
job.start() job.start()
except PermissionError as e: except PermissionError as e:
Logger.log("e", "Permission denied when trying to write to %s: %s", file_name, str(e))
raise OutputDeviceError.PermissionDeniedError() from e raise OutputDeviceError.PermissionDeniedError() from e
except OSError as e: except OSError as e:
Logger.log("e", "Operating system would not let us write to %s: %s", file_name, str(e))
raise OutputDeviceError.WriteRequestFailedError() from e raise OutputDeviceError.WriteRequestFailedError() from e
def _onProgress(self, job, progress): def _onProgress(self, job, progress):
@ -68,6 +78,8 @@ class RemovableDriveOutputDevice(OutputDevice):
if hasattr(job, "_message"): if hasattr(job, "_message"):
job._message.hide() job._message.hide()
job._message = None job._message = None
self._writing = False
self.writeFinished.emit(self) self.writeFinished.emit(self)
if job.getResult(): if job.getResult():
message = Message(catalog.i18nc("@info:status", "Saved to Removable Drive {0} as {1}").format(self.getName(), os.path.basename(job.getFileName()))) message = Message(catalog.i18nc("@info:status", "Saved to Removable Drive {0} as {1}").format(self.getName(), os.path.basename(job.getFileName())))

View File

@ -37,7 +37,11 @@ class RemovableDrivePlugin(OutputDevicePlugin):
raise NotImplementedError() raise NotImplementedError()
def ejectDevice(self, device): def ejectDevice(self, device):
result = self.performEjectDevice(device) try:
result = self.performEjectDevice(device)
except Exception as e:
result = False
if result: if result:
message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName())) message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName()))
message.show() message.show()

View File

@ -20,17 +20,17 @@ catalog = i18nCatalog("cura")
# WinAPI Constants that we need # WinAPI Constants that we need
# Hardcoded here due to stupid WinDLL stuff that does not give us access to these values. # Hardcoded here due to stupid WinDLL stuff that does not give us access to these values.
DRIVE_REMOVABLE = 2 DRIVE_REMOVABLE = 2 # [CodeStyle: Windows Enum value]
GENERIC_READ = 2147483648 GENERIC_READ = 2147483648 # [CodeStyle: Windows Enum value]
GENERIC_WRITE = 1073741824 GENERIC_WRITE = 1073741824 # [CodeStyle: Windows Enum value]
FILE_SHARE_READ = 1 FILE_SHARE_READ = 1 # [CodeStyle: Windows Enum value]
FILE_SHARE_WRITE = 2 FILE_SHARE_WRITE = 2 # [CodeStyle: Windows Enum value]
IOCTL_STORAGE_EJECT_MEDIA = 2967560 IOCTL_STORAGE_EJECT_MEDIA = 2967560 # [CodeStyle: Windows Enum value]
OPEN_EXISTING = 3 OPEN_EXISTING = 3 # [CodeStyle: Windows Enum value]
## Removable drive support for windows ## Removable drive support for windows
class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
@ -65,11 +65,11 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
continue continue
# Check for the free space. Some card readers show up as a drive with 0 space free when there is no card inserted. # Check for the free space. Some card readers show up as a drive with 0 space free when there is no card inserted.
freeBytes = ctypes.c_longlong(0) free_bytes = ctypes.c_longlong(0)
if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(freeBytes), None, None) == 0: if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(free_bytes), None, None) == 0:
continue continue
if freeBytes.value < 1: if free_bytes.value < 1:
continue continue
drives[drive] = "{0} ({1}:)".format(volume_name, letter) drives[drive] = "{0} ({1}:)".format(volume_name, letter)
@ -88,13 +88,10 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
result = None result = None
# Then, try and tell it to eject # Then, try and tell it to eject
try: if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None):
if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None):
result = False
else:
result = True
except Exception as e:
result = False result = False
else:
result = True
# Finally, close the handle # Finally, close the handle
windll.kernel32.CloseHandle(handle) windll.kernel32.CloseHandle(handle)

View File

@ -110,7 +110,7 @@ class SliceInfo(Extension):
# Convert data to bytes # Convert data to bytes
submitted_data = urllib.parse.urlencode(submitted_data) submitted_data = urllib.parse.urlencode(submitted_data)
binary_data = submitted_data.encode('utf-8') binary_data = submitted_data.encode("utf-8")
# Submit data # Submit data
try: try:

View File

@ -0,0 +1,68 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Resources import Resources
from UM.Application import Application
from UM.Math.Color import Color
from UM.Preferences import Preferences
from UM.View.Renderer import Renderer
from UM.View.GL.OpenGL import OpenGL
import math
## Standard view for mesh models.
class SolidView(View):
def __init__(self):
super().__init__()
Preferences.getInstance().addPreference("view/show_overhang", True)
self._enabled_shader = None
self._disabled_shader = None
def beginRendering(self):
scene = self.getController().getScene()
renderer = self.getRenderer()
if not self._enabled_shader:
self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
if not self._disabled_shader:
self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
self._disabled_shader.setUniformValue("u_diffuseColor", [0.68, 0.68, 0.68, 1.0])
if Application.getInstance().getMachineManager().getActiveProfile():
profile = Application.getInstance().getMachineManager().getActiveProfile()
if profile.getSettingValue("support_enable") or not Preferences.getInstance().getValue("view/show_overhang"):
angle = profile.getSettingValue("support_angle")
if angle != None:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle)))
else:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
for node in DepthFirstIterator(scene.getRoot()):
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
# TODO: Find a better way to handle this
#if node.getBoundingBoxMesh():
# renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
if hasattr(node, "_outside_buildarea"):
if node._outside_buildarea:
renderer.queueNode(node, shader = self._disabled_shader)
else:
renderer.queueNode(node, shader = self._enabled_shader)
else:
renderer.queueNode(node, material = self._enabled_shader)
if node.callDecoration("isGroup"):
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
def endRendering(self):
pass
#def _onPreferenceChanged(self, preference):
#if preference == "view/show_overhang": ## Todo: This a printer only setting. Should be removed from Uranium.
#self._enabled_material = None

View File

@ -0,0 +1,24 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from . import SolidView
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": i18n_catalog.i18nc("@label", "Solid View"),
"author": "Ultimaker",
"version": "1.0",
"decription": i18n_catalog.i18nc("@info:whatsthis", "Provides a normal solid mesh view."),
"api": 2
},
"view": {
"name": i18n_catalog.i18nc("@item:inmenu", "Solid")
}
}
def register(app):
return { "view": SolidView.SolidView() }

View File

@ -45,7 +45,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
self._connect_thread.daemon = True self._connect_thread.daemon = True
self._end_stop_thread = threading.Thread(target = self._pollEndStop) 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 # Printer is connected
self._is_connected = False self._is_connected = False
@ -63,7 +64,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
self._listen_thread.daemon = True self._listen_thread.daemon = True
self._update_firmware_thread = threading.Thread(target= self._updateFirmware) self._update_firmware_thread = threading.Thread(target= self._updateFirmware)
self._update_firmware_thread.deamon = True self._update_firmware_thread.daemon = True
self.firmwareUpdateComplete.connect(self._onFirmwareUpdateComplete)
self._heatup_wait_start_time = time.time() self._heatup_wait_start_time = time.time()
@ -122,6 +124,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
progressChanged = pyqtSignal() progressChanged = pyqtSignal()
extruderTemperatureChanged = pyqtSignal() extruderTemperatureChanged = pyqtSignal()
bedTemperatureChanged = pyqtSignal() bedTemperatureChanged = pyqtSignal()
firmwareUpdateComplete = pyqtSignal()
endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"]) endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"])
@ -195,6 +198,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
## Private fuction (threaded) that actually uploads the firmware. ## Private fuction (threaded) that actually uploads the firmware.
def _updateFirmware(self): def _updateFirmware(self):
self.setProgress(0, 100)
if self._is_connecting or self._is_connected: if self._is_connecting or self._is_connected:
self.close() self.close()
hex_file = intelHex.readHex(self._firmware_file_name) hex_file = intelHex.readHex(self._firmware_file_name)
@ -205,7 +210,11 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
programmer = stk500v2.Stk500v2() programmer = stk500v2.Stk500v2()
programmer.progressCallback = self.setProgress 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. time.sleep(1) # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
@ -237,8 +246,9 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
@pyqtSlot() @pyqtSlot()
def startPollEndstop(self): def startPollEndstop(self):
self._poll_endstop = True if self._poll_endstop == -1:
self._end_stop_thread.start() self._poll_endstop = True
self._end_stop_thread.start()
@pyqtSlot() @pyqtSlot()
def stopPollEndstop(self): def stopPollEndstop(self):
@ -323,6 +333,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
## Close the printer connection ## Close the printer connection
def close(self): def close(self):
Logger.log("d", "Closing the printer connection.")
if self._connect_thread.isAlive(): if self._connect_thread.isAlive():
try: try:
self._connect_thread.join() self._connect_thread.join()
@ -332,8 +343,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
self._connect_thread = threading.Thread(target=self._connect) self._connect_thread = threading.Thread(target=self._connect)
self._connect_thread.daemon = True self._connect_thread.daemon = True
self.setIsConnected(False)
if self._serial is not None: if self._serial is not None:
self.setIsConnected(False)
try: try:
self._listen_thread.join() self._listen_thread.join()
except: except:
@ -345,7 +356,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
self._serial = None self._serial = None
def isConnected(self): def isConnected(self):
return self._is_connected return self._is_connected
@pyqtSlot(int) @pyqtSlot(int)
def heatupNozzle(self, temperature): def heatupNozzle(self, temperature):
@ -411,6 +422,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
def createControlInterface(self): def createControlInterface(self):
if self._control_view is None: if self._control_view is None:
Logger.log("d", "Creating control interface for printer connection")
path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "ControlWindow.qml")) path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "ControlWindow.qml"))
component = QQmlComponent(Application.getInstance()._engine, path) component = QQmlComponent(Application.getInstance()._engine, path)
self._control_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._control_context = QQmlContext(Application.getInstance()._engine.rootContext())
@ -455,21 +467,21 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
self._bed_temperature = temperature self._bed_temperature = temperature
self.bedTemperatureChanged.emit() self.bedTemperatureChanged.emit()
def requestWrite(self, node): def requestWrite(self, node, file_name = None):
self.showControlInterface() self.showControlInterface()
def _setEndstopState(self, endstop_key, value): def _setEndstopState(self, endstop_key, value):
if endstop_key == b'x_min': if endstop_key == b"x_min":
if self._x_min_endstop_pressed != value: if self._x_min_endstop_pressed != value:
self.endstopStateChanged.emit('x_min', value) self.endstopStateChanged.emit("x_min", value)
self._x_min_endstop_pressed = value self._x_min_endstop_pressed = value
elif endstop_key == b'y_min': elif endstop_key == b"y_min":
if self._y_min_endstop_pressed != value: if self._y_min_endstop_pressed != value:
self.endstopStateChanged.emit('y_min', value) self.endstopStateChanged.emit("y_min", value)
self._y_min_endstop_pressed = value self._y_min_endstop_pressed = value
elif endstop_key == b'z_min': elif endstop_key == b"z_min":
if self._z_min_endstop_pressed != value: if self._z_min_endstop_pressed != value:
self.endstopStateChanged.emit('z_min', value) self.endstopStateChanged.emit("z_min", value)
self._z_min_endstop_pressed = value self._z_min_endstop_pressed = value
## Listen thread function. ## Listen thread function.
@ -516,8 +528,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
pass pass
#TODO: temperature changed callback #TODO: temperature changed callback
elif b"_min" in line or b"_max" in line: elif b"_min" in line or b"_max" in line:
tag, value = line.split(b':', 1) tag, value = line.split(b":", 1)
self._setEndstopState(tag,(b'H' in value or b'TRIGGERED' in value)) self._setEndstopState(tag,(b"H" in value or b"TRIGGERED" in value))
if self._is_printing: if self._is_printing:
if line == b"" and time.time() > ok_timeout: if line == b"" and time.time() > ok_timeout:
@ -617,6 +629,6 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter):
def _onFirmwareUpdateComplete(self): def _onFirmwareUpdateComplete(self):
self._update_firmware_thread.join() self._update_firmware_thread.join()
self._update_firmware_thread = threading.Thread(target= self._updateFirmware) self._update_firmware_thread = threading.Thread(target= self._updateFirmware)
self._update_firmware_thread.deamon = True self._update_firmware_thread.daemon = True
self.connect() self.connect()

View File

@ -11,6 +11,7 @@ from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.Message import Message
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
@ -54,6 +55,16 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
addConnectionSignal = Signal() addConnectionSignal = Signal()
printerConnectionStateChanged = pyqtSignal() printerConnectionStateChanged = pyqtSignal()
progressChanged = pyqtSignal()
@pyqtProperty(float, notify = progressChanged)
def progress(self):
progress = 0
for name, connection in self._printer_connections.items():
progress += connection.progress
return progress / len(self._printer_connections)
def start(self): def start(self):
self._check_updates = True self._check_updates = True
self._update_thread.start() self._update_thread.start()
@ -84,12 +95,18 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
self._firmware_view.show() self._firmware_view.show()
@pyqtSlot()
def updateAllFirmware(self): def updateAllFirmware(self):
if not self._printer_connections:
Message("Cannot update firmware, there were no connected printers found.").show()
return
self.spawnFirmwareInterface("") self.spawnFirmwareInterface("")
for printer_connection in self._printer_connections: for printer_connection in self._printer_connections:
try: try:
self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
except FileNotFoundError: except FileNotFoundError:
Logger.log("w", "No firmware found for printer %s", printer_connection)
continue continue
@pyqtSlot(str, result = bool) @pyqtSlot(str, result = bool)
@ -148,11 +165,22 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
continue continue
self._serial_port_list = list(serial_ports) 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. ## Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
def addConnection(self, serial_port): def addConnection(self, serial_port):
connection = PrinterConnection.PrinterConnection(serial_port) connection = PrinterConnection.PrinterConnection(serial_port)
connection.connect() connection.connect()
connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged) connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
connection.progressChanged.connect(self.progressChanged)
self._printer_connections[serial_port] = connection self._printer_connections[serial_port] = connection
def _onPrinterConnectionStateChanged(self, serial_port): def _onPrinterConnectionStateChanged(self, serial_port):
@ -196,4 +224,4 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*") base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*")
return list(base_list) return list(base_list)
_instance = None _instance = None

View File

@ -4,22 +4,22 @@ To support more chips add the relevant data to the avrChipDB list.
This is a python 3 conversion of the code created by David Braam for the Cura project. This is a python 3 conversion of the code created by David Braam for the Cura project.
""" """
avrChipDB = { avr_chip_db = {
'ATMega1280': { "ATMega1280": {
'signature': [0x1E, 0x97, 0x03], "signature": [0x1E, 0x97, 0x03],
'pageSize': 128, "pageSize": 128,
'pageCount': 512, "pageCount": 512,
}, },
'ATMega2560': { "ATMega2560": {
'signature': [0x1E, 0x98, 0x01], "signature": [0x1E, 0x98, 0x01],
'pageSize': 128, "pageSize": 128,
'pageCount': 1024, "pageCount": 1024,
}, },
} }
def getChipFromDB(sig): def getChipFromDB(sig):
for chip in avrChipDB.values(): for chip in avr_chip_db.values():
if chip['signature'] == sig: if chip["signature"] == sig:
return chip return chip
return False return False

View File

@ -11,36 +11,36 @@ def readHex(filename):
Read an verify an intel hex file. Return the data as an list of bytes. Read an verify an intel hex file. Return the data as an list of bytes.
""" """
data = [] data = []
extraAddr = 0 extra_addr = 0
f = io.open(filename, "r") f = io.open(filename, "r")
for line in f: for line in f:
line = line.strip() line = line.strip()
if len(line) < 1: if len(line) < 1:
continue continue
if line[0] != ':': if line[0] != ":":
raise Exception("Hex file has a line not starting with ':'") raise Exception("Hex file has a line not starting with ':'")
recLen = int(line[1:3], 16) rec_len = int(line[1:3], 16)
addr = int(line[3:7], 16) + extraAddr addr = int(line[3:7], 16) + extra_addr
recType = int(line[7:9], 16) rec_type = int(line[7:9], 16)
if len(line) != recLen * 2 + 11: if len(line) != rec_len * 2 + 11:
raise Exception("Error in hex file: " + line) raise Exception("Error in hex file: " + line)
checkSum = 0 check_sum = 0
for i in range(0, recLen + 5): for i in range(0, rec_len + 5):
checkSum += int(line[i*2+1:i*2+3], 16) check_sum += int(line[i*2+1:i*2+3], 16)
checkSum &= 0xFF check_sum &= 0xFF
if checkSum != 0: if check_sum != 0:
raise Exception("Checksum error in hex file: " + line) raise Exception("Checksum error in hex file: " + line)
if recType == 0:#Data record if rec_type == 0:#Data record
while len(data) < addr + recLen: while len(data) < addr + rec_len:
data.append(0) data.append(0)
for i in range(0, recLen): for i in range(0, rec_len):
data[addr + i] = int(line[i*2+9:i*2+11], 16) data[addr + i] = int(line[i*2+9:i*2+11], 16)
elif recType == 1: #End Of File record elif rec_type == 1: #End Of File record
pass pass
elif recType == 2: #Extended Segment Address Record elif rec_type == 2: #Extended Segment Address Record
extraAddr = int(line[9:13], 16) * 16 extra_addr = int(line[9:13], 16) * 16
else: else:
print(recType, recLen, addr, checkSum, line) print(rec_type, rec_len, addr, check_sum, line)
f.close() f.close()
return data return data

View File

@ -14,18 +14,18 @@ class IspBase():
Base class for ISP based AVR programmers. Base class for ISP based AVR programmers.
Functions in this class raise an IspError when something goes wrong. Functions in this class raise an IspError when something goes wrong.
""" """
def programChip(self, flashData): def programChip(self, flash_data):
""" Program a chip with the given flash data. """ """ Program a chip with the given flash data. """
self.curExtAddr = -1 self.cur_ext_addr = -1
self.chip = chipDB.getChipFromDB(self.getSignature()) self.chip = chipDB.getChipFromDB(self.getSignature())
if not self.chip: if not self.chip:
raise IspError("Chip with signature: " + str(self.getSignature()) + "not found") raise IspError("Chip with signature: " + str(self.getSignature()) + "not found")
self.chipErase() self.chipErase()
print("Flashing %i bytes" % len(flashData)) print("Flashing %i bytes" % len(flash_data))
self.writeFlash(flashData) self.writeFlash(flash_data)
print("Verifying %i bytes" % len(flashData)) print("Verifying %i bytes" % len(flash_data))
self.verifyFlash(flashData) self.verifyFlash(flash_data)
print("Completed") print("Completed")
def getSignature(self): def getSignature(self):
@ -45,20 +45,20 @@ class IspBase():
""" """
self.sendISP([0xAC, 0x80, 0x00, 0x00]) self.sendISP([0xAC, 0x80, 0x00, 0x00])
def writeFlash(self, flashData): def writeFlash(self, flash_data):
""" """
Write the flash data, needs to be implemented in a subclass. Write the flash data, needs to be implemented in a subclass.
""" """
raise IspError("Called undefined writeFlash") raise IspError("Called undefined writeFlash")
def verifyFlash(self, flashData): def verifyFlash(self, flash_data):
""" """
Verify the flash data, needs to be implemented in a subclass. Verify the flash data, needs to be implemented in a subclass.
""" """
raise IspError("Called undefined verifyFlash") raise IspError("Called undefined verifyFlash")
class IspError(BaseException): class IspError(Exception):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value

View File

@ -19,10 +19,10 @@ class Stk500v2(ispBase.IspBase):
def __init__(self): def __init__(self):
self.serial = None self.serial = None
self.seq = 1 self.seq = 1
self.lastAddr = -1 self.last_addr = -1
self.progressCallback = None self.progress_callback = None
def connect(self, port = 'COM22', speed = 115200): def connect(self, port = "COM22", speed = 115200):
if self.serial is not None: if self.serial is not None:
self.close() self.close()
try: try:
@ -82,49 +82,49 @@ class Stk500v2(ispBase.IspBase):
def writeFlash(self, flash_data): def writeFlash(self, flash_data):
#Set load addr to 0, in case we have more then 64k flash we need to enable the address extension #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
page_size = self.chip['pageSize'] * 2 page_size = self.chip["pageSize"] * 2
flashSize = page_size * self.chip['pageCount'] flash_size = page_size * self.chip["pageCount"]
print("Writing flash") print("Writing flash")
if flashSize > 0xFFFF: if flash_size > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else: else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00]) self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
load_count = (len(flash_data) + page_size - 1) / page_size load_count = (len(flash_data) + page_size - 1) / page_size
for i in range(0, int(load_count)): for i in range(0, int(load_count)):
recv = self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)]) recv = self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)])
if self.progressCallback is not None: if self.progress_callback is not None:
if self._has_checksum: if self._has_checksum:
self.progressCallback(i + 1, load_count) self.progress_callback(i + 1, load_count)
else: else:
self.progressCallback(i + 1, load_count*2) self.progress_callback(i + 1, load_count*2)
def verifyFlash(self, flashData): def verifyFlash(self, flash_data):
if self._has_checksum: if self._has_checksum:
self.sendMessage([0x06, 0x00, (len(flashData) >> 17) & 0xFF, (len(flashData) >> 9) & 0xFF, (len(flashData) >> 1) & 0xFF]) self.sendMessage([0x06, 0x00, (len(flash_data) >> 17) & 0xFF, (len(flash_data) >> 9) & 0xFF, (len(flash_data) >> 1) & 0xFF])
res = self.sendMessage([0xEE]) res = self.sendMessage([0xEE])
checksum_recv = res[2] | (res[3] << 8) checksum_recv = res[2] | (res[3] << 8)
checksum = 0 checksum = 0
for d in flashData: for d in flash_data:
checksum += d checksum += d
checksum &= 0xFFFF checksum &= 0xFFFF
if hex(checksum) != hex(checksum_recv): if hex(checksum) != hex(checksum_recv):
raise ispBase.IspError('Verify checksum mismatch: 0x%x != 0x%x' % (checksum & 0xFFFF, checksum_recv)) raise ispBase.IspError("Verify checksum mismatch: 0x%x != 0x%x" % (checksum & 0xFFFF, checksum_recv))
else: else:
#Set load addr to 0, in case we have more then 64k flash we need to enable the address extension #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
flashSize = self.chip['pageSize'] * 2 * self.chip['pageCount'] flash_size = self.chip["pageSize"] * 2 * self.chip["pageCount"]
if flashSize > 0xFFFF: if flash_size > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else: else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00]) self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
loadCount = (len(flashData) + 0xFF) / 0x100 load_count = (len(flash_data) + 0xFF) / 0x100
for i in range(0, int(loadCount)): for i in range(0, int(load_count)):
recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102] recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102]
if self.progressCallback is not None: if self.progress_callback is not None:
self.progressCallback(loadCount + i + 1, loadCount*2) self.progress_callback(load_count + i + 1, load_count*2)
for j in range(0, 0x100): for j in range(0, 0x100):
if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]: if i * 0x100 + j < len(flash_data) and flash_data[i * 0x100 + j] != recv[j]:
raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + j)) raise ispBase.IspError("Verify error at: 0x%x" % (i * 0x100 + j))
def sendMessage(self, data): def sendMessage(self, data):
message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E) message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E)
@ -138,12 +138,12 @@ class Stk500v2(ispBase.IspBase):
self.serial.write(message) self.serial.write(message)
self.serial.flush() self.serial.flush()
except SerialTimeoutException: except SerialTimeoutException:
raise ispBase.IspError('Serial send timeout') raise ispBase.IspError("Serial send timeout")
self.seq = (self.seq + 1) & 0xFF self.seq = (self.seq + 1) & 0xFF
return self.recvMessage() return self.recvMessage()
def recvMessage(self): def recvMessage(self):
state = 'Start' state = "Start"
checksum = 0 checksum = 0
while True: while True:
s = self.serial.read() s = self.serial.read()
@ -152,31 +152,31 @@ class Stk500v2(ispBase.IspBase):
b = struct.unpack(">B", s)[0] b = struct.unpack(">B", s)[0]
checksum ^= b checksum ^= b
#print(hex(b)) #print(hex(b))
if state == 'Start': if state == "Start":
if b == 0x1B: if b == 0x1B:
state = 'GetSeq' state = "GetSeq"
checksum = 0x1B checksum = 0x1B
elif state == 'GetSeq': elif state == "GetSeq":
state = 'MsgSize1' state = "MsgSize1"
elif state == 'MsgSize1': elif state == "MsgSize1":
msgSize = b << 8 msg_size = b << 8
state = 'MsgSize2' state = "MsgSize2"
elif state == 'MsgSize2': elif state == "MsgSize2":
msgSize |= b msg_size |= b
state = 'Token' state = "Token"
elif state == 'Token': elif state == "Token":
if b != 0x0E: if b != 0x0E:
state = 'Start' state = "Start"
else: else:
state = 'Data' state = "Data"
data = [] data = []
elif state == 'Data': elif state == "Data":
data.append(b) data.append(b)
if len(data) == msgSize: if len(data) == msg_size:
state = 'Checksum' state = "Checksum"
elif state == 'Checksum': elif state == "Checksum":
if checksum != 0: if checksum != 0:
state = 'Start' state = "Start"
else: else:
return data return data
@ -190,7 +190,7 @@ def portList():
values = _winreg.EnumValue(key, i) values = _winreg.EnumValue(key, i)
except: except:
return ret return ret
if 'USBSER' in values[0]: if "USBSER" in values[0]:
ret.append(values[1]) ret.append(values[1])
i+=1 i+=1
return ret return ret
@ -205,7 +205,7 @@ def runProgrammer(port, filename):
def main(): def main():
""" Entry point to call the stk500v2 programmer from the commandline. """ """ Entry point to call the stk500v2 programmer from the commandline. """
import threading import threading
if sys.argv[1] == 'AUTO': if sys.argv[1] == "AUTO":
print(portList()) print(portList())
for port in portList(): for port in portList():
threading.Thread(target=runProgrammer, args=(port,sys.argv[2])).start() threading.Thread(target=runProgrammer, args=(port,sys.argv[2])).start()
@ -216,5 +216,5 @@ def main():
programmer.programChip(intelHex.readHex(sys.argv[2])) programmer.programChip(intelHex.readHex(sys.argv[2]))
sys.exit(1) sys.exit(1)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@ -0,0 +1,39 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import os.path
from UM.Application import Application
from UM.PluginRegistry import PluginRegistry
from UM.View.RenderPass import RenderPass
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
class XRayPass(RenderPass):
def __init__(self, width, height):
super().__init__("xray", width, height)
self._shader = None
self._gl = OpenGL.getInstance().getBindingsObject()
self._scene = Application.getInstance().getController().getScene()
def render(self):
if not self._shader:
self._shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray.shader"))
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData() and node.isVisible():
batch.addItem(node.getWorldTransformation(), node.getMeshData())
self.bind()
self._gl.glDisable(self._gl.GL_DEPTH_TEST)
batch.render(self._scene.getActiveCamera())
self._gl.glEnable(self._gl.GL_DEPTH_TEST)
self.release()

View File

@ -0,0 +1,72 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import os.path
from UM.PluginRegistry import PluginRegistry
from UM.Event import Event
from UM.View.View import View
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from . import XRayPass
## View used to display a see-through version of objects with errors highlighted.
class XRayView(View):
def __init__(self):
super().__init__()
self._xray_shader = None
self._xray_pass = None
self._xray_composite_shader = None
self._composite_pass = None
self._old_composite_shader = None
self._old_layer_bindings = None
def beginRendering(self):
scene = self.getController().getScene()
renderer = self.getRenderer()
if not self._xray_shader:
self._xray_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray.shader"))
self._xray_shader.setUniformValue("u_color", [0.1, 0.1, 0.2, 1.0])
for node in BreadthFirstIterator(scene.getRoot()):
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
renderer.queueNode(node,
shader = self._xray_shader,
type = RenderBatch.RenderType.Solid,
blend_mode = RenderBatch.BlendMode.Additive,
sort = -10,
state_setup_callback = lambda gl: gl.glDepthFunc(gl.GL_ALWAYS),
state_teardown_callback = lambda gl: gl.glDepthFunc(gl.GL_LESS)
)
def endRendering(self):
pass
def event(self, event):
if event.type == Event.ViewActivateEvent:
if not self._xray_pass:
# Currently the RenderPass constructor requires a size > 0
# This should be fixed in RenderPass's constructor.
self._xray_pass = XRayPass.XRayPass(1, 1)
self.getRenderer().addRenderPass(self._xray_pass)
if not self._xray_composite_shader:
self._xray_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray_composite.shader"))
if not self._composite_pass:
self._composite_pass = self.getRenderer().getRenderPass("composite")
self._old_layer_bindings = self._composite_pass.getLayerBindings()
self._composite_pass.setLayerBindings(["default", "selection", "xray"])
self._old_composite_shader = self._composite_pass.getCompositeShader()
self._composite_pass.setCompositeShader(self._xray_composite_shader)
if event.type == Event.ViewDeactivateEvent:
self._composite_pass.setLayerBindings(self._old_layer_bindings)
self._composite_pass.setCompositeShader(self._old_composite_shader)

View File

@ -0,0 +1,24 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from . import XRayView
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": catalog.i18nc("@label", "X-Ray View"),
"author": "Ultimaker",
"version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides the X-Ray view."),
"api": 2
},
"view": {
"name": catalog.i18nc("@item:inlistbox", "X-Ray")
}
}
def register(app):
return { "view": XRayView.XRayView() }

View File

@ -0,0 +1,27 @@
[shaders]
vertex =
uniform highp mat4 u_modelViewProjectionMatrix;
attribute highp vec4 a_vertex;
void main()
{
gl_Position = u_modelViewProjectionMatrix * a_vertex;
}
fragment =
uniform lowp vec4 u_color;
void main()
{
gl_FragColor = u_color;
}
[defaults]
u_color = [0.02, 0.02, 0.02, 1.0]
[bindings]
u_modelViewProjectionMatrix = model_view_projection_matrix
[attributes]
a_vertex = vertex

View File

@ -0,0 +1,75 @@
[shaders]
vertex =
uniform highp mat4 u_modelViewProjectionMatrix;
attribute highp vec4 a_vertex;
attribute highp vec2 a_uvs;
varying highp vec2 v_uvs;
void main()
{
gl_Position = u_modelViewProjectionMatrix * a_vertex;
v_uvs = a_uvs;
}
fragment =
uniform sampler2D u_layer0;
uniform sampler2D u_layer1;
uniform sampler2D u_layer2;
uniform sampler2D u_layer3;
uniform float u_imageWidth;
uniform float u_imageHeight;
uniform vec2 u_offset[9];
uniform float u_outline_strength;
uniform vec4 u_outline_color;
uniform vec4 u_error_color;
varying vec2 v_uvs;
float kernel[9];
void main()
{
kernel[0] = 0.0; kernel[1] = 1.0; kernel[2] = 0.0;
kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0;
kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0;
vec4 result = vec4(0.965, 0.965, 0.965, 1.0);
vec4 layer0 = texture2D(u_layer0, v_uvs);
result = layer0 * layer0.a + result * (1.0 - layer0.a);
float intersection_count = (texture2D(u_layer2, v_uvs).r * 255.0) / 5.0;
if(mod(intersection_count, 2.0) == 1.0)
{
result = u_error_color;
}
vec4 sum = vec4(0.0);
for (int i = 0; i < 9; i++)
{
vec4 color = vec4(texture2D(u_layer1, v_uvs.xy + u_offset[i]).a);
sum += color * (kernel[i] / u_outline_strength);
}
gl_FragColor = mix(result, vec4(abs(sum.a)) * u_outline_color, abs(sum.a));
}
[defaults]
u_layer0 = 0
u_layer1 = 1
u_layer2 = 2
u_layer3 = 3
u_outline_strength = 1.0
u_outline_color = [0.05, 0.66, 0.89, 1.0]
u_error_color = [1.0, 0.0, 0.0, 1.0]
[bindings]
[attributes]
a_vertex = vertex
a_uvs = uv

2130
resources/i18n/de/cura.po Normal file → Executable file

File diff suppressed because it is too large Load Diff

5649
resources/i18n/de/fdmprinter.json.po Normal file → Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

325
resources/i18n/fi/cura.po Normal file → Executable file
View File

@ -8,20 +8,20 @@ msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-12 20:10+0200\n" "POT-Creation-Date: 2015-09-12 20:10+0200\n"
"PO-Revision-Date: 2015-06-30 18:02+0300\n" "PO-Revision-Date: 2015-09-28 14:08+0300\n"
"Last-Translator: Tapio <info@tapimex.fi>\n" "Last-Translator: Tapio <info@tapimex.fi>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: fi_FI\n" "Language: fi_FI\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.2\n" "X-Generator: Poedit 1.8.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:20 #: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:20
msgctxt "@title:window" msgctxt "@title:window"
msgid "Oops!" msgid "Oops!"
msgstr "" msgstr "Hups!"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:26 #: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:26
msgctxt "@label" msgctxt "@label"
@ -30,11 +30,14 @@ msgid ""
"below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/" "below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/"
"issues\">http://github.com/Ultimaker/Cura/issues</a></p>" "issues\">http://github.com/Ultimaker/Cura/issues</a></p>"
msgstr "" msgstr ""
"<p>Tapahtui epätavallinen poikkeus!</p><p>Lähetä virheraportti alla olevin "
"tiedoin osoitteella <a href=\"http://github.com/Ultimaker/Cura/issues"
"\">http://github.com/Ultimaker/Cura/issues</a></p>"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:46 #: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:46
msgctxt "@action:button" msgctxt "@action:button"
msgid "Open Web Page" msgid "Open Web Page"
msgstr "" msgstr "Avaa verkkosivu"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CuraApplication.py:135 #: /home/ahiemstra/dev/15.10/src/cura/cura/CuraApplication.py:135
#, fuzzy #, fuzzy
@ -51,34 +54,32 @@ msgstr "Ladataan käyttöliittymää..."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:12 #: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:12
msgctxt "@label" msgctxt "@label"
msgid "3MF Reader" msgid "3MF Reader"
msgstr "" msgstr "3MF Reader"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:15 #: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:15
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Provides support for reading 3MF files." msgid "Provides support for reading 3MF files."
msgstr "" msgstr "Tukee 3MF-tiedostojen lukemista."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:20 #: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:20
#, fuzzy
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "3MF File" msgid "3MF File"
msgstr "&Tiedosto" msgstr "3MF-tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:12 #: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:12
#, fuzzy
msgctxt "@label" msgctxt "@label"
msgid "Change Log" msgid "Change Log"
msgstr "Moottorin loki" msgstr "Muutosloki"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:15 #: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:15
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Shows changes since latest checked version" msgid "Shows changes since latest checked version"
msgstr "" msgstr "Näyttää viimeisimmän tarkistetun version jälkeen tapahtuneet muutokset"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/__init__.py:13 #: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/__init__.py:13
msgctxt "@label" msgctxt "@label"
msgid "CuraEngine Backend" msgid "CuraEngine Backend"
msgstr "" msgstr "CuraEngine-taustaosa"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/__init__.py:15 #: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/__init__.py:15
#, fuzzy #, fuzzy
@ -90,25 +91,22 @@ msgstr "Linkki CuraEngine-viipalointiin taustalla"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py:111 #: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py:111
msgctxt "@info:status" msgctxt "@info:status"
msgid "Processing Layers" msgid "Processing Layers"
msgstr "" msgstr "Käsitellään kerroksia"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/CuraEngineBackend.py:169 #: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/CuraEngineBackend.py:169
#, fuzzy
msgctxt "@info:status" msgctxt "@info:status"
msgid "Slicing..." msgid "Slicing..."
msgstr "Lasketaan..." msgstr "Viipaloidaan..."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:12 #: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:12
#, fuzzy
msgctxt "@label" msgctxt "@label"
msgid "GCode Writer" msgid "GCode Writer"
msgstr "GCode-tiedosto" msgstr "GCode Writer"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:15 #: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:15
#, fuzzy
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Writes GCode to a file" msgid "Writes GCode to a file"
msgstr "Kirjoittaa GCoden tiedostoon" msgstr "Kirjoittaa GCodea tiedostoon"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:22 #: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:22
#, fuzzy #, fuzzy
@ -117,19 +115,16 @@ msgid "GCode File"
msgstr "GCode-tiedosto" msgstr "GCode-tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:13 #: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:13
#, fuzzy
msgctxt "@label" msgctxt "@label"
msgid "Layer View" msgid "Layer View"
msgstr "Kerrokset" msgstr "Kerrosnäkymä"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:16 #: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:16
#, fuzzy
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Provides the Layer view." msgid "Provides the Layer view."
msgstr "Kerrosnäkymä." msgstr "Näyttää kerrosnäkymän."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:20 #: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:20
#, fuzzy
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Layers" msgid "Layers"
msgstr "Kerrokset" msgstr "Kerrokset"
@ -137,28 +132,27 @@ msgstr "Kerrokset"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:20 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:20
msgctxt "@action:button" msgctxt "@action:button"
msgid "Save to Removable Drive" msgid "Save to Removable Drive"
msgstr "" msgstr "Tallenna siirrettävälle asemalle"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:21 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:21
#, fuzzy, python-brace-format #, python-brace-format
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Save to Removable Drive {0}" msgid "Save to Removable Drive {0}"
msgstr "Tallenna SD-kortille {0}" msgstr "Tallenna siirrettävälle asemalle {0}"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:52 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:52
#, python-brace-format #, python-brace-format
msgctxt "@info:progress" msgctxt "@info:progress"
msgid "Saving to Removable Drive <filename>{0}</filename>" msgid "Saving to Removable Drive <filename>{0}</filename>"
msgstr "" msgstr "Tallennetaan siirrettävälle asemalle <filename>{0}</filename>"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:73 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:73
#, fuzzy, python-brace-format #, python-brace-format
msgctxt "@info:status" msgctxt "@info:status"
msgid "Saved to Removable Drive {0} as {1}" msgid "Saved to Removable Drive {0} as {1}"
msgstr "Tallennettu SD-kortille {0} nimellä {1}" msgstr "Tallennettu siirrettävälle asemalle {0} nimellä {1}"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:74 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:74
#, fuzzy
msgctxt "@action:button" msgctxt "@action:button"
msgid "Eject" msgid "Eject"
msgstr "Poista" msgstr "Poista"
@ -167,96 +161,98 @@ msgstr "Poista"
#, python-brace-format #, python-brace-format
msgctxt "@action" msgctxt "@action"
msgid "Eject removable device {0}" msgid "Eject removable device {0}"
msgstr "" msgstr "Poista siirrettävä asema {0}"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:79 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:79
#, python-brace-format #, python-brace-format
msgctxt "@info:status" msgctxt "@info:status"
msgid "Could not save to removable drive {0}: {1}" msgid "Could not save to removable drive {0}: {1}"
msgstr "" msgstr "Ei voitu tallentaa siirrettävälle asemalle {0}: {1}"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py:58 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py:58
msgctxt "@item:intext" msgctxt "@item:intext"
msgid "Removable Drive" msgid "Removable Drive"
msgstr "" msgstr "Siirrettävä asema"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py:42 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py:42
#, python-brace-format #, python-brace-format
msgctxt "@info:status" msgctxt "@info:status"
msgid "Ejected {0}. You can now safely remove the drive." msgid "Ejected {0}. You can now safely remove the drive."
msgstr "" msgstr "Poistettu {0}. Voit nyt poistaa aseman turvallisesti."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py:45 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py:45
#, python-brace-format #, python-brace-format
msgctxt "@info:status" msgctxt "@info:status"
msgid "Failed to eject {0}. Maybe it is still in use?" msgid "Failed to eject {0}. Maybe it is still in use?"
msgstr "" msgstr "{0} poisto epäonnistui. Onko se vielä käytössä?"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/__init__.py:12 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/__init__.py:12
msgctxt "@label" msgctxt "@label"
msgid "Removable Drive Output Device Plugin" msgid "Removable Drive Output Device Plugin"
msgstr "" msgstr "Irrotettavan aseman lisäosa"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/__init__.py:14 #: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/__init__.py:14
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Provides removable drive hotplugging and writing support" msgid "Provides removable drive hotplugging and writing support"
msgstr "" msgstr "Tukee irrotettavan aseman kytkemistä lennossa ja sille kirjoittamista"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/__init__.py:10 #: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/__init__.py:10
msgctxt "@label" msgctxt "@label"
msgid "Slice info" msgid "Slice info"
msgstr "" msgstr "Viipalointitiedot"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/__init__.py:13 #: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/__init__.py:13
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "Submits anonymous slice info. Can be disabled through preferences." msgid "Submits anonymous slice info. Can be disabled through preferences."
msgstr "" msgstr ""
"Lähettää anonyymiä viipalointitietoa. Voidaan lisäasetuksista kytkeä pois "
"käytöstä."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/SliceInfo.py:35 #: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/SliceInfo.py:35
msgctxt "@info" msgctxt "@info"
msgid "" msgid ""
"Cura automatically sends slice info. You can disable this in preferences" "Cura automatically sends slice info. You can disable this in preferences"
msgstr "" msgstr ""
"Cura lähettää automaattisesti viipalointitietoa. Voit lisäasetuksista kytkeä "
"sen pois käytöstä"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/SliceInfo.py:36 #: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/SliceInfo.py:36
msgctxt "@action:button" msgctxt "@action:button"
msgid "Dismiss" msgid "Dismiss"
msgstr "" msgstr "Ohita"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:35 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:35
msgctxt "@item:inmenu" msgctxt "@item:inmenu"
msgid "USB printing" msgid "USB printing"
msgstr "" msgstr "USB-tulostus"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:36 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:36
msgctxt "@action:button" msgctxt "@action:button"
msgid "Print with USB" msgid "Print with USB"
msgstr "" msgstr "Tulosta USB:llä"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:37 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:37
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
msgid "Print with USB" msgid "Print with USB"
msgstr "" msgstr "Tulostus USB:n kautta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:13 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:13
msgctxt "@label" msgctxt "@label"
msgid "USB printing" msgid "USB printing"
msgstr "" msgstr "USB-tulostus"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:17 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:17
#, fuzzy
msgctxt "@info:whatsthis" msgctxt "@info:whatsthis"
msgid "" msgid ""
"Accepts G-Code and sends them to a printer. Plugin can also update firmware." "Accepts G-Code and sends them to a printer. Plugin can also update firmware."
msgstr "" msgstr ""
"Hyväksyy G-Coden ja lähettää ne tulostimeen. Lisäosa voi myös päivittää " "Hyväksyy G-Code-määrittelyt ja lähettää ne tulostimeen. Lisäosa voi myös "
"laiteohjelmiston" "päivittää laiteohjelmiston."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:46 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:46
#, fuzzy
msgctxt "@title:menu" msgctxt "@title:menu"
msgid "Firmware" msgid "Firmware"
msgstr "Päivitä laiteohjelmisto" msgstr "Laiteohjelmisto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:47 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:47
#, fuzzy #, fuzzy
@ -267,7 +263,7 @@ msgstr "Päivitä laiteohjelmisto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:17 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:17
msgctxt "@title:window" msgctxt "@title:window"
msgid "Print with USB" msgid "Print with USB"
msgstr "" msgstr "Tulostus USB:n kautta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:28 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:28
#, fuzzy #, fuzzy
@ -294,10 +290,9 @@ msgid "Cancel"
msgstr "Peruuta" msgstr "Peruuta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:20 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:20
#, fuzzy
msgctxt "@title:window" msgctxt "@title:window"
msgid "Firmware Update" msgid "Firmware Update"
msgstr "Laiteohjelmiston päivitys suoritettu." msgstr "Laiteohjelmiston päivitys"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:38 #: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:38
#, fuzzy #, fuzzy
@ -347,12 +342,12 @@ msgstr "Moottorin loki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:31 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:31
msgctxt "@label" msgctxt "@label"
msgid "Variant:" msgid "Variant:"
msgstr "" msgstr "Variantti:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:82 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:82
msgctxt "@label" msgctxt "@label"
msgid "Global Profile:" msgid "Global Profile:"
msgstr "" msgstr "Yleisprofiili:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:60 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:60
#, fuzzy #, fuzzy
@ -370,31 +365,29 @@ msgstr "Tulostimen nimi:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:29 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:29
msgctxt "@title" msgctxt "@title"
msgid "Select Upgraded Parts" msgid "Select Upgraded Parts"
msgstr "" msgstr "Valitse päivitettävät osat"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:214 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:214
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:29 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:29
#, fuzzy
msgctxt "@title" msgctxt "@title"
msgid "Upgrade Firmware" msgid "Upgrade Firmware"
msgstr "Päivitä laiteohjelmisto" msgstr "Laiteohjelmiston päivitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:217 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:217
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:37 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:37
#, fuzzy
msgctxt "@title" msgctxt "@title"
msgid "Check Printer" msgid "Check Printer"
msgstr "Lisää tulostin" msgstr "Tarkista tulostin"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:220 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:220
msgctxt "@title" msgctxt "@title"
msgid "Bed Levelling" msgid "Bed Levelling"
msgstr "" msgstr "Pöydän tasaaminen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:25 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:25
msgctxt "@title" msgctxt "@title"
msgid "Bed Leveling" msgid "Bed Leveling"
msgstr "" msgstr "Pöydän tasaaminen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:34 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:34
msgctxt "@label" msgctxt "@label"
@ -403,6 +396,8 @@ msgid ""
"buildplate. When you click 'Move to Next Position' the nozzle will move to " "buildplate. When you click 'Move to Next Position' the nozzle will move to "
"the different positions that can be adjusted." "the different positions that can be adjusted."
msgstr "" msgstr ""
"Voit säätää alustaa, jotta tulosteista tulisi hyviä. Kun napsautat 'Siirry "
"seuraavaan positioon', suutin siirtyy eri positioihin, joita voidaan säätää."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:40 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:40
msgctxt "@label" msgctxt "@label"
@ -411,16 +406,19 @@ msgid ""
"print bed height. The print bed height is right when the paper is slightly " "print bed height. The print bed height is right when the paper is slightly "
"gripped by the tip of the nozzle." "gripped by the tip of the nozzle."
msgstr "" msgstr ""
"Laita paperinpala kussakin positiossa suuttimen alle ja säädä tulostuspöydän "
"korkeus. Tulostuspöydän korkeus on oikea, kun suuttimen kärki juuri ja juuri "
"osuu paperiin."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:44 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:44
msgctxt "@action:button" msgctxt "@action:button"
msgid "Move to Next Position" msgid "Move to Next Position"
msgstr "" msgstr "Siirry seuraavaan positioon"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:66 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:66
msgctxt "@action:button" msgctxt "@action:button"
msgid "Skip Bedleveling" msgid "Skip Bedleveling"
msgstr "" msgstr "Ohita pöydän tasaus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:39 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:39
msgctxt "@label" msgctxt "@label"
@ -428,27 +426,28 @@ msgid ""
"To assist you in having better default settings for your Ultimaker. Cura " "To assist you in having better default settings for your Ultimaker. Cura "
"would like to know which upgrades you have in your machine:" "would like to know which upgrades you have in your machine:"
msgstr "" msgstr ""
"Saat paremmat oletusasetukset Ultimakeriin. Cura haluaisi tietää, mitä "
"päivityksiä laitteessasi on:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:49 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:49
#, fuzzy
msgctxt "@option:check" msgctxt "@option:check"
msgid "Extruder driver ugrades" msgid "Extruder driver ugrades"
msgstr "Suulakkeen lämpötila %1" msgstr "Suulakekäytön päivitykset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:54 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:54
msgctxt "@option:check" msgctxt "@option:check"
msgid "Heated printer bed (standard kit)" msgid "Heated printer bed (standard kit)"
msgstr "" msgstr "Lämmitetty tulostinpöytä (normaali sarja)"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:58 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:58
msgctxt "@option:check" msgctxt "@option:check"
msgid "Heated printer bed (self built)" msgid "Heated printer bed (self built)"
msgstr "" msgstr "Lämmitetty tulostinpöytä (itse rakennettu)"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:62 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:62
msgctxt "@option:check" msgctxt "@option:check"
msgid "Dual extrusion (experimental)" msgid "Dual extrusion (experimental)"
msgstr "" msgstr "Kaksoispursotus (kokeellinen)"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:70 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:70
msgctxt "@label" msgctxt "@label"
@ -458,6 +457,11 @@ msgid ""
"improve reliability. This upgrade can be bought from the Ultimaker webshop " "improve reliability. This upgrade can be bought from the Ultimaker webshop "
"or found on thingiverse as thing:26094" "or found on thingiverse as thing:26094"
msgstr "" msgstr ""
"Jos olet hankkinut Ultimakerin lokakuun 2012 jälkeen, sinulla on "
"suulakekäytön päivityspaketti (Extruder drive). Ellei sinulla ole tätä "
"päivitystä, sitä suositellaan kovasti luotettavuuden parantamiseksi. Tämä "
"päivityspaketti voidaan ostaa Ultimakerin verkkokaupasta tai se löytyy "
"thingiverse-sivustolta numerolla: 26094"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:44 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:44
msgctxt "@label" msgctxt "@label"
@ -465,36 +469,38 @@ msgid ""
"It's a good idea to do a few sanity checks on your Ultimaker. You can skip " "It's a good idea to do a few sanity checks on your Ultimaker. You can skip "
"this step if you know your machine is functional" "this step if you know your machine is functional"
msgstr "" msgstr ""
"Ultimakerille on hyvä tehdä muutamia toimintatarkastuksia. Voit jättää tämän "
"vaiheen väliin, jos tiedät laitteesi olevan toimintakunnossa"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:49 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:49
msgctxt "@action:button" msgctxt "@action:button"
msgid "Start Printer Check" msgid "Start Printer Check"
msgstr "" msgstr "Aloita tulostintarkistus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:56 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:56
msgctxt "@action:button" msgctxt "@action:button"
msgid "Skip Printer Check" msgid "Skip Printer Check"
msgstr "" msgstr "Ohita tulostintarkistus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:65 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:65
msgctxt "@label" msgctxt "@label"
msgid "Connection: " msgid "Connection: "
msgstr "" msgstr "Yhteys:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69
msgctxt "@info:status" msgctxt "@info:status"
msgid "Done" msgid "Done"
msgstr "" msgstr "Valmis"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69
msgctxt "@info:status" msgctxt "@info:status"
msgid "Incomplete" msgid "Incomplete"
msgstr "" msgstr "Kesken"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:76 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:76
msgctxt "@label" msgctxt "@label"
msgid "Min endstop X: " msgid "Min endstop X: "
msgstr "" msgstr "Min. päätyraja X:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:80 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:80
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:91 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:91
@ -503,7 +509,7 @@ msgstr ""
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:201 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:201
msgctxt "@info:status" msgctxt "@info:status"
msgid "Works" msgid "Works"
msgstr "" msgstr "Toimii"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:80 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:80
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:91 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:91
@ -512,40 +518,39 @@ msgstr ""
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:163 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:163
msgctxt "@info:status" msgctxt "@info:status"
msgid "Not checked" msgid "Not checked"
msgstr "" msgstr "Ei tarkistettu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:87 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:87
msgctxt "@label" msgctxt "@label"
msgid "Min endstop Y: " msgid "Min endstop Y: "
msgstr "" msgstr "Min. päätyraja Y:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:99 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:99
msgctxt "@label" msgctxt "@label"
msgid "Min endstop Z: " msgid "Min endstop Z: "
msgstr "" msgstr "Min. päätyraja Z:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:111 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:111
msgctxt "@label" msgctxt "@label"
msgid "Nozzle temperature check: " msgid "Nozzle temperature check: "
msgstr "" msgstr "Suuttimen lämpötilatarkistus:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:119 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:119
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:149 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:149
msgctxt "@action:button" msgctxt "@action:button"
msgid "Start Heating" msgid "Start Heating"
msgstr "" msgstr "Aloita lämmitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:124 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:124
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:154 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:154
msgctxt "@info:progress" msgctxt "@info:progress"
msgid "Checking" msgid "Checking"
msgstr "" msgstr "Tarkistetaan"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:141 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:141
#, fuzzy
msgctxt "@label" msgctxt "@label"
msgid "bed temperature check:" msgid "bed temperature check:"
msgstr "Pöydän lämpötila %1" msgstr "Pöydän lämpötilan tarkistus:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:38 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:38
msgctxt "@label" msgctxt "@label"
@ -554,6 +559,9 @@ msgid ""
"firmware controls the step motors, regulates the temperature and ultimately " "firmware controls the step motors, regulates the temperature and ultimately "
"makes your printer work." "makes your printer work."
msgstr "" msgstr ""
"Laiteohjelmisto on suoraan 3D-tulostimessa toimiva ohjelma. Laiteohjelmisto "
"ohjaa askelmoottoreita, säätää lämpötilaa ja loppujen lopuksi saa tulostimen "
"toimimaan."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:45 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:45
msgctxt "@label" msgctxt "@label"
@ -561,6 +569,8 @@ msgid ""
"The firmware shipping with new Ultimakers works, but upgrades have been made " "The firmware shipping with new Ultimakers works, but upgrades have been made "
"to make better prints, and make calibration easier." "to make better prints, and make calibration easier."
msgstr "" msgstr ""
"Uusien Ultimakerien mukana toimitettu laiteohjelmisto toimii, mutta "
"päivityksillä saadaan parempia tulosteita ja kalibrointi helpottuu."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:52 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:52
msgctxt "@label" msgctxt "@label"
@ -568,37 +578,36 @@ msgid ""
"Cura requires these new features and thus your firmware will most likely " "Cura requires these new features and thus your firmware will most likely "
"need to be upgraded. You can do so now." "need to be upgraded. You can do so now."
msgstr "" msgstr ""
"Cura tarvitsee näitä uusia ominaisuuksia ja siten laiteohjelmisto on "
"todennäköisesti päivitettävä. Voit tehdä sen nyt."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:55 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:55
#, fuzzy
msgctxt "@action:button" msgctxt "@action:button"
msgid "Upgrade to Marlin Firmware" msgid "Upgrade to Marlin Firmware"
msgstr "Päivitä laiteohjelmisto" msgstr "Päivitä Marlin-laiteohjelmistoon"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:58 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UpgradeFirmware.qml:58
msgctxt "@action:button" msgctxt "@action:button"
msgid "Skip Upgrade" msgid "Skip Upgrade"
msgstr "" msgstr "Ohita päivitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:15 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:15
#, fuzzy
msgctxt "@title:window" msgctxt "@title:window"
msgid "About Cura" msgid "About Cura"
msgstr "Tietoja Curasta" msgstr "Tietoja Curasta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:54 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:54
#, fuzzy
msgctxt "@label" msgctxt "@label"
msgid "End-to-end solution for fused filament 3D printing." msgid "End-to-end solution for fused filament 3D printing."
msgstr "Kokonaisvaltainen sulatettavan tulostuslangan 3D-tulostusratkaisu." msgstr "Kokonaisvaltainen sulatettavan tulostuslangan 3D-tulostusratkaisu."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:66 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:66
#, fuzzy
msgctxt "@info:credit" msgctxt "@info:credit"
msgid "" msgid ""
"Cura has been developed by Ultimaker B.V. in cooperation with the community." "Cura has been developed by Ultimaker B.V. in cooperation with the community."
msgstr "" msgstr ""
"Cura-ohjelman on kehittänyt Ultimaker B.V. yhteistyössä yhteisön kanssa." "Cura-ohjelman on kehittänyt Ultimaker B.V. yhteistyössä käyttäjäyhteisön "
"kanssa."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:16 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:16
#, fuzzy #, fuzzy
@ -618,11 +627,13 @@ msgid ""
"Highlight unsupported areas of the model in red. Without support these areas " "Highlight unsupported areas of the model in red. Without support these areas "
"will nog print properly." "will nog print properly."
msgstr "" msgstr ""
"Korostaa mallin vailla tukea olevat alueet punaisella. Ilman tukea nämä "
"alueet eivät tulostu kunnolla."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:74 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:74
msgctxt "@action:button" msgctxt "@action:button"
msgid "Center camera when item is selected" msgid "Center camera when item is selected"
msgstr "" msgstr "Keskitä kamera kun kohde on valittu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:78 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ViewPage.qml:78
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
@ -630,11 +641,13 @@ msgid ""
"Moves the camera so the object is in the center of the view when an object " "Moves the camera so the object is in the center of the view when an object "
"is selected" "is selected"
msgstr "" msgstr ""
"Siirtää kameraa siten, että kappale on näkymän keskellä, kun kappale on "
"valittu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:51 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:51
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen" msgid "Toggle Fu&ll Screen"
msgstr "" msgstr "Vaihda &koko näyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:58 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:58
#, fuzzy #, fuzzy
@ -655,10 +668,9 @@ msgid "&Quit"
msgstr "&Lopeta" msgstr "&Lopeta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:82 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:82
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Preferences..." msgid "&Preferences..."
msgstr "&Suosikkiasetukset..." msgstr "&Lisäasetukset..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:89 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:89
#, fuzzy #, fuzzy
@ -667,15 +679,14 @@ msgid "&Add Printer..."
msgstr "L&isää tulostin..." msgstr "L&isää tulostin..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:95 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:95
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Manage Pr&inters..." msgid "Manage Pr&inters..."
msgstr "L&isää tulostin..." msgstr "Tulostinten &hallinta..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:102 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:102
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Manage Profiles..." msgid "Manage Profiles..."
msgstr "" msgstr "Profiilien hallinta..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:109 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:109
#, fuzzy #, fuzzy
@ -696,74 +707,64 @@ msgid "&About..."
msgstr "Ti&etoja..." msgstr "Ti&etoja..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:130 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:130
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Delete &Selection" msgid "Delete &Selection"
msgstr "Poista valinta" msgstr "&Poista valinta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:138 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:138
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Delete Object" msgid "Delete Object"
msgstr "Poista kohde" msgstr "Poista kappale"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:146 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:146
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Ce&nter Object on Platform" msgid "Ce&nter Object on Platform"
msgstr "Keskitä kohde alustalle" msgstr "K&eskitä kappale alustalle"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:152 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:152
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Group Objects" msgid "&Group Objects"
msgstr "" msgstr "&Ryhmitä kappaleet"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:160 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:160
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Ungroup Objects" msgid "Ungroup Objects"
msgstr "" msgstr "Pura kappaleiden ryhmitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:168 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:168
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Merge Objects" msgid "&Merge Objects"
msgstr "Poista kohde" msgstr "&Yhdistä kappaleet"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:176 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:176
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Duplicate Object" msgid "&Duplicate Object"
msgstr "Monista kohde" msgstr "&Monista kappale"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:183 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:183
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Clear Build Platform" msgid "&Clear Build Platform"
msgstr "Tyhjennä alusta" msgstr "&Tyhjennä alusta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:190 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:190
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Re&load All Objects" msgid "Re&load All Objects"
msgstr "Lataa kaikki kohteet uudelleen" msgstr "&Lataa kaikki kappaleet uudelleen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:197 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:197
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Reset All Object Positions" msgid "Reset All Object Positions"
msgstr "Nollaa kaikki kohteiden sijainnit" msgstr "Nollaa kaikkien kappaleiden sijainnit"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:203 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:203
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "Reset All Object &Transformations" msgid "Reset All Object &Transformations"
msgstr "Nollaa kaikkien kohteiden muunnokset" msgstr "Nollaa kaikkien kappaleiden m&uunnokset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:209 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:209
#, fuzzy
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Open File..." msgid "&Open File..."
msgstr "Avaa tiedosto" msgstr "&Avaa tiedosto..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:217 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:217
#, fuzzy #, fuzzy
@ -775,84 +776,87 @@ msgstr "Näytä moottorin l&oki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:461 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:461
msgctxt "@title:tab" msgctxt "@title:tab"
msgid "General" msgid "General"
msgstr "" msgstr "Yleiset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:36 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:36
msgctxt "@label" msgctxt "@label"
msgid "Language" msgid "Language"
msgstr "" msgstr "Kieli"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:47 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:47
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr "bulgaria"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:48 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:48
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Czech" msgid "Czech"
msgstr "" msgstr "tsekki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:49 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:49
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "English" msgid "English"
msgstr "" msgstr "englanti"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:50 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:50
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr "suomi"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:51 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:51
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "French" msgid "French"
msgstr "" msgstr "ranska"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:52 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:52
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "German" msgid "German"
msgstr "" msgstr "saksa"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:53 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:53
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Italian" msgid "Italian"
msgstr "" msgstr "italia"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:54 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:54
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Polish" msgid "Polish"
msgstr "" msgstr "puola"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:55 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:55
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Russian" msgid "Russian"
msgstr "" msgstr "venäjä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:56 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:56
msgctxt "@item:inlistbox" msgctxt "@item:inlistbox"
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr "espanja"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:98 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:98
msgctxt "@label" msgctxt "@label"
msgid "" msgid ""
"You will need to restart the application for language changes to have effect." "You will need to restart the application for language changes to have effect."
msgstr "" msgstr ""
"Sovellus on käynnistettävä uudelleen, jotta kielimuutokset tulevat voimaan."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:114 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:114
msgctxt "@option:check" msgctxt "@option:check"
msgid "Ensure objects are kept apart" msgid "Ensure objects are kept apart"
msgstr "" msgstr "Pidä kappaleet erillään"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:118 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:118
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
msgid "" msgid ""
"Should objects on the platform be moved so that they no longer intersect." "Should objects on the platform be moved so that they no longer intersect."
msgstr "" msgstr ""
"Pitäisikö kappaleita alustalla siirtää niin, etteivät ne enää leikkaa "
"toisiaan?"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:147 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:147
msgctxt "@option:check" msgctxt "@option:check"
msgid "Send (Anonymous) Print Information" msgid "Send (Anonymous) Print Information"
msgstr "" msgstr "Lähetä (anonyymit) tulostustiedot"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:151 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:151
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
@ -861,103 +865,102 @@ msgid ""
"models, IP addresses or other personally identifiable information is sent or " "models, IP addresses or other personally identifiable information is sent or "
"stored." "stored."
msgstr "" msgstr ""
"Pitäisikö anonyymejä tietoja tulosteesta lähettää Ultimakerille? Huomaa, "
"että malleja, IP-osoitteita tai muita henkilökohtaisia tietoja ei lähetetä "
"eikä tallenneta."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:179 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:179
msgctxt "@option:check" msgctxt "@option:check"
msgid "Scale Too Large Files" msgid "Scale Too Large Files"
msgstr "" msgstr "Skaalaa liian isot tiedostot"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:183 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:183
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
msgid "" msgid ""
"Should opened files be scaled to the build volume when they are too large?" "Should opened files be scaled to the build volume when they are too large?"
msgstr "" msgstr ""
"Pitäisikö avoimia tiedostoja skaalata rakennustilavuuteen, jos ne ovat liian "
"isoja?"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:70 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:70
#, fuzzy
msgctxt "@label:textbox" msgctxt "@label:textbox"
msgid "Printjob Name" msgid "Printjob Name"
msgstr "Tulostimen nimi:" msgstr "Tulostustyön nimi"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:167 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:167
msgctxt "@label %1 is length of filament" msgctxt "@label %1 is length of filament"
msgid "%1 m" msgid "%1 m"
msgstr "" msgstr "%1 m"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:229 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:229
msgctxt "@info:tooltip" msgctxt "@info:tooltip"
msgid "Select the active output device" msgid "Select the active output device"
msgstr "" msgstr "Valitse aktiivinen oheislaite"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:34 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:34
msgctxt "@label" msgctxt "@label"
msgid "Infill:" msgid "Infill:"
msgstr "" msgstr "Täyttö:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:119 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:119
msgctxt "@label" msgctxt "@label"
msgid "Sparse" msgid "Sparse"
msgstr "" msgstr "Harva"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:121 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:121
msgctxt "@label" msgctxt "@label"
msgid "Sparse (20%) infill will give your model an average strength" msgid "Sparse (20%) infill will give your model an average strength"
msgstr "" msgstr "Harva (20 %) täyttö antaa mallille keskimääräistä lujuutta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:125 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:125
msgctxt "@label" msgctxt "@label"
msgid "Dense" msgid "Dense"
msgstr "" msgstr "Tiheä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:127 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:127
msgctxt "@label" msgctxt "@label"
msgid "Dense (50%) infill will give your model an above average strength" msgid "Dense (50%) infill will give your model an above average strength"
msgstr "" msgstr "Tiheä (50 %) täyttö antaa mallille keskimääräistä paremman lujuuden"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:131 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:131
msgctxt "@label" msgctxt "@label"
msgid "Solid" msgid "Solid"
msgstr "" msgstr "Kiinteä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:133 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:133
msgctxt "@label" msgctxt "@label"
msgid "Solid (100%) infill will make your model completely solid" msgid "Solid (100%) infill will make your model completely solid"
msgstr "" msgstr "Kiinteä (100 %) täyttö tekee mallista täysin umpinaisen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:151 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:151
#, fuzzy
msgctxt "@label:listbox" msgctxt "@label:listbox"
msgid "Helpers:" msgid "Helpers:"
msgstr "&Ohje" msgstr "Avustimet:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:169 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:169
msgctxt "@option:check" msgctxt "@option:check"
msgid "Enable Skirt Adhesion" msgid "Enable Skirt Adhesion"
msgstr "" msgstr "Ota helman tarttuvuus käyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:187 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:187
#, fuzzy
msgctxt "@option:check" msgctxt "@option:check"
msgid "Enable Support" msgid "Enable Support"
msgstr "Ota tuki käyttöön" msgstr "Ota tuki käyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:123 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:123
#, fuzzy
msgctxt "@title:tab" msgctxt "@title:tab"
msgid "Simple" msgid "Simple"
msgstr "Perusasetukset" msgstr "Suppea"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:124 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:124
#, fuzzy
msgctxt "@title:tab" msgctxt "@title:tab"
msgid "Advanced" msgid "Advanced"
msgstr "Lisäasetukset" msgstr "Laajennettu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:30 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:30
#, fuzzy
msgctxt "@label:listbox" msgctxt "@label:listbox"
msgid "Print Setup" msgid "Print Setup"
msgstr "Tulostimen asennus" msgstr "Tulostusasetukset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:100 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:100
#, fuzzy #, fuzzy
@ -980,18 +983,17 @@ msgstr "&Tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:43 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:43
msgctxt "@title:menu" msgctxt "@title:menu"
msgid "Open &Recent" msgid "Open &Recent"
msgstr "" msgstr "Avaa &viimeisin"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:72 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:72
msgctxt "@action:inmenu" msgctxt "@action:inmenu"
msgid "&Save Selection to File" msgid "&Save Selection to File"
msgstr "" msgstr "&Tallenna valinta tiedostoon"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:80 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:80
#, fuzzy
msgctxt "@title:menu" msgctxt "@title:menu"
msgid "Save &All" msgid "Save &All"
msgstr "Tallenna tiedosto" msgstr "Tallenna &kaikki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:108 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:108
#, fuzzy #, fuzzy
@ -1000,10 +1002,9 @@ msgid "&Edit"
msgstr "&Muokkaa" msgstr "&Muokkaa"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:125 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:125
#, fuzzy
msgctxt "@title:menu" msgctxt "@title:menu"
msgid "&View" msgid "&View"
msgstr "Näytä" msgstr "&Näytä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:151 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:151
#, fuzzy #, fuzzy
@ -1014,7 +1015,7 @@ msgstr "&Laite"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:197 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:197
msgctxt "@title:menu" msgctxt "@title:menu"
msgid "&Profile" msgid "&Profile"
msgstr "" msgstr "&Profiili"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:224 #: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:224
#, fuzzy #, fuzzy

1771
resources/i18n/fi/fdmprinter.json.po Normal file → Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -2,13 +2,13 @@
"id": "rigidbotbig", "id": "rigidbotbig",
"version": 1, "version": 1,
"name": "RigidBot", "name": "RigidBot",
"manufacturer": "Invent-A-Part", "manufacturer": "Other",
"author": "RBC", "author": "RBC",
"platform": "rigidbot_platform.stl", "platform": "rigidbot_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_width": { "default": 254 }, "machine_width": { "default": 254 },
"machine_depth": { "default": 254 }, "machine_depth": { "default": 254 },
@ -18,6 +18,8 @@
"machine_nozzle_size": { "default": 0.4, "machine_nozzle_size": { "default": 0.4,
"visible": true "visible": true
}, },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_x": { "default": 0 },
"machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 },
@ -26,14 +28,12 @@
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" }, "machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": { "machine_start_gcode": {
"default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." "default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}" "default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
} },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"shell_thickness": { "default": 0.8 }, "shell_thickness": { "default": 0.8 },
"wall_thickness": { "default": 0.8 }, "wall_thickness": { "default": 0.8 },

View File

@ -2,13 +2,13 @@
"id": "rigidbotbig", "id": "rigidbotbig",
"version": 1, "version": 1,
"name": "RigidBotBig", "name": "RigidBotBig",
"manufacturer": "Invent-A-Part", "manufacturer": "Other",
"author": "RBC", "author": "RBC",
"platform": "rigidbotbig_platform.stl", "platform": "rigidbotbig_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_width": { "default": 400 }, "machine_width": { "default": 400 },
"machine_depth": { "default": 300 }, "machine_depth": { "default": 300 },
@ -16,6 +16,8 @@
"machine_heated_bed": { "default": true }, "machine_heated_bed": { "default": true },
"machine_nozzle_size": { "default": 0.4}, "machine_nozzle_size": { "default": 0.4},
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_x": { "default": 0 },
"machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 },
@ -24,14 +26,12 @@
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" }, "machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": { "machine_start_gcode": {
"default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." "default": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..."
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}" "default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
} },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"shell_thickness": { "default": 0.8}, "shell_thickness": { "default": 0.8},
"wall_thickness": { "default": 0.8 }, "wall_thickness": { "default": 0.8 },

View File

@ -2,12 +2,12 @@
"id": "bq_hephestos", "id": "bq_hephestos",
"version": 1, "version": 1,
"name": "BQ Prusa i3 Hephestos", "name": "BQ Prusa i3 Hephestos",
"manufacturer": "BQ", "manufacturer": "Other",
"author": "BQ", "author": "BQ",
"platform": "hephestos_platform.stl", "platform": "bq_hephestos_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --"
}, },
@ -33,10 +33,8 @@
"default": "RepRap" "default": "RepRap"
}, },
"machine_platform_offset": { "machine_platform_offset": {
"default": [0, 100, 0] "default": [0, -82, 0]
} },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false }, "layer_height_0": { "default": 0.2, "visible": false },
"shell_thickness": { "default": 1.0 }, "shell_thickness": { "default": 1.0 },

View File

@ -0,0 +1,65 @@
{
"id": "bq_hephestos_2",
"version": 1,
"name": "BQ Hephestos 2",
"manufacturer": "Other",
"author": "BQ",
"platform": "bq_hephestos_2_platform.stl",
"inherits": "fdmprinter.json",
"overrides": {
"machine_start_gcode": {
"default": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --"
},
"machine_end_gcode": {
"default": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --"
},
"machine_width": {
"default": 210
},
"machine_depth": {
"default": 297
},
"machine_height": {
"default": 220
},
"machine_heated_bed": {
"default": false
},
"machine_center_is_zero": {
"default": false
},
"machine_gcode_flavor": {
"default": "RepRap"
},
"machine_platform_offset": {
"default": [-6, 1320, 0]
}
"material_print_temperature": { "default": 210.0, "visible": true },
"material_bed_temperature": { "default": 0 },
"material_diameter": { "default": 1.75 },
"layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": true },
"shell_thickness": { "default": 1.2 },
"wall_line_count": { "default": 3, "visible": false },
"wall_thickness": { "default": 1.2, "visible": false },
"top_bottom_thickness": { "default": 1.2, "visible": false },
"infill_sparse_density": { "default": 20.0 },
"infill_overlap": { "default": 15.0, "visible": false },
"speed_print": { "default": 60.0 },
"speed_travel": { "default": 160.0 },
"speed_layer_0": { "default": 30.0, "visible": true },
"speed_wall_x": { "default": 35.0, "visible": false },
"speed_wall_0": { "default": 30.0, "visible": false },
"speed_infill": { "default": 80.0, "visible": true },
"speed_topbottom": { "default": 35.0, "visible": false },
"skirt_speed": { "default": 35.0, "visible": false },
"retraction_amount": { "default": 2.0, "visible": false },
"retraction_speed": { "default": 45.0, "visible": false },
"skirt_line_count": { "default": 4 },
"skirt_minimal_length": { "default": 30.0, "visible": false },
"skirt_gap": { "default": 6.0 },
"cool_fan_full_at_height": { "default": 0.4, "visible": false },
"support_enable": { "default": false }
}
}

View File

@ -2,12 +2,12 @@
"id": "bq_hephestos_xl", "id": "bq_hephestos_xl",
"version": 1, "version": 1,
"name": "BQ Prusa i3 Hephestos XL", "name": "BQ Prusa i3 Hephestos XL",
"manufacturer": "BQ", "manufacturer": "Other",
"author": "BQ", "author": "BQ",
"platform": "hephestos_platform.stl", "platform": "bq_hephestos_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --"
}, },
@ -33,10 +33,8 @@
"default": "RepRap" "default": "RepRap"
}, },
"machine_platform_offset": { "machine_platform_offset": {
"default": [0, 100, 0] "default": [0, -82, 0]
} },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false }, "layer_height_0": { "default": 0.2, "visible": false },
"shell_thickness": { "default": 1.0 }, "shell_thickness": { "default": 1.0 },

View File

@ -2,12 +2,12 @@
"id": "bq_witbox", "id": "bq_witbox",
"version": 1, "version": 1,
"name": "BQ Witbox", "name": "BQ Witbox",
"manufacturer": "BQ", "manufacturer": "Other",
"author": "BQ", "author": "BQ",
"platform": "witbox_platform.stl", "platform": "bq_witbox_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --"
}, },
@ -34,9 +34,7 @@
}, },
"machine_platform_offset": { "machine_platform_offset": {
"default": [0, -145, -38] "default": [0, -145, -38]
} },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false }, "layer_height_0": { "default": 0.2, "visible": false },
"shell_thickness": { "default": 1.0 }, "shell_thickness": { "default": 1.0 },

View File

@ -0,0 +1,65 @@
{
"id": "bq_witbox_2",
"version": 1,
"name": "BQ Witbox 2",
"manufacturer": "Other",
"author": "BQ",
"platform": "bq_witbox_platform.stl",
"inherits": "fdmprinter.json",
"overrides": {
"machine_start_gcode": {
"default": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --"
},
"machine_end_gcode": {
"default": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --"
},
"machine_width": {
"default": 297
},
"machine_depth": {
"default": 210
},
"machine_height": {
"default": 200
},
"machine_heated_bed": {
"default": false
},
"machine_center_is_zero": {
"default": false
},
"machine_gcode_flavor": {
"default": "RepRap"
},
"machine_platform_offset": {
"default": [0, -145, -38]
},
"material_print_temperature": { "default": 210.0, "visible": true },
"material_bed_temperature": { "default": 0 },
"material_diameter": { "default": 1.75 },
"layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": true },
"shell_thickness": { "default": 1.2 },
"wall_line_count": { "default": 3, "visible": false },
"wall_thickness": { "default": 1.2, "visible": false },
"top_bottom_thickness": { "default": 1.2, "visible": false },
"infill_sparse_density": { "default": 20.0 },
"infill_overlap": { "default": 15.0, "visible": false },
"speed_print": { "default": 60.0 },
"speed_travel": { "default": 160.0 },
"speed_layer_0": { "default": 30.0, "visible": true },
"speed_wall_x": { "default": 35.0, "visible": false },
"speed_wall_0": { "default": 30.0, "visible": false },
"speed_infill": { "default": 80.0, "visible": true },
"speed_topbottom": { "default": 35.0, "visible": false },
"skirt_speed": { "default": 35.0, "visible": false },
"retraction_amount": { "default": 2.0, "visible": false },
"retraction_speed": { "default": 45.0, "visible": false },
"skirt_line_count": { "default": 4 },
"skirt_minimal_length": { "default": 30.0, "visible": false },
"skirt_gap": { "default": 6.0 },
"cool_fan_full_at_height": { "default": 0.4, "visible": false },
"support_enable": { "default": false }
}
}

View File

@ -0,0 +1,271 @@
{
"version": 1,
"id": "dual_extrusion",
"name": "Dual Extrusion Base File",
"inherits": "fdmprinter.json",
"visible": false,
"machine_extruder_trains": {
"0": {
"extruder_nr": { "default": 0 },
"machine_nozzle_offset_x": { "default": 0.0 },
"machine_nozzle_offset_y": { "default": 0.0 }
},
"1": {
"extruder_nr": { "default": 1 }
}
},
"machine_settings": {
"machine_use_extruder_offset_to_offset_coords": { "default": false },
"machine_nozzle_offset_x": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_nozzle_offset_y": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_extruder_start_code": { "default": "", "SEE_machine_extruder_trains": true },
"machine_extruder_start_pos_abs": { "default": false, "SEE_machine_extruder_trains": true },
"machine_extruder_start_pos_x": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_extruder_start_pos_y": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_extruder_end_pos_abs": { "default": false, "SEE_machine_extruder_trains": true },
"machine_extruder_end_pos_x": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_extruder_end_pos_y": { "default": 0, "SEE_machine_extruder_trains": true },
"machine_extruder_end_code": { "default": "", "SEE_machine_extruder_trains": true }
},
"overrides": {
"speed_print": {
"children": {
"speed_prime_tower": {
"label": "Prime Tower Speed",
"description": "The speed at which the prime tower is printed. Printing the prime tower slower can make it more stable when the adhesion between the different filaments is suboptimal.",
"unit": "mm/s",
"type": "float",
"min_value": "0.1",
"max_value_warning": "150",
"default": 60,
"visible": false,
"enabled": "prime_tower_enable"
}
}
},
"line_width": {
"children": {
"prime_tower_line_width": {
"label": "Prime Tower Line Width",
"description": "Width of a single prime tower line.",
"unit": "mm",
"min_value": "0.0001",
"min_value_warning": "0.2",
"max_value_warning": "5",
"default": 0.4,
"type": "float",
"visible": false,
"enabled": "prime_tower_enable"
}
}
}
},
"categories": {
"dual": {
"label": "Dual Extrusion",
"visible": true,
"icon": "category_dual",
"settings": {
"extruder_nr": {
"label": "Extruder",
"description": "The extruder train used for printing. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16",
"always_visible": true,
"children": {
"adhesion_extruder_nr": {
"label": "Platform Adhesion Extruder",
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16"
},
"support_extruder_nr": {
"label": "Support Extruder",
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16",
"children": {
"support_infill_extruder_nr": {
"label": "Support Infill Extruder",
"description": "The extruder train to use for printing the infill of the support. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16"
},
"support_extruder_nr_layer_0": {
"label": "First Layer Support Extruder",
"description": "The extruder train to use for printing the first layer of support infill. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16"
},
"support_roof_extruder_nr": {
"label": "Support Roof Extruder",
"description": "The extruder train to use for printing the roof of the support. This is used in multi-extrusion.",
"type": "int",
"default": 0,
"min_value": "0",
"max_value": "16",
"enabled": "support_roof_enable"
}
}
}
}
},
"prime_tower_enable": {
"label": "Enable Prime Tower",
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
"type": "boolean",
"visible": true,
"default": false
},
"prime_tower_size": {
"label": "Prime Tower Size",
"description": "The width of the prime tower.",
"visible": false,
"type": "float",
"unit": "mm",
"default": 15,
"min_value": "0",
"max_value_warning": "20",
"inherit_function": "15 if prime_tower_enable else 0",
"enabled": "prime_tower_enable"
},
"prime_tower_position_x": {
"label": "Prime Tower X Position",
"description": "The x position of the prime tower.",
"visible": false,
"type": "float",
"unit": "mm",
"default": 200,
"enabled": "prime_tower_enable"
},
"prime_tower_position_y": {
"label": "Prime Tower Y Position",
"description": "The y position of the prime tower.",
"visible": false,
"type": "float",
"unit": "mm",
"default": 200,
"enabled": "prime_tower_enable"
},
"prime_tower_flow": {
"label": "Prime Tower Flow",
"description": "Flow compensation: the amount of material extruded is multiplied by this value.",
"visible": false,
"unit": "%",
"default": 100,
"type": "float",
"min_value": "5",
"min_value_warning": "50",
"max_value_warning": "150",
"enabled": "prime_tower_enable"
},
"prime_tower_wipe_enabled": {
"label": "Wipe Nozzle on Prime tower",
"description": "After printing the prime tower with the one nozzle, wipe the oozed material from the other nozzle off on the prime tower.",
"type": "boolean",
"default": false,
"enabled": "prime_tower_enable"
},
"multiple_mesh_overlap": {
"label": "Dual Extrusion Overlap",
"description": "Make the objects printed with different extruder trains overlap a bit. This makes the different materials bond together better.",
"visible": false,
"type": "float",
"unit": "mm",
"default": 0.15,
"min_value": "0",
"max_value_warning": "1.0"
},
"ooze_shield_enabled": {
"label": "Enable Ooze Shield",
"description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
"type": "boolean",
"default": false
},
"ooze_shield_angle": {
"label": "Ooze Shield Angle",
"description": "The maximum angle a part in the ooze shield will have. With 0 degrees being vertical, and 90 degrees being horizontal. A smaller angle leads to less failed ooze shields, but more material.",
"unit": "°",
"type": "float",
"min_value": "0",
"max_value": "90",
"default": 60,
"visible": false,
"enabled": "ooze_shield_enabled"
},
"ooze_shield_dist": {
"label": "Ooze Shields Distance",
"description": "Distance of the ooze shield from the print, in the X/Y directions.",
"unit": "mm",
"type": "float",
"min_value": "0",
"max_value_warning": "30",
"default": 2,
"visible": false,
"enabled": "ooze_shield_enabled"
}
}
},
"material": {
"settings": {
"switch_extruder_retraction_amount": {
"label": "Nozzle Switch Retraction Distance",
"description": "The amount of retraction: Set at 0 for no retraction at all. This should generally be the same as the length of the heat zone.",
"unit": "mm",
"type": "float",
"default": 16,
"visible": false,
"inherit_function": "machine_heat_zone_length",
"enabled": "retraction_enable"
},
"switch_extruder_retraction_speeds": {
"label": "Nozzle Switch Retraction Speed",
"description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.",
"unit": "mm/s",
"type": "float",
"default": 20,
"visible": false,
"inherit": false,
"enabled": "retraction_enable",
"children": {
"switch_extruder_retraction_speed": {
"label": "Nozzle Switch Retract Speed",
"description": "The speed at which the filament is retracted during a nozzle switch retract. ",
"unit": "mm/s",
"type": "float",
"default": 20,
"visible": false,
"enabled": "retraction_enable"
},
"switch_extruder_prime_speed": {
"label": "Nozzle Switch Prime Speed",
"description": "The speed at which the filament is pushed back after a nozzle switch retraction.",
"unit": "mm/s",
"type": "float",
"default": 20,
"visible": false,
"enabled": "retraction_enable"
}
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,19 +3,21 @@
"version": 1, "version": 1,
"name": "German RepRap Neo", "name": "German RepRap Neo",
"manufacturer": "Other", "manufacturer": "Other",
"author": "other", "author": "Other",
"icon": "icon_ultimaker.png", "icon": "icon_ultimaker.png",
"platform": "grr_neo_platform.stl", "platform": "grr_neo_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"visible": "true", "visible": "true",
"machine_settings": { "overrides": {
"machine_width": { "default": 150 }, "machine_width": { "default": 150 },
"machine_height": { "default": 150 }, "machine_height": { "default": 150 },
"machine_depth": { "default": 150 }, "machine_depth": { "default": 150 },
"machine_center_is_zero": { "default": false }, "machine_center_is_zero": { "default": false },
"machine_nozzle_size": { "default": 0.5 }, "machine_nozzle_size": { "default": 0.5 },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_shape_min_x": { "default": 75 }, "machine_head_shape_min_x": { "default": 75 },
"machine_head_shape_min_y": { "default": 18 }, "machine_head_shape_min_y": { "default": 18 },
"machine_head_shape_max_x": { "default": 18 }, "machine_head_shape_max_x": { "default": 18 },
@ -28,10 +30,8 @@
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" "default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
} },
},
"overrides": {
"material_bed_temperature": { "visible": false } "material_bed_temperature": { "visible": false }
} }
} }

View File

@ -9,7 +9,7 @@
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_width": { "default": 210 }, "machine_width": { "default": 210 },
"machine_depth": { "default": 185 }, "machine_depth": { "default": 185 },
"machine_height": { "default": 200 }, "machine_height": { "default": 200 },
@ -17,6 +17,8 @@
"machine_center_is_zero": { "default": false }, "machine_center_is_zero": { "default": false },
"machine_nozzle_size": { "default": 0.4 }, "machine_nozzle_size": { "default": 0.4 },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_x": { "default": 0 },
"machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 },
@ -28,10 +30,8 @@
"machine_nozzle_tip_outer_diameter": { "default": 1.0 }, "machine_nozzle_tip_outer_diameter": { "default": 1.0 },
"machine_nozzle_head_distance": { "default": 3.0 }, "machine_nozzle_head_distance": { "default": 3.0 },
"machine_nozzle_expansion_angle": { "default": 45 } "machine_nozzle_expansion_angle": { "default": 45 },
},
"overrides": {
"layer_height": { "default": 0.2 }, "layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false }, "layer_height_0": { "default": 0.2, "visible": false },
"wall_line_count": { "default": 2, "visible": true }, "wall_line_count": { "default": 2, "visible": true },

View File

@ -2,19 +2,22 @@
"id": "prusa_i3", "id": "prusa_i3",
"version": 1, "version": 1,
"name": "Prusa i3", "name": "Prusa i3",
"manufacturer": "Prusa", "manufacturer": "Other",
"author": "other", "author": "Other",
"icon": "icon_ultimaker2.png", "icon": "icon_ultimaker2.png",
"platform": "prusai3_platform.stl", "platform": "prusai3_platform.stl",
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_settings": { "overrides": {
"machine_heated_bed": { "default": true },
"machine_width": { "default": 200 }, "machine_width": { "default": 200 },
"machine_height": { "default": 200 }, "machine_height": { "default": 200 },
"machine_depth": { "default": 200 }, "machine_depth": { "default": 200 },
"machine_center_is_zero": { "default": false }, "machine_center_is_zero": { "default": false },
"machine_nozzle_size": { "default": 0.4 }, "machine_nozzle_size": { "default": 0.4 },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_shape_min_x": { "default": 75 }, "machine_head_shape_min_x": { "default": 75 },
"machine_head_shape_min_y": { "default": 18 }, "machine_head_shape_min_y": { "default": 18 },
"machine_head_shape_max_x": { "default": 18 }, "machine_head_shape_max_x": { "default": 18 },
@ -28,9 +31,5 @@
"machine_end_gcode": { "machine_end_gcode": {
"default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" "default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
} }
},
"overrides": {
"material_bed_temperature": { "visible": true }
} }
} }

View File

@ -11,10 +11,13 @@
"inherits": "fdmprinter.json", "inherits": "fdmprinter.json",
"machine_extruder_trains": [ "machine_extruder_trains": {
{ "0": {
"machine_nozzle_size": { "machine_nozzle_heat_up_speed": {
"default": 0.4 "default": 2.0
},
"machine_nozzle_cool_down_speed": {
"default": 2.0
}, },
"machine_nozzle_tip_outer_diameter": { "machine_nozzle_tip_outer_diameter": {
"default": 1 "default": 1
@ -29,8 +32,8 @@
"default": 16 "default": 16
} }
} }
], },
"machine_settings": { "overrides": {
"machine_start_gcode" : { "default": "" }, "machine_start_gcode" : { "default": "" },
"machine_end_gcode" : { "default": "" }, "machine_end_gcode" : { "default": "" },
"machine_width": { "default": 230 }, "machine_width": { "default": 230 },
@ -42,25 +45,27 @@
{ {
"default": [ "default": [
[ [
-40, -42,
30 12
], ],
[ [
-40, -42,
-10 -32
], ],
[ [
60, 62,
-10 12
], ],
[ [
60, 62,
30 -32
] ]
] ]
}, },
"machine_center_is_zero": { "default": false }, "machine_center_is_zero": { "default": false },
"machine_nozzle_size": { "default": 0.4 }, "machine_nozzle_size": { "default": 0.4, "description": "The inner diameter of the hole in the nozzle. If you are using an Olsson Block with a non-standard nozzle then set this field to the nozzle size you are using." },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"gantry_height": { "default": 55 }, "gantry_height": { "default": 55 },
"machine_use_extruder_offset_to_offset_coords": { "default": true }, "machine_use_extruder_offset_to_offset_coords": { "default": true },
"machine_gcode_flavor": { "default": "UltiGCode" }, "machine_gcode_flavor": { "default": "UltiGCode" },
@ -74,10 +79,8 @@
"machine_nozzle_tip_outer_diameter": { "default": 1.0 }, "machine_nozzle_tip_outer_diameter": { "default": 1.0 },
"machine_nozzle_head_distance": { "default": 3.0 }, "machine_nozzle_head_distance": { "default": 3.0 },
"machine_nozzle_expansion_angle": { "default": 45 } "machine_nozzle_expansion_angle": { "default": 45 },
},
"overrides": {
"material_print_temperature": { "enabled": "False" }, "material_print_temperature": { "enabled": "False" },
"material_bed_temperature": { "enabled": "False" }, "material_bed_temperature": { "enabled": "False" },
"material_diameter": { "enabled": "False" }, "material_diameter": { "enabled": "False" },

View File

@ -10,7 +10,7 @@
"inherits": "ultimaker2.json", "inherits": "ultimaker2.json",
"machine_settings": { "overrides": {
"machine_width": { "default": 230 }, "machine_width": { "default": 230 },
"machine_depth": { "default": 225 }, "machine_depth": { "default": 225 },
"machine_height": { "default": 315 } "machine_height": { "default": 315 }

View File

@ -10,7 +10,7 @@
"inherits": "ultimaker2.json", "inherits": "ultimaker2.json",
"machine_settings": { "overrides": {
"machine_width": { "default": 120 }, "machine_width": { "default": 120 },
"machine_depth": { "default": 120 }, "machine_depth": { "default": 120 },
"machine_height": { "default": 115 }, "machine_height": { "default": 115 },

View File

@ -16,10 +16,13 @@
"BedLeveling" "BedLeveling"
], ],
"machine_extruder_trains": [ "machine_extruder_trains": {
{ "0": {
"machine_nozzle_size": { "machine_nozzle_heat_up_speed": {
"default": 0.4 "default": 2.0
},
"machine_nozzle_cool_down_speed": {
"default": 2.0
}, },
"machine_nozzle_tip_outer_diameter": { "machine_nozzle_tip_outer_diameter": {
"default": 1 "default": 1
@ -34,13 +37,15 @@
"default": 16 "default": 16
} }
} }
], },
"machine_settings": { "overrides": {
"machine_width": { "default": 205 }, "machine_width": { "default": 205 },
"machine_height": { "default": 200 }, "machine_height": { "default": 200 },
"machine_depth": { "default": 205 }, "machine_depth": { "default": 205 },
"machine_center_is_zero": { "default": false }, "machine_center_is_zero": { "default": false },
"machine_nozzle_size": { "default": 0.4 }, "machine_nozzle_size": { "default": 0.4 },
"machine_nozzle_heat_up_speed": { "default": 2.0 },
"machine_nozzle_cool_down_speed": { "default": 2.0 },
"machine_head_with_fans_polygon": "machine_head_with_fans_polygon":
{ {
"default": [ "default": [
@ -71,10 +76,8 @@
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" "default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
} },
},
"overrides": { "machine_extruder_drive_upgrade": { "default": false }
"material_bed_temperature": { "visible": false }
} }
} }

View File

@ -16,7 +16,7 @@
"BedLeveling" "BedLeveling"
], ],
"machine_settings": { "overrides": {
"machine_heated_bed": { "default": true } "machine_heated_bed": { "default": true }
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,3 +4,5 @@ name = High Quality
[settings] [settings]
layer_height = 0.06 layer_height = 0.06
infill_sparse_density = 12

View File

@ -0,0 +1,15 @@
[general]
version = 1
name = Low Quality
[settings]
layer_height = 0.15
shell_thickness = 0.8
infill_sparse_density = 8
speed_print = 60
speed_wall_0 = 40
speed_wall_x = 50
speed_topbottom = 30
speed_travel = 150
speed_layer_0 = 30
skirt_speed = 30

View File

@ -3,4 +3,3 @@ version = 1
name = Normal Quality name = Normal Quality
[settings] [settings]
layer_height = 0.1

View File

@ -0,0 +1,9 @@
[general]
version = 1
name = Ulti Quality
[settings]
layer_height = 0.04
shell_thickness = 1.6
top_bottom_thickness = 0.8
infill_sparse_density = 14

View File

@ -47,7 +47,6 @@ Item
Action Action
{ {
id:toggleFullScreenAction id:toggleFullScreenAction
shortcut: StandardKey.FullScreen;
text: catalog.i18nc("@action:inmenu","Toggle Fu&ll Screen"); text: catalog.i18nc("@action:inmenu","Toggle Fu&ll Screen");
iconName: "view-fullscreen"; iconName: "view-fullscreen";
} }
@ -137,7 +136,6 @@ Item
id: deleteObjectAction; id: deleteObjectAction;
text: catalog.i18nc("@action:inmenu","Delete Object"); text: catalog.i18nc("@action:inmenu","Delete Object");
iconName: "edit-delete"; iconName: "edit-delete";
shortcut: StandardKey.Backspace;
} }
Action Action
@ -182,6 +180,7 @@ Item
id: deleteAllAction; id: deleteAllAction;
text: catalog.i18nc("@action:inmenu","&Clear Build Platform"); text: catalog.i18nc("@action:inmenu","&Clear Build Platform");
iconName: "edit-delete"; iconName: "edit-delete";
shortcut: "Ctrl+D";
} }
Action Action
@ -216,5 +215,6 @@ Item
id: showEngineLogAction; id: showEngineLogAction;
text: catalog.i18nc("@action:inmenu","Show Engine &Log..."); text: catalog.i18nc("@action:inmenu","Show Engine &Log...");
iconName: "view-list-text"; iconName: "view-list-text";
shortcut: StandardKey.WhatsThis;
} }
} }

View File

@ -22,6 +22,19 @@ UM.MainWindow
id: backgroundItem; id: backgroundItem;
anchors.fill: parent; anchors.fill: parent;
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name:"cura"}
//DeleteSelection on the keypress backspace event
Keys.onPressed: {
if (event.key == Qt.Key_Backspace)
{
if(objectContextMenu.objectId != 0)
{
Printer.deleteObject(objectContextMenu.objectId);
}
}
}
UM.ApplicationMenu UM.ApplicationMenu
{ {
id: menu id: menu
@ -72,7 +85,7 @@ UM.MainWindow
text: catalog.i18nc("@action:inmenu", "&Save Selection to File"); text: catalog.i18nc("@action:inmenu", "&Save Selection to File");
enabled: UM.Selection.hasSelection; enabled: UM.Selection.hasSelection;
iconName: "document-save-as"; iconName: "document-save-as";
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file"); onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", Printer.jobName);
} }
Menu Menu
{ {
@ -88,7 +101,7 @@ UM.MainWindow
MenuItem MenuItem
{ {
text: model.description; text: model.description;
onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id); onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, Printer.jobName);
} }
onObjectAdded: saveAllMenu.insertItem(index, object) onObjectAdded: saveAllMenu.insertItem(index, object)
onObjectRemoved: saveAllMenu.removeItem(object) onObjectRemoved: saveAllMenu.removeItem(object)
@ -139,10 +152,6 @@ UM.MainWindow
onObjectRemoved: top_view_menu.removeItem(object) onObjectRemoved: top_view_menu.removeItem(object)
} }
ExclusiveGroup { id: view_menu_top_group; } ExclusiveGroup { id: view_menu_top_group; }
MenuSeparator { }
MenuItem { action: actions.toggleFullScreen; }
} }
Menu Menu
{ {
@ -301,14 +310,24 @@ UM.MainWindow
} }
} }
JobSpecs
{
anchors
{
bottom: parent.bottom;
right: sidebar.left;
bottomMargin: UM.Theme.sizes.default_margin.height;
rightMargin: UM.Theme.sizes.default_margin.width;
}
}
UM.MessageStack UM.MessageStack
{ {
anchors anchors
{ {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: -(UM.Theme.sizes.logo.width/ 2) horizontalCenterOffset: -(UM.Theme.sizes.sidebar.width/ 2)
top: parent.verticalCenter; verticalCenter: parent.verticalCenter;
bottom: parent.bottom;
} }
} }
@ -321,8 +340,7 @@ UM.MainWindow
//anchors.bottom: parent.bottom //anchors.bottom: parent.bottom
anchors.top: viewModeButton.bottom anchors.top: viewModeButton.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height; anchors.topMargin: UM.Theme.sizes.default_margin.height;
anchors.right: sidebar.left; anchors.left: viewModeButton.left;
anchors.rightMargin: UM.Theme.sizes.window_margin.width;
//anchors.bottom: buttons.top; //anchors.bottom: buttons.top;
//anchors.bottomMargin: UM.Theme.sizes.default_margin.height; //anchors.bottomMargin: UM.Theme.sizes.default_margin.height;
@ -335,10 +353,9 @@ UM.MainWindow
{ {
id: openFileButton; id: openFileButton;
//style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button; //style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button;
//style: UM.Theme.styles.open_file_button
text: catalog.i18nc("@action:button","Open File"); text: catalog.i18nc("@action:button","Open File");
iconSource: UM.Theme.icons.load iconSource: UM.Theme.icons.load
style: UM.Theme.styles.open_file_button style: UM.Theme.styles.tool_button
tooltip: ''; tooltip: '';
anchors anchors
{ {
@ -364,6 +381,7 @@ UM.MainWindow
source: UM.Theme.images.logo; source: UM.Theme.images.logo;
width: UM.Theme.sizes.logo.width; width: UM.Theme.sizes.logo.width;
height: UM.Theme.sizes.logo.height; height: UM.Theme.sizes.logo.height;
z: -1;
sourceSize.width: width; sourceSize.width: width;
sourceSize.height: height; sourceSize.height: height;
@ -372,12 +390,12 @@ UM.MainWindow
Button Button
{ {
id: viewModeButton id: viewModeButton
property bool verticalTooltip: true
anchors anchors
{ {
top: parent.top; top: toolbar.bottom;
right: sidebar.left; topMargin: UM.Theme.sizes.window_margin.height;
rightMargin: UM.Theme.sizes.window_margin.width; left: parent.left;
} }
text: catalog.i18nc("@action:button","View Mode"); text: catalog.i18nc("@action:button","View Mode");
iconSource: UM.Theme.icons.viewmode; iconSource: UM.Theme.icons.viewmode;
@ -389,12 +407,13 @@ UM.MainWindow
id: viewMenu; id: viewMenu;
Instantiator Instantiator
{ {
id: viewMenuInstantiator
model: UM.ViewModel { } model: UM.ViewModel { }
MenuItem MenuItem
{ {
text: model.name; text: model.name
checkable: true; checkable: true;
checked: model.active; checked: model.active
exclusiveGroup: viewMenuGroup; exclusiveGroup: viewMenuGroup;
onTriggered: UM.Controller.setActiveView(model.id); onTriggered: UM.Controller.setActiveView(model.id);
} }
@ -411,12 +430,9 @@ UM.MainWindow
id: toolbar; id: toolbar;
anchors { anchors {
left: parent.left top: openFileButton.bottom;
top: parent.top topMargin: UM.Theme.sizes.window_margin.height;
topMargin: 74 left: parent.left;
//horizontalCenter: parent.horizontalCenter
//horizontalCenterOffset: -(UM.Theme.sizes.sidebar.width / 2)
//top: parent.top;
} }
} }
@ -463,14 +479,27 @@ UM.MainWindow
{ {
//; Remove & re-add the general page as we want to use our own instead of uranium standard. //; Remove & re-add the general page as we want to use our own instead of uranium standard.
removePage(0); removePage(0);
insertPage(0, catalog.i18nc("@title:tab","General") , "" , Qt.resolvedUrl("./GeneralPage.qml")); insertPage(0, catalog.i18nc("@title:tab","General"), generalPage);
//: View preferences page title //: View preferences page title
insertPage(1, catalog.i18nc("@title:tab","View"), "view-preview", Qt.resolvedUrl("./ViewPage.qml")); insertPage(1, catalog.i18nc("@title:tab","View"), viewPage);
//Force refresh //Force refresh
setPage(0) setPage(0)
} }
Item {
visible: false
GeneralPage
{
id: generalPage
}
ViewPage
{
id: viewPage
}
}
} }
Actions Actions
@ -543,8 +572,8 @@ UM.MainWindow
addMachine.onTriggered: addMachineWizard.visible = true; 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(2); } configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(3); }
manageProfiles.onTriggered: { preferences.visible = true; preferences.setPage(4); } manageProfiles.onTriggered: { preferences.visible = true; preferences.setPage(4); }
documentation.onTriggered: CuraActions.openDocumentation(); documentation.onTriggered: CuraActions.openDocumentation();
@ -605,7 +634,7 @@ UM.MainWindow
id: openDialog; id: openDialog;
//: File open dialog title //: File open dialog title
title: catalog.i18nc("@title:window","Open File") title: catalog.i18nc("@title:window","Open file")
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
//TODO: Support multiple file selection, workaround bug in KDE file dialog //TODO: Support multiple file selection, workaround bug in KDE file dialog
//selectMultiple: true //selectMultiple: true
@ -621,6 +650,11 @@ UM.MainWindow
onAccepted: onAccepted:
{ {
//Because several implementations of the file dialog only update the folder
//when it is explicitly set.
var f = folder;
folder = f;
UM.MeshFileHandler.readLocalFile(fileUrl) UM.MeshFileHandler.readLocalFile(fileUrl)
openDialog.sendMeshName(fileUrl.toString()) openDialog.sendMeshName(fileUrl.toString())
} }
@ -648,14 +682,34 @@ UM.MainWindow
onRequestAddPrinter: onRequestAddPrinter:
{ {
addMachineWizard.visible = true addMachineWizard.visible = true
addMachineWizard.firstRun = true addMachineWizard.firstRun = false
} }
} }
Component.onCompleted: Component.onCompleted:
{ {
UM.Theme.load(UM.Resources.getPath(UM.Resources.Themes, "cura")) UM.Theme.load(UM.Resources.getPath(UM.Resources.Themes, "cura"))
base.visible = true; }
Timer
{
id: startupTimer;
interval: 100;
repeat: false;
running: true;
onTriggered:
{
if(!base.visible)
{
base.visible = true;
restart();
}
else if(UM.MachineManager.activeMachineInstance == "")
{
addMachineWizard.firstRun = true;
addMachineWizard.open();
}
}
} }
} }

View File

@ -13,6 +13,18 @@ UM.PreferencesPage
//: General configuration page title //: General configuration page title
title: catalog.i18nc("@title:tab","General"); 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() function reset()
{ {
UM.Preferences.resetPreference("general/language") UM.Preferences.resetPreference("general/language")
@ -22,7 +34,8 @@ UM.PreferencesPage
pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free"))
sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info")) sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info"))
scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit"))
languageComboBox.currentIndex = 0 var defaultLanguage = UM.Preferences.getValue("general/language")
setDefaultLanguage(defaultLanguage)
} }
GridLayout GridLayout
@ -44,16 +57,16 @@ UM.PreferencesPage
id: languageList id: languageList
Component.onCompleted: { Component.onCompleted: {
append({ text: catalog.i18nc("@item:inlistbox", "Bulgarian"), code: "bg" }) // append({ text: catalog.i18nc("@item:inlistbox", "Bulgarian"), code: "bg" })
append({ text: catalog.i18nc("@item:inlistbox", "Czech"), code: "cs" }) // append({ text: catalog.i18nc("@item:inlistbox", "Czech"), code: "cs" })
append({ text: catalog.i18nc("@item:inlistbox", "English"), code: "en" }) append({ text: catalog.i18nc("@item:inlistbox", "English"), code: "en" })
append({ text: catalog.i18nc("@item:inlistbox", "Finnish"), code: "fi" }) append({ text: catalog.i18nc("@item:inlistbox", "Finnish"), code: "fi" })
append({ text: catalog.i18nc("@item:inlistbox", "French"), code: "fr" }) append({ text: catalog.i18nc("@item:inlistbox", "French"), code: "fr" })
append({ text: catalog.i18nc("@item:inlistbox", "German"), code: "de" }) append({ text: catalog.i18nc("@item:inlistbox", "German"), code: "de" })
append({ text: catalog.i18nc("@item:inlistbox", "Italian"), code: "it" }) // append({ text: catalog.i18nc("@item:inlistbox", "Italian"), code: "it" })
append({ text: catalog.i18nc("@item:inlistbox", "Polish"), code: "pl" }) append({ text: catalog.i18nc("@item:inlistbox", "Polish"), code: "pl" })
append({ text: catalog.i18nc("@item:inlistbox", "Russian"), code: "ru" }) // append({ text: catalog.i18nc("@item:inlistbox", "Russian"), code: "ru" })
append({ text: catalog.i18nc("@item:inlistbox", "Spanish"), code: "es" }) // append({ text: catalog.i18nc("@item:inlistbox", "Spanish"), code: "es" })
} }
} }

165
resources/qml/JobSpecs.qml Normal file
View File

@ -0,0 +1,165 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import UM 1.1 as UM
Rectangle {
id: base;
property bool activity: Printer.getPlatformActivity;
property string fileBaseName
property variant activeMachineInstance: UM.MachineManager.activeMachineInstance
onActiveMachineInstanceChanged:
{
base.createFileName()
}
UM.I18nCatalog { id: catalog; name:"cura"}
property variant printDuration: PrintInformation.currentPrintTime;
property real printMaterialAmount: PrintInformation.materialAmount;
width: UM.Theme.sizes.jobspecs.width
height: childrenRect.height
color: "transparent"
function createFileName(){
var splitMachineName = UM.MachineManager.activeMachineInstance.split(" ")
var abbrMachine = ''
for (var i = 0; i < splitMachineName.length; i++){
if (splitMachineName[i].search(/ultimaker/i) != -1){
abbrMachine += 'UM'
}
else{
if (splitMachineName[i].charAt(0).search(/[0-9]/g) == -1)
abbrMachine += splitMachineName[i].charAt(0)
}
var regExpAdditives = /[0-9\+]/g;
var resultAdditives = splitMachineName[i].match(regExpAdditives);
if (resultAdditives != null){
for (var j = 0; j < resultAdditives.length; j++){
abbrMachine += resultAdditives[j]
}
}
}
printJobTextfield.text = abbrMachine + '_' + base.fileBaseName
}
Connections {
target: openDialog
onHasMesh: {
if(base.fileBaseName == ''){
base.fileBaseName = name
base.createFileName()
}
}
}
onActivityChanged: {
if (activity == false){
base.fileBaseName = ''
base.createFileName()
}
}
TextField {
id: printJobTextfield
anchors.right: parent.right
height: UM.Theme.sizes.jobspecs_line.height
width: base.width
property int unremovableSpacing: 5
text: ''
horizontalAlignment: TextInput.AlignRight
onTextChanged: Printer.setJobName(text)
visible: base.activity
onEditingFinished: {
if (printJobTextfield.text != ''){
printJobTextfield.focus = false
}
}
validator: RegExpValidator {
regExp: /^[^\\ \/ \.]*$/
}
style: TextFieldStyle{
textColor: UM.Theme.colors.setting_control_text;
font: UM.Theme.fonts.default;
background: Rectangle {
opacity: 0
border.width: 0
}
}
}
Label{
id: boundingSpec
anchors.top: printJobTextfield.bottom
anchors.right: parent.right
height: UM.Theme.sizes.jobspecs_line.height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.fonts.small
color: UM.Theme.colors.text_subtext
text: Printer.getSceneBoundingBoxString
}
Rectangle {
id: specsRow
anchors.top: boundingSpec.bottom
anchors.right: parent.right
height: UM.Theme.sizes.jobspecs_line.height
Item{
width: parent.width
height: parent.height
UM.RecolorImage {
id: timeIcon
anchors.right: timeSpec.left
anchors.rightMargin: UM.Theme.sizes.default_margin.width/2
anchors.verticalCenter: parent.verticalCenter
width: UM.Theme.sizes.save_button_specs_icons.width
height: UM.Theme.sizes.save_button_specs_icons.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.colors.text_subtext
source: UM.Theme.icons.print_time;
}
Label{
id: timeSpec
anchors.right: lengthIcon.left
anchors.rightMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter
font: UM.Theme.fonts.small
color: UM.Theme.colors.text_subtext
text: (!base.printDuration || !base.printDuration.valid) ? "00h 00min" : base.printDuration.getDisplayString(UM.DurationFormat.Short)
}
UM.RecolorImage {
id: lengthIcon
anchors.right: lengthSpec.left
anchors.rightMargin: UM.Theme.sizes.default_margin.width/2
anchors.verticalCenter: parent.verticalCenter
width: UM.Theme.sizes.save_button_specs_icons.width
height: UM.Theme.sizes.save_button_specs_icons.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.colors.text_subtext
source: UM.Theme.icons.category_material;
}
Label{
id: lengthSpec
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
font: UM.Theme.fonts.small
color: UM.Theme.colors.text_subtext
text: base.printMaterialAmount <= 0 ? "0.0 m" : catalog.i18nc("@label %1 is length of filament","%1 m").arg(base.printMaterialAmount)
}
}
}
}

View File

@ -8,69 +8,68 @@ import QtQuick.Layouts 1.1
import UM 1.1 as UM import UM 1.1 as UM
Column{ Item{
id: base; id: base;
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}
property int totalHeightProfileSetup: childrenRect.height property int totalHeightProfileSetup: childrenRect.height
property Action manageProfilesAction property Action manageProfilesAction
spacing: 0
Rectangle{ Rectangle {
id: variantItem; id: variantRow
height: UM.Theme.sizes.sidebar_setup.height anchors.top: base.top
width: base.width width: base.width
visible: UM.MachineManager.hasVariants; height: UM.Theme.sizes.sidebar_setup.height
//visible: UM.MachineManager.hasVariants;
visible: true
Rectangle { Label{
id: variantRow id: variantLabel
width: base.width text: catalog.i18nc("@label","Variant:");
height: parent.heigth anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.verticalCenter: parent.verticalCenter
width: parent.width/100*45
font: UM.Theme.fonts.default;
}
Label{ ToolButton {
id: variantLabel id: variantSelection
text: catalog.i18nc("@label","Variant:"); text: UM.MachineManager.activeMachineVariant
anchors.left: parent.left width: parent.width/100*55
anchors.leftMargin: UM.Theme.sizes.default_margin.width; height: UM.Theme.sizes.setting_control.height
anchors.verticalCenter: parent.verticalCenter tooltip: UM.MachineManager.activeMachineInstance;
width: parent.width/100*45 anchors.right: parent.right
font: UM.Theme.fonts.default; anchors.rightMargin: UM.Theme.sizes.default_margin.width
} anchors.verticalCenter: parent.verticalCenter
style: UM.Theme.styles.sidebar_header_button
ToolButton { menu: Menu
id: variantSelection {
text: UM.MachineManager.activeMachineVariant id: variantsSelectionMenu
width: parent.width/100*55 Instantiator
height: UM.Theme.sizes.setting_control.height
tooltip: UM.MachineManager.activeMachineInstance;
anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter
style: UM.Theme.styles.sidebar_header_button
menu: Menu
{ {
id: variantsSelectionMenu model: UM.MachineVariantsModel { id: variantsModel }
Instantiator MenuItem
{ {
model: UM.MachineVariantsModel { } text: model.name;
MenuItem checkable: true;
{ checked: model.active;
text: model.name; exclusiveGroup: variantSelectionMenuGroup;
checkable: true; onTriggered: UM.MachineManager.setActiveMachineVariant(variantsModel.getItem(index).name)
checked: model.active;
exclusiveGroup: variantSelectionMenuGroup;
onTriggered: UM.MachineManager.setActiveMachineVariant(model.getItem(index).name)
}
} }
onObjectAdded: variantsSelectionMenu.insertItem(index, object)
ExclusiveGroup { id: variantSelectionMenuGroup; } onObjectRemoved: variantsSelectionMenu.removeItem(object)
} }
ExclusiveGroup { id: variantSelectionMenuGroup; }
} }
} }
} }
Rectangle{ Rectangle{
id: globalProfileRow; id: globalProfileRow;
anchors.top: UM.MachineManager.hasVariants ? variantRow.bottom : base.top
//anchors.top: variantRow.bottom
height: UM.Theme.sizes.sidebar_setup.height height: UM.Theme.sizes.sidebar_setup.height
width: base.width width: base.width
@ -82,6 +81,7 @@ Column{
text: catalog.i18nc("@label","Global Profile:"); text: catalog.i18nc("@label","Global Profile:");
width: parent.width/100*45 width: parent.width/100*45
font: UM.Theme.fonts.default; font: UM.Theme.fonts.default;
color: UM.Theme.colors.text;
} }
@ -148,8 +148,4 @@ Column{
// } // }
} }
} }
Rectangle{ }
width: base.width
height: UM.Theme.sizes.default_margin.width/2
}
}

View File

@ -10,176 +10,71 @@ import UM 1.1 as UM
Rectangle { Rectangle {
id: base; id: base;
UM.I18nCatalog { id: catalog; name:"cura"}
property real progress: UM.Backend.progress; property real progress: UM.Backend.progress;
property bool activity: Printer.getPlatformActivity; property bool activity: Printer.getPlatformActivity;
Behavior on progress { NumberAnimation { duration: 250; } } //Behavior on progress { NumberAnimation { duration: 250; } }
property int totalHeight: childrenRect.height property int totalHeight: childrenRect.height + UM.Theme.sizes.default_margin.height
property string fileBaseName property string fileBaseName
property variant activeMachineInstance: UM.MachineManager.activeMachineInstance property string statusText: {
if(progress == 0) {
onActiveMachineInstanceChanged: if(!activity) {
{ return catalog.i18nc("@label:PrintjobStatus","Please load a 3d model");
base.createFileName() } else {
} return catalog.i18nc("@label:PrintjobStatus","Preparing to slice...");
UM.I18nCatalog { id: catalog; name:"cura"}
property variant printDuration: PrintInformation.currentPrintTime;
property real printMaterialAmount: PrintInformation.materialAmount;
function createFileName(){
var splitMachineName = UM.MachineManager.activeMachineInstance.split(" ")
var abbrMachine = ''
for (var i = 0; i < splitMachineName.length; i++){
if (splitMachineName[i].search(/ultimaker/i) != -1){
abbrMachine += 'UM'
}
else{
if (splitMachineName[i].charAt(0).search(/[0-9]/g) == -1)
abbrMachine += splitMachineName[i].charAt(0)
}
var regExpAdditives = /[0-9\+]/g;
var resultAdditives = splitMachineName[i].match(regExpAdditives);
if (resultAdditives != null){
for (var j = 0; j < resultAdditives.length; j++){
abbrMachine += resultAdditives[j]
}
}
} }
printJobTextfield.text = abbrMachine + '_' + base.fileBaseName } else if(base.progress < 0.99) {
return catalog.i18nc("@label:PrintjobStatus","Slicing...");
} else {
return catalog.i18nc("@label:PrintjobStatus","Ready to ") + UM.OutputDeviceManager.activeDeviceShortDescription;
}
} }
Connections { Label {
target: openDialog id: statusLabel
onHasMesh: { width: parent.width - 2 * UM.Theme.sizes.default_margin.width
base.fileBaseName = name anchors.top: parent.top
base.createFileName() anchors.left: parent.left
} anchors.leftMargin: UM.Theme.sizes.default_margin.width
color: UM.Theme.colors.text
font: UM.Theme.fonts.large
text: statusText;
} }
Rectangle{ Rectangle{
id: printJobRow id: progressBar
implicitWidth: base.width; width: parent.width - 2 * UM.Theme.sizes.default_margin.width
implicitHeight: UM.Theme.sizes.sidebar_header.height height: UM.Theme.sizes.progressbar.height
anchors.top: parent.top anchors.top: statusLabel.bottom
color: UM.Theme.colors.sidebar_header_bar anchors.topMargin: UM.Theme.sizes.default_margin.height/4
Label{ anchors.left: parent.left
id: printJobTextfieldLabel anchors.leftMargin: UM.Theme.sizes.default_margin.width
text: catalog.i18nc("@label:textbox", "Printjob Name"); radius: UM.Theme.sizes.progressbar_radius.width
anchors.left: parent.left color: UM.Theme.colors.progressbar_background
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.verticalCenter: parent.verticalCenter
font: UM.Theme.fonts.default;
color: UM.Theme.colors.text_white
}
TextField {
id: printJobTextfield
anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width;
anchors.verticalCenter: parent.verticalCenter
width: parent.width/100*55
height: UM.Theme.sizes.sidebar_inputFields.height
property int unremovableSpacing: 5
text: ''
onEditingFinished: {
if (printJobTextfield.text != ''){
printJobTextfield.focus = false
}
}
validator: RegExpValidator {
regExp: /^[^\\ \/ \.]*$/
}
style: TextFieldStyle{
textColor: UM.Theme.colors.setting_control_text;
font: UM.Theme.fonts.default;
background: Rectangle {
radius: 0
implicitWidth: parent.width
implicitHeight: parent.height
border.width: 1;
border.color: UM.Theme.colors.slider_groove_border;
}
}
}
}
Rectangle { Rectangle{
id: specsRow width: Math.max(parent.width * base.progress)
implicitWidth: base.width
implicitHeight: UM.Theme.sizes.sidebar_specs_bar.height
anchors.top: printJobRow.bottom
Item{
id: time
width: childrenRect.width;
height: parent.height height: parent.height
anchors.left: parent.left color: UM.Theme.colors.progressbar_control
anchors.leftMargin: UM.Theme.sizes.default_margin.width radius: UM.Theme.sizes.progressbar_radius.width
anchors.top: parent.top visible: base.progress > 0.99 ? false : true
visible: base.printMaterialAmount > 0 ? true : false
UM.RecolorImage {
id: timeIcon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
width: UM.Theme.sizes.save_button_specs_icons.width
height: UM.Theme.sizes.save_button_specs_icons.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.colors.text_hover
source: UM.Theme.icons.print_time;
}
Label{
id: timeSpec
anchors.verticalCenter: parent.verticalCenter
anchors.left: timeIcon.right
anchors.leftMargin: UM.Theme.sizes.default_margin.width/2
font: UM.Theme.fonts.default
color: UM.Theme.colors.text
text: (!base.printDuration || !base.printDuration.valid) ? "" : base.printDuration.getDisplayString(UM.DurationFormat.Short)
}
}
Item{
width: parent.width / 100 * 55
height: parent.height
anchors.left: time.right
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.top: parent.top
visible: base.printMaterialAmount > 0 ? true : false
UM.RecolorImage {
id: lengthIcon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
width: UM.Theme.sizes.save_button_specs_icons.width
height: UM.Theme.sizes.save_button_specs_icons.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.colors.text_hover
source: UM.Theme.icons.category_material;
}
Label{
id: lengthSpec
anchors.verticalCenter: parent.verticalCenter
anchors.left: lengthIcon.right
anchors.leftMargin: UM.Theme.sizes.default_margin.width/2
font: UM.Theme.fonts.default
color: UM.Theme.colors.text
text: base.printMaterialAmount <= 0 ? "" : catalog.i18nc("@label %1 is length of filament","%1 m").arg(base.printMaterialAmount)
}
} }
} }
Rectangle{ Rectangle{
id: saveRow id: saveRow
width: base.width width: base.width
height: saveToButton.height + (UM.Theme.sizes.default_margin.height / 2) // height + bottomMargin height: saveToButton.height
anchors.top: specsRow.bottom anchors.top: progressBar.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.left: parent.left anchors.left: parent.left
Button { Button {
id: saveToButton id: saveToButton
property int resizedWidth property int resizedWidth
x: base.width - saveToButton.resizedWidth - UM.Theme.sizes.default_margin.width - UM.Theme.sizes.save_button_save_to_button.height x: base.width - saveToButton.resizedWidth - UM.Theme.sizes.default_margin.width - UM.Theme.sizes.save_button_save_to_button.height + 3
tooltip: UM.OutputDeviceManager.activeDeviceDescription; tooltip: UM.OutputDeviceManager.activeDeviceDescription;
enabled: base.progress > 0.99 && base.activity == true enabled: base.progress > 0.99 && base.activity == true
height: UM.Theme.sizes.save_button_save_to_button.height height: UM.Theme.sizes.save_button_save_to_button.height
@ -188,35 +83,32 @@ Rectangle {
text: UM.OutputDeviceManager.activeDeviceShortDescription text: UM.OutputDeviceManager.activeDeviceShortDescription
onClicked: onClicked:
{ {
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice) UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, Printer.jobName)
} }
style: ButtonStyle { style: ButtonStyle {
background: Rectangle { background: Rectangle {
color: control.hovered ? UM.Theme.colors.load_save_button_hover : UM.Theme.colors.load_save_button //opacity: control.enabled ? 1.0 : 0.5
//Behavior on opacity { NumberAnimation { duration: 50; } }
border.color: !control.enabled ? UM.Theme.colors.action_button_disabled_border :
control.pressed ? UM.Theme.colors.action_button_active_border :
control.hovered ? UM.Theme.colors.action_button_hovered_border : UM.Theme.colors.action_button_border
color: !control.enabled ? UM.Theme.colors.action_button_disabled :
control.pressed ? UM.Theme.colors.action_button_active :
control.hovered ? UM.Theme.colors.action_button_hovered : UM.Theme.colors.action_button
Behavior on color { ColorAnimation { duration: 50; } } Behavior on color { ColorAnimation { duration: 50; } }
width: { width: {
var w = 0; saveToButton.resizedWidth = actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)
if (base.width*0.55 > actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)){ return saveToButton.resizedWidth
saveToButton.resizedWidth = base.width*0.55
w = base.width*0.55
}
else {
saveToButton.resizedWidth = actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)
w = actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)
}
if(w < base.width * 0.55) {
w = base.width * 0.55;
}
return w;
} }
Label { Label {
id: actualLabel id: actualLabel
//Behavior on opacity { NumberAnimation { duration: 50; } }
anchors.centerIn: parent anchors.centerIn: parent
color: UM.Theme.colors.load_save_button_text color: !control.enabled ? UM.Theme.colors.action_button_disabled_text :
font: UM.Theme.fonts.default control.pressed ? UM.Theme.colors.action_button_active_text :
control.hovered ? UM.Theme.colors.action_button_hovered_text : UM.Theme.colors.action_button_text
font: UM.Theme.fonts.action_button
text: control.text; text: control.text;
} }
} }
@ -232,12 +124,18 @@ Rectangle {
anchors.rightMargin: UM.Theme.sizes.default_margin.width anchors.rightMargin: UM.Theme.sizes.default_margin.width
width: UM.Theme.sizes.save_button_save_to_button.height width: UM.Theme.sizes.save_button_save_to_button.height
height: UM.Theme.sizes.save_button_save_to_button.height height: UM.Theme.sizes.save_button_save_to_button.height
enabled: base.progress > 0.99 && base.activity == true
//iconSource: UM.Theme.icons[UM.OutputDeviceManager.activeDeviceIconName]; //iconSource: UM.Theme.icons[UM.OutputDeviceManager.activeDeviceIconName];
style: ButtonStyle { style: ButtonStyle {
background: Rectangle { background: Rectangle {
id: deviceSelectionIcon id: deviceSelectionIcon
color: control.hovered ? UM.Theme.colors.load_save_button_hover : UM.Theme.colors.load_save_button border.color: !control.enabled ? UM.Theme.colors.action_button_disabled_border :
control.pressed ? UM.Theme.colors.action_button_active_border :
control.hovered ? UM.Theme.colors.action_button_hovered_border : UM.Theme.colors.action_button_border
color: !control.enabled ? UM.Theme.colors.action_button_disabled :
control.pressed ? UM.Theme.colors.action_button_active :
control.hovered ? UM.Theme.colors.action_button_hovered : UM.Theme.colors.action_button
Behavior on color { ColorAnimation { duration: 50; } } Behavior on color { ColorAnimation { duration: 50; } }
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width / 2; anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width / 2;
@ -245,15 +143,16 @@ Rectangle {
height: parent.height height: parent.height
UM.RecolorImage { UM.RecolorImage {
id: lengthIcon
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: UM.Theme.sizes.standard_arrow.width width: UM.Theme.sizes.standard_arrow.width
height: UM.Theme.sizes.standard_arrow.height height: UM.Theme.sizes.standard_arrow.height
sourceSize.width: width sourceSize.width: width
sourceSize.height: width sourceSize.height: height
color: UM.Theme.colors.load_save_button_text color: !control.enabled ? UM.Theme.colors.action_button_disabled_text :
source: UM.Theme.icons.arrow_bottom control.pressed ? UM.Theme.colors.action_button_active_text :
control.hovered ? UM.Theme.colors.action_button_hovered_text : UM.Theme.colors.action_button_text;
source: UM.Theme.icons.arrow_bottom;
} }
} }
label: Label{ } label: Label{ }

View File

@ -15,6 +15,7 @@ Rectangle
property Action addMachineAction; property Action addMachineAction;
property Action configureMachinesAction; property Action configureMachinesAction;
property Action manageProfilesAction; property Action manageProfilesAction;
property int currentModeIndex;
color: UM.Theme.colors.sidebar; color: UM.Theme.colors.sidebar;
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}
@ -49,37 +50,118 @@ Rectangle
addMachineAction: base.addMachineAction; addMachineAction: base.addMachineAction;
configureMachinesAction: base.configureMachinesAction; configureMachinesAction: base.configureMachinesAction;
modesModel: modesListModel; }
currentModeIndex: Rectangle {
{ id: headerSeparator
var index = parseInt(UM.Preferences.getValue("cura/active_mode")) width: parent.width
if(index) height: UM.Theme.sizes.sidebar_lining.height
{ color: UM.Theme.colors.sidebar_lining
return index; anchors.top: header.bottom
} anchors.topMargin: UM.Theme.sizes.default_margin.height
return 0;
}
onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex);
} }
ProfileSetup { ProfileSetup {
id: profileItem id: profileItem
manageProfilesAction: base.manageProfilesAction manageProfilesAction: base.manageProfilesAction
anchors.top: header.bottom anchors.top: settingsModeSelection.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
width: parent.width width: parent.width
height: totalHeightProfileSetup height: totalHeightProfileSetup
} }
currentModeIndex:
{
var index = parseInt(UM.Preferences.getValue("cura/active_mode"))
if(index)
{
return index;
}
return 0;
}
onCurrentModeIndexChanged:
{
UM.Preferences.setValue("cura/active_mode", currentModeIndex);
}
Label {
id: settingsModeLabel
text: catalog.i18nc("@label:listbox","Setup");
anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.top: headerSeparator.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
width: parent.width/100*45
font: UM.Theme.fonts.large;
color: UM.Theme.colors.text
}
Rectangle {
id: settingsModeSelection
width: parent.width/100*55
height: UM.Theme.sizes.sidebar_header_mode_toggle.height
anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width
anchors.top: headerSeparator.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
Component{
id: wizardDelegate
Button {
height: settingsModeSelection.height
anchors.left: parent.left
anchors.leftMargin: model.index * (settingsModeSelection.width / 2)
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 2
text: model.text
exclusiveGroup: modeMenuGroup;
checkable: true;
checked: base.currentModeIndex == index
onClicked: base.currentModeIndex = index
style: ButtonStyle {
background: Rectangle {
border.color: control.checked ? UM.Theme.colors.toggle_checked_border :
control.pressed ? UM.Theme.colors.toggle_active_border :
control.hovered ? UM.Theme.colors.toggle_hovered_border : UM.Theme.colors.toggle_unchecked_border
color: control.checked ? UM.Theme.colors.toggle_checked :
control.pressed ? UM.Theme.colors.toggle_active :
control.hovered ? UM.Theme.colors.toggle_hovered : UM.Theme.colors.toggle_unchecked
Behavior on color { ColorAnimation { duration: 50; } }
Label {
anchors.centerIn: parent
color: control.checked ? UM.Theme.colors.toggle_checked_text :
control.pressed ? UM.Theme.colors.toggle_active_text :
control.hovered ? UM.Theme.colors.toggle_hovered_text : UM.Theme.colors.toggle_unchecked_text
font: UM.Theme.fonts.default
text: control.text;
}
}
label: Item { }
}
}
}
ExclusiveGroup { id: modeMenuGroup; }
ListView{
id: modesList
property var index: 0
model: modesListModel
delegate: wizardDelegate
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
}
}
Loader Loader
{ {
id: sidebarContents; id: sidebarContents;
anchors.bottom: saveButton.top anchors.bottom: footerSeparator.top
anchors.top: profileItem.bottom anchors.top: profileItem.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.left: base.left anchors.left: base.left
anchors.right: base.right anchors.right: base.right
source: modesListModel.count > header.currentModeIndex ? modesListModel.get(header.currentModeIndex).file : ""; source: modesListModel.count > base.currentModeIndex ? modesListModel.get(base.currentModeIndex).file : "";
property Item sidebar: base; property Item sidebar: base;
@ -100,6 +182,15 @@ Rectangle
} }
} }
Rectangle {
id: footerSeparator
width: parent.width
height: UM.Theme.sizes.sidebar_lining.height
color: UM.Theme.colors.sidebar_lining
anchors.bottom: saveButton.top
anchors.bottomMargin: UM.Theme.sizes.default_margin.height
}
SaveButton SaveButton
{ {
id: saveButton; id: saveButton;
@ -122,6 +213,6 @@ Rectangle
{ {
modesListModel.append({ text: catalog.i18nc("@title:tab", "Simple"), file: "SidebarSimple.qml" }) modesListModel.append({ text: catalog.i18nc("@title:tab", "Simple"), file: "SidebarSimple.qml" })
modesListModel.append({ text: catalog.i18nc("@title:tab", "Advanced"), file: "SidebarAdvanced.qml" }) modesListModel.append({ text: catalog.i18nc("@title:tab", "Advanced"), file: "SidebarAdvanced.qml" })
sidebarContents.setSource(modesListModel.get(header.currentModeIndex).file) sidebarContents.setSource(modesListModel.get(base.currentModeIndex).file)
} }
} }

View File

@ -11,8 +11,6 @@ Item
{ {
id: base; id: base;
// Machine Setup // Machine Setup
property variant modesModel;
property int currentModeIndex: 0;
property Action addMachineAction; property Action addMachineAction;
property Action configureMachinesAction; property Action configureMachinesAction;
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}
@ -21,77 +19,29 @@ Item
Rectangle { Rectangle {
id: settingsModeRow id: settingsModeRow
width: base.width width: base.width
height: UM.Theme.sizes.sidebar_header.height height: 0
anchors.top: parent.top anchors.top: parent.top
color: UM.Theme.colors.sidebar_header_bar color: UM.Theme.colors.sidebar_header_bar
}
Label{ Label{
id: settingsModeLabel id: printjobTabLabel
text: catalog.i18nc("@label:listbox","Print Setup"); text: catalog.i18nc("@label:listbox","Print Job");
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width; anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.verticalCenter: parent.verticalCenter anchors.top: settingsModeRow.bottom
width: parent.width/100*45 anchors.topMargin: UM.Theme.sizes.default_margin.height
font: UM.Theme.fonts.default; width: parent.width/100*45
color: UM.Theme.colors.text_white font: UM.Theme.fonts.large;
} color: UM.Theme.colors.text
Rectangle{
id: settingsModeSelection
width: parent.width/100*55
height: childrenRect.height - UM.Theme.sizes.default_margin.width;
anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width;
anchors.verticalCenter: parent.verticalCenter
Component{
id: wizardDelegate
Button {
id: simpleModeButton
height: settingsModeSelection.height
anchors.left: parent.left
anchors.leftMargin: model.index * (settingsModeSelection.width / 2)
anchors.top: parent.top
width: parent.width / 2
text: model.text
exclusiveGroup: modeMenuGroup;
checkable: true;
checked: base.currentModeIndex == index
onClicked: base.currentModeIndex = index
style: ButtonStyle {
background: Rectangle {
color: control.checked ? UM.Theme.colors.toggle_active : UM.Theme.colors.toggle_disabled
Behavior on color { ColorAnimation { duration: 50; } }
Label {
anchors.centerIn: parent
color: control.checked ? UM.Theme.colors.toggle_active_text : UM.Theme.colors.toggle_disabled_text
font: UM.Theme.fonts.default
text: control.text;
}
}
label: Item { }
}
}
}
ExclusiveGroup { id: modeMenuGroup; }
ListView{
id: modesList
property var index: 0
model: base.modesModel
delegate: wizardDelegate
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: UM.Theme.sizes.sidebar_header.height
currentIndex: base.currentIndex;
}
}
} }
Rectangle { Rectangle {
id: machineSelectionRow id: machineSelectionRow
width: base.width width: base.width
height: UM.Theme.sizes.sidebar_header.height height: UM.Theme.sizes.sidebar_setup.height
anchors.top: settingsModeRow.bottom anchors.top: printjobTabLabel.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
Label{ Label{
@ -102,6 +52,7 @@ Item
anchors.leftMargin: UM.Theme.sizes.default_margin.width anchors.leftMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font: UM.Theme.fonts.default; font: UM.Theme.fonts.default;
color: UM.Theme.colors.text;
} }
ToolButton { ToolButton {
@ -110,7 +61,6 @@ Item
width: parent.width/100*55 width: parent.width/100*55
height: UM.Theme.sizes.setting_control.height height: UM.Theme.sizes.setting_control.height
tooltip: UM.MachineManager.activeMachineInstance; tooltip: UM.MachineManager.activeMachineInstance;
//style: UM.Theme.styles.sidebar_header_button;
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width anchors.rightMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

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