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(CURA_VERSION "master" CACHE STRING "Version name of Cura")
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
# Extract Strings
add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura)
@ -60,9 +63,12 @@ install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
install(DIRECTORY plugins DESTINATION lib/cura)
install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if(NOT APPLE AND NOT WIN32)
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages)
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages FILES_MATCHING PATTERN *.py)
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages/cura)
install(FILES cura.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
else()
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages FILES_MATCHING PATTERN *.py)
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
endif()
include(CPackConfig.cmake)

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:
* (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
Dependencies
@ -41,6 +41,7 @@ Third party plugins
-------------
* [Print time calculator](https://github.com/nallath/PrintCostCalculator)
* [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
----------------------------------

View file

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

View file

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

View file

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

View file

@ -8,9 +8,15 @@ from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTex
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
def show(type, value, tb):
if not hasattr(sys, "frozen"):
traceback.print_exception(type, value, tb)
def show(exception_type, value, tb):
debug_mode = False
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()
if not application:
@ -34,7 +40,7 @@ def show(type, value, tb):
except:
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 = 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)
layout.addWidget(buttons)
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"))
dialog.exec_()

View file

@ -3,11 +3,6 @@
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.Scene.SceneNode import SceneNode
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.Matrix import Matrix
from UM.Math.Quaternion import Quaternion
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Resources import Resources
from UM.Scene.ToolHandle import ToolHandle
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -44,6 +40,8 @@ from . import CameraAnimation
from . import PrintInformation
from . import CuraActions
from . import MultiMaterialDecorator
from . import ZOffsetDecorator
from . import CuraSplashScreen
from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from PyQt5.QtGui import QColor, QIcon
@ -57,6 +55,16 @@ import numpy
import copy
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 ResourceTypes:
QmlFiles = Resources.UserType + 1
@ -68,7 +76,7 @@ class CuraApplication(QtApplication):
if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
super().__init__(name = "cura", version = "15.09.82")
super().__init__(name = "cura", version = CuraVersion)
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@ -90,6 +98,8 @@ class CuraApplication(QtApplication):
self._i18n_catalog = None
self._previous_active_tool = None
self._platform_activity = False
self._scene_boundingbox = AxisAlignedBox()
self._job_name = None
self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged)
self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested)
@ -131,8 +141,12 @@ class CuraApplication(QtApplication):
def addCommandLineOptions(self, parser):
super().addCommandLineOptions(parser)
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):
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");
i18nCatalog.setTagReplacements({
@ -144,7 +158,7 @@ class CuraApplication(QtApplication):
controller = self.getController()
controller.setActiveView("MeshView")
controller.setActiveView("SolidView")
controller.setCameraTool("CameraTool")
controller.setSelectionTool("SelectionTool")
@ -159,31 +173,26 @@ class CuraApplication(QtApplication):
self._volume = BuildVolume.BuildVolume(root)
self.getRenderer().setLightPosition(Vector(0, 150, 0))
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
camera = Camera("3d", root)
camera.setPosition(Vector(-150, 150, 300))
camera.setPosition(Vector(-80, 250, 700))
camera.setPerspective(True)
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.setCameraTool(self.getController().getTool("CameraTool"))
controller.getScene().setActiveCamera("3d")
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self.initializeEngine()
manager = self.getMachineManager()
if not self.getMachineManager().getMachineInstances():
self.requestAddPrinter.emit()
if self._engine.rootObjects:
self.closeSplash()
@ -232,35 +241,58 @@ class CuraApplication(QtApplication):
requestAddPrinter = pyqtSignal()
activityChanged = pyqtSignal()
sceneBoundingBoxChanged = pyqtSignal()
@pyqtProperty(bool, notify = activityChanged)
def getPlatformActivity(self):
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):
count = 0
scene_boundingbox = AxisAlignedBox()
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode or not node.getMeshData():
continue
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.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
@pyqtSlot("quint64")
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
object = Selection.getSelectedObject(0)
if object:
if object.getParent():
group_node = object.getParent()
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
node = Selection.getSelectedObject(0)
if node:
if node.getParent():
group_node = node.getParent()
if not group_node.callDecoration("isGroup"):
op = RemoveSceneNodeOperation(object)
op = RemoveSceneNodeOperation(node)
else:
while group_node.getParent().callDecoration("isGroup"):
group_node = group_node.getParent()
@ -294,10 +326,15 @@ class CuraApplication(QtApplication):
@pyqtSlot("quint64")
def centerObject(self, 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
node = Selection.getSelectedObject(0)
if not node:
return
if node.getParent() and node.getParent().callDecoration("isGroup"):
node = node.getParent()
if node:
op = SetTransformOperation(node, Vector())
op.push()
@ -335,14 +372,12 @@ class CuraApplication(QtApplication):
continue #Grouped nodes don't need resetting as their parent (the group) is resetted)
nodes.append(node)
if nodes:
op = GroupedOperation()
for node in nodes:
# Ensure that the object is above the build platform
move_distance = node.getBoundingBox().center.y
if move_distance <= 0:
move_distance = -node.getBoundingBox().bottom
op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0)))
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
op.addOperation(SetTransformOperation(node, Vector(0,0,0)))
op.push()
@ -364,10 +399,8 @@ class CuraApplication(QtApplication):
for node in nodes:
# Ensure that the object is above the build platform
move_distance = node.getBoundingBox().center.y
if move_distance <= 0:
move_distance = -node.getBoundingBox().bottom
op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0), Quaternion(), Vector(1, 1, 1)))
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
op.addOperation(SetTransformOperation(node, Vector(0,0,0), Quaternion(), Vector(1, 1, 1)))
op.push()
@ -407,6 +440,7 @@ class CuraApplication(QtApplication):
return log
recentFilesChanged = pyqtSignal()
@pyqtProperty("QVariantList", notify = recentFilesChanged)
def recentFiles(self):
return self._recent_files
@ -421,6 +455,7 @@ class CuraApplication(QtApplication):
self.expandedCategoriesChanged.emit()
expandedCategoriesChanged = pyqtSignal()
@pyqtProperty("QStringList", notify = expandedCategoriesChanged)
def expandedCategories(self):
return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
@ -464,17 +499,20 @@ class CuraApplication(QtApplication):
group_decorator = GroupDecorator()
group_node.addDecorator(group_decorator)
group_node.setParent(self.getController().getScene().getRoot())
center = Selection.getSelectionCenter()
group_node.setPosition(center)
group_node.setCenterPosition(center)
for node in Selection.getAllSelectedObjects():
world = node.getWorldPosition()
node.setParent(group_node)
group_node.setCenterPosition(group_node.getBoundingBox().center)
#group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0))
group_node.translate(group_node.getBoundingBox().center)
node.setPosition(world - center)
for node in group_node.getChildren():
Selection.remove(node)
Selection.add(group_node)
@pyqtSlot()
def ungroupSelected(self):
ungrouped_nodes = []
@ -485,12 +523,11 @@ class CuraApplication(QtApplication):
for child in node.getChildren():
if type(child) is SceneNode:
children_to_move.append(child)
for child in children_to_move:
position = child.getWorldPosition()
child.setParent(node.getParent())
print(node.getPosition())
child.translate(node.getPosition())
child.setPosition(child.getPosition().scale(node.getScale()))
child.setPosition(position - node.getParent().getWorldPosition())
child.scale(node.getScale())
child.rotate(node.getOrientation())
@ -501,6 +538,9 @@ class CuraApplication(QtApplication):
for node in ungrouped_nodes:
Selection.remove(node)
def _createSplashScreen(self):
return CuraSplashScreen.CuraSplashScreen()
def _onActiveMachineChanged(self):
machine = self.getMachineManager().getActiveMachineInstance()
if machine:
@ -536,6 +576,8 @@ class CuraApplication(QtApplication):
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
op.push()
self.getController().getScene().sceneChanged.emit(node) #Force scene change.
def _onJobFinished(self, job):
if type(job) is not ReadMeshJob or not job.getResult():
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)
self._element_counts[layer] = data.elementCount
self.clear()
self.addVertices(vertices)
self.addColors(colors)
self.addIndices(indices.flatten())
@ -107,7 +108,7 @@ class Layer():
def build(self, offset, vertices, colors, indices):
result = offset
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
polygon.build(result, vertices, colors, indices)
@ -117,14 +118,27 @@ class Layer():
return result
def createMesh(self):
return self.createMeshOrJumps(True)
def createJumps(self):
return self.createMeshOrJumps(False)
def createMeshOrJumps(self, make_mesh):
builder = MeshBuilder()
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()
points = numpy.copy(polygon.data)
if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
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.
normals = numpy.copy(points)
@ -175,6 +189,8 @@ class Polygon():
SkirtType = 5
InfillType = 6
SupportInfillType = 7
MoveCombingType = 8
MoveRetractionType = 9
def __init__(self, mesh, type, data, line_width):
super().__init__()
@ -185,18 +201,14 @@ class Polygon():
def build(self, offset, vertices, colors, indices):
self._begin = offset
self._end = self._begin + len(self._data) - 1
color = self.getColor()
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)
color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32)
for i in range(len(self._data)):
vertices[offset + i, :] = self._data[i, :]
colors[offset + i, 0] = color.r
colors[offset + i, 1] = color.g
colors[offset + i, 2] = color.b
colors[offset + i, 3] = color.a
self._end = self._begin + len(self._data) - 1
vertices[self._begin:self._end + 1, :] = self._data[:, :]
colors[self._begin:self._end + 1, :] = color
for i in range(self._begin, self._end):
indices[i, 0] = i
@ -220,6 +232,10 @@ class Polygon():
return Color(1.0, 0.74, 0.0, 1.0)
elif self._type == self.SupportInfillType:
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:
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.
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:
current = todo_node_list.pop()
for node in current.todo:
@ -61,7 +61,7 @@ class OneAtATimeIterator(Iterator.Iterator):
self._node_stack = new_order
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!
@ -99,7 +99,7 @@ class OneAtATimeIterator(Iterator.Iterator):
## Internal object used to keep track of a possible order in which to print objects.
class _objectOrder():
class _ObjectOrder():
def __init__(self, order, todo):
"""
: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 ConvexHullJob
from . import ZOffsetDecorator
import time
import threading
@ -69,8 +70,12 @@ class PlatformPhysics:
# Move it downwards if bottom is above platform
move_vector = Vector()
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
if bbox.bottom > 0:
move_vector.setY(-bbox.bottom)
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):
# pass#move_vector.setY(-bbox.bottom)
@ -127,8 +132,8 @@ class PlatformPhysics:
if overlap is None:
continue
move_vector.setX(overlap[0] * 1.01)
move_vector.setZ(overlap[1] * 1.01)
move_vector.setX(overlap[0] * 1.1)
move_vector.setZ(overlap[1] * 1.1)
convex_hull = node.callDecoration("getConvexHull")
if convex_hull:
if not convex_hull.isValid():
@ -149,5 +154,16 @@ class PlatformPhysics:
self._enabled = False
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._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.
import sys
import os
def exceptHook(type, value, traceback):
import cura.CrashHandler
@ -11,7 +12,22 @@ def exceptHook(type, value, traceback):
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.run()

View file

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

View file

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

View file

@ -31,6 +31,7 @@ class ChangeLog(Extension, QObject,):
self._change_logs = None
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
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()
def getChangeLogs(self):
@ -57,7 +58,7 @@ class ChangeLog(Extension, QObject,):
def loadChangeLogs(self):
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_header = None
for line in f:
@ -77,8 +78,8 @@ class ChangeLog(Extension, QObject,):
def _onEngineCreated(self):
if not self._version:
return #We're on dev branch.
if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"):
self.showChangelog()
#if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"):
#self.showChangelog()
def showChangelog(self):
if not self._changelog_window:

View file

@ -11,8 +11,8 @@ import UM 1.1 as UM
UM.Dialog
{
id: base
width: 300 * Screen.devicePixelRatio;
height: 500 * Screen.devicePixelRatio;
minimumWidth: 400
minimumHeight: 300;
title: "Changelog"
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
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():
return {
"plugin": {
"name": catalog.i18nc("@label", "Change Log"),
"name": catalog.i18nc("@label", "Changelog"),
"author": "Ultimaker",
"version": "1.0",
"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 ProcessSlicedObjectListJob
from . import ProcessGCodeJob
from . import StartSliceJob
import os
import sys
@ -49,6 +50,7 @@ class CuraEngineBackend(Backend):
self._onActiveViewChanged()
self._stored_layer_data = None
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onChanged)
self._profile = None
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
@ -67,22 +69,26 @@ class CuraEngineBackend(Backend):
self._slicing = False
self._restart = False
self._save_gcode = True
self._save_polygons = True
self._report_progress = True
self._enabled = True
self._always_restart = True
self._message = None
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.
# This is usefull for debugging and used to actually start the engine
# \return list of commands and args / parameters.
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.
# \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.
slicingCancelled = Signal()
## Perform a slice of the scene with the given set of settings.
#
# \param kwargs Keyword arguments.
# Valid values are:
# - settings: The settings to use for the slice. The default is the active machine.
# - save_gcode: True if the generated gcode should be saved, False if not. True by default.
# - save_polygons: True if the generated polygon data should be saved, False if not. True by default.
# - force_restart: True if the slicing process should be forcefully restarted if it is already slicing.
# If False, this method will do nothing when already slicing. True by default.
# - report_progress: True if the slicing progress should be reported, False if not. Default is True.
def slice(self, **kwargs):
## Perform a slice of the scene.
def slice(self):
if not self._enabled:
return
if self._slicing:
if not kwargs.get("force_restart", True):
return
self._slicing = False
self._restart = True
if self._process is not None:
@ -121,110 +115,56 @@ class CuraEngineBackend(Backend):
self._process.terminate()
except: # terminating a process that is already terminating causes an exception, silently ignore this.
pass
self.slicingCancelled.emit()
return
Logger.log("d", "Preparing to send slice data to engine.")
object_groups = []
if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
for node in OneAtATimeIterator(self._scene.getRoot()):
temp_list = []
children = node.getAllChildren()
children.append(node)
for child_node in children:
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
temp_list.append(child_node)
object_groups.append(temp_list)
else:
temp_list = []
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
if not getattr(node, "_outside_buildarea", False):
temp_list.append(node)
if len(temp_list) == 0:
self.processingProgress.emit(0.0)
return
object_groups.append(temp_list)
#for node in DepthFirstIterator(self._scene.getRoot()):
# if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
# if not getattr(node, "_outside_buildarea", False):
# objects.append(node)
if len(object_groups) == 0:
if self._message:
self._message.hide()
self._message = None
return #No point in slicing an empty build plate
if kwargs.get("profile", self._profile).hasErrorValue():
Logger.log('w', "Profile has error values. Aborting slicing")
self.slicingCancelled.emit()
return
if self._profile.hasErrorValue():
Logger.log("w", "Profile has error values. Aborting slicing")
if self._message:
self._message.hide()
self._message = None
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
self._message.show()
return #No slicing if we have error values since those are by definition illegal values.
# Remove existing layer data (if any)
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData():
if node.callDecoration("getLayerData"):
Application.getInstance().getController().getScene().getRoot().removeChild(node)
break
Application.getInstance().getController().getScene().gcode_list = None
self.processingProgress.emit(0.0)
if self._message:
self._message.setProgress(-1)
#else:
# self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
# self._message.show()
self._scene.gcode_list = []
self._slicing = True
self.slicingStarted.emit()
self._report_progress = kwargs.get("report_progress", True)
if self._report_progress:
self.processingProgress.emit(0.0)
if not self._message:
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
self._message.show()
else:
self._message.setProgress(-1)
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
job.start()
job.finished.connect(self._onStartSliceCompleted)
self._sendSettings(kwargs.get("profile", self._profile))
self._scene.acquireLock()
# Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages.
# This is done so the gcode can be fragmented in memory and does not need a continues memory space.
# (AKA. This prevents MemoryErrors)
self._save_gcode = kwargs.get("save_gcode", True)
if self._save_gcode:
setattr(self._scene, "gcode_list", [])
self._save_polygons = kwargs.get("save_polygons", True)
slice_message = Cura_pb2.Slice()
for group in object_groups:
group_message = slice_message.object_lists.add()
for object in group:
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
obj = group_message.objects.add()
obj.id = id(object)
verts = numpy.array(mesh_data.getVertices())
verts[:,[1,2]] = verts[:,[2,1]]
verts[:,1] *= -1
obj.vertices = verts.tostring()
self._handlePerObjectSettings(object, obj)
# Hack to add per-object settings also to the "MeshGroup" in CuraEngine
# We really should come up with a better solution for this.
self._handlePerObjectSettings(group[0], group_message)
self._scene.releaseLock()
Logger.log("d", "Sending data to engine for slicing.")
self._socket.sendMessage(slice_message)
def _onSceneChanged(self, source):
if (type(source) is not SceneNode) or (source is self._scene.getRoot()) or (source.getMeshData() is None):
def _onStartSliceCompleted(self, job):
if job.getError() or job.getResult() != True:
if self._message:
self._message.hide()
self._message = None
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
self._onChanged()
@ -242,41 +182,42 @@ class CuraEngineBackend(Backend):
self._onChanged()
def _onSlicedObjectListMessage(self, message):
if self._save_polygons:
if self._layer_view_active:
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
job.start()
else :
self._stored_layer_data = message
if self._layer_view_active:
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
job.start()
else :
self._stored_layer_data = message
def _onProgressMessage(self, message):
if message.amount >= 0.99:
self._slicing = False
if self._message:
self._message.setProgress(100)
self._message.hide()
self._message = None
if self._message:
self._message.setProgress(round(message.amount * 100))
if self._report_progress:
self.processingProgress.emit(message.amount)
self.processingProgress.emit(message.amount)
def _onGCodeLayerMessage(self, message):
if self._save_gcode:
job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
job.start()
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
def _onGCodePrefixMessage(self, message):
if self._save_gcode:
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
def _onObjectPrintTimeMessage(self, message):
self.printDurationMessage.emit(message.time, message.material_amount)
self.processingProgress.emit(1.0)
self._slicing = False
if self._message:
self._message.setProgress(100)
self._message.hide()
self._message = None
if self._always_restart:
try:
self._process.terminate()
self._createSocket()
except: # terminating a process that is already terminating causes an exception, silently ignore this.
pass
def _createSocket(self):
super()._createSocket()
@ -298,15 +239,6 @@ class CuraEngineBackend(Backend):
self._change_timer.start()
def _sendSettings(self, profile):
msg = Cura_pb2.SettingList()
for key, value in profile.getAllSettingValues(include_machine = True).items():
s = msg.settings.add()
s.name = key
s.value = str(value).encode("utf-8")
self._socket.sendMessage(msg)
def _onBackendConnected(self):
if self._restart:
self._onChanged()
@ -327,22 +259,18 @@ class CuraEngineBackend(Backend):
if self._stored_layer_data:
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
job.start()
self._stored_layer_data = None
else:
self._layer_view_active = False
def _handlePerObjectSettings(self, node, message):
profile = node.callDecoration("getProfile")
if profile:
for key, value in profile.getChangedSettingValues().items():
setting = message.settings.add()
setting.name = key
setting.value = str(value).encode()
object_settings = node.callDecoration("getAllSettingValues")
if not object_settings:
return
for key, value in object_settings.items():
setting = message.settings.add()
setting.name = key
setting.value = str(value).encode()
def _onInstanceChanged(self):
self._slicing = False
self._restart = True
if self._process is not None:
Logger.log("d", "Killing engine process")
try:
self._process.terminate()
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!
# 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 message as _message
from google.protobuf import reflection as _reflection
@ -18,7 +16,8 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='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)
@ -62,11 +61,19 @@ _POLYGON_TYPE = _descriptor.EnumDescriptor(
name='SupportInfillType', index=7, number=7,
options=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,
options=None,
serialized_start=622,
serialized_end=759,
serialized_end=804,
)
_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE)
@ -100,6 +107,7 @@ _OBJECTLIST = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -130,6 +138,7 @@ _SLICE = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -155,21 +164,21 @@ _OBJECT = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='vertices', full_name='cura.proto.Object.vertices', index=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,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normals', full_name='cura.proto.Object.normals', index=2,
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,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='indices', full_name='cura.proto.Object.indices', index=3,
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,
is_extension=False, extension_scope=None,
options=None),
@ -188,6 +197,7 @@ _OBJECT = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -218,6 +228,7 @@ _PROGRESS = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -248,6 +259,7 @@ _SLICEDOBJECTLIST = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -285,6 +297,7 @@ _SLICEDOBJECT = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -336,6 +349,7 @@ _LAYER = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
@ -361,7 +375,7 @@ _POLYGON = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='points', full_name='cura.proto.Polygon.points', index=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,
is_extension=False, extension_scope=None,
options=None),
@ -381,11 +395,12 @@ _POLYGON = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=534,
serialized_end=759,
serialized_end=804,
)
@ -406,7 +421,7 @@ _GCODELAYER = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='data', full_name='cura.proto.GCodeLayer.data', index=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,
is_extension=False, extension_scope=None,
options=None),
@ -418,11 +433,12 @@ _GCODELAYER = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=761,
serialized_end=799,
serialized_start=806,
serialized_end=844,
)
@ -462,11 +478,12 @@ _OBJECTPRINTTIME = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=801,
serialized_end=869,
serialized_start=846,
serialized_end=914,
)
@ -492,11 +509,12 @@ _SETTINGLIST = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=871,
serialized_end=923,
serialized_start=916,
serialized_end=968,
)
@ -510,14 +528,14 @@ _SETTING = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='name', full_name='cura.proto.Setting.name', index=0,
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,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='cura.proto.Setting.value', index=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,
is_extension=False, extension_scope=None,
options=None),
@ -529,11 +547,12 @@ _SETTING = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=925,
serialized_end=963,
serialized_start=970,
serialized_end=1008,
)
@ -547,7 +566,7 @@ _GCODEPREFIX = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='data', full_name='cura.proto.GCodePrefix.data', index=0,
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,
is_extension=False, extension_scope=None,
options=None),
@ -559,11 +578,12 @@ _GCODEPREFIX = _descriptor.Descriptor(
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=965,
serialized_end=992,
serialized_start=1010,
serialized_end=1037,
)
_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.i18n import i18nCatalog
from . import LayerData
from . import LayerDataDecorator
from cura import LayerData
from cura import LayerDataDecorator
import numpy
import struct
@ -32,19 +32,18 @@ class ProcessSlicedObjectListJob(Job):
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
objectIdMap = {}
object_id_map = {}
new_node = SceneNode()
## Put all nodes in a dict identified by ID
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData():
if node.callDecoration("getLayerData"):
#if hasattr(node.getMeshData(), "layerData"):
self._scene.getRoot().removeChild(node)
else:
objectIdMap[id(node)] = node
object_id_map[id(node)] = node
Job.yieldThread()
settings = Application.getInstance().getMachineManager().getActiveProfile()
layerHeight = settings.getSettingValue("layer_height")
center = None
if not settings.getSettingValue("machine_center_is_zero"):
@ -54,9 +53,15 @@ class ProcessSlicedObjectListJob(Job):
mesh = MeshData()
layer_data = LayerData.LayerData()
layer_count = 0
for object in self._message.objects:
layer_count += len(object.layers)
current_layer = 0
for object in self._message.objects:
try:
node = objectIdMap[object.id]
node = object_id_map[object.id]
except KeyError:
continue
@ -73,23 +78,34 @@ class ProcessSlicedObjectListJob(Job):
points[:,2] *= -1
points -= numpy.array(center)
points -= center
layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)
Job.yieldThread()
current_layer += 1
progress = (current_layer / layer_count) * 100
# TODO: Rebuild the layer data mesh once the layer has been processed.
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
if self._progress:
self._progress.setProgress(progress)
# We are done processing all the layers we got from the engine, now create a mesh out of the data
layer_data.build()
#Add layerdata decorator to scene node to indicate that the node has layerdata
decorator = LayerDataDecorator.LayerDataDecorator()
decorator.setLayerData(layer_data)
new_node.addDecorator(decorator)
new_node.setMeshData(mesh)
new_node.setParent(self._scene.getRoot())
if self._progress:
self._progress.setProgress(100)
view = Application.getInstance().getController().getActiveView()
if view.getPluginId() == "LayerView":
view.resetLayerData()

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.Mesh.MeshData import MeshData
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from cura.ConvexHullNode import ConvexHullNode
from PyQt5 import QtCore, QtWidgets
@ -21,7 +24,8 @@ from . import LayerViewProxy
class LayerView(View):
def __init__(self):
super().__init__()
self._material = None
self._shader = None
self._selection_shader = None
self._num_layers = 0
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
self._proxy = LayerViewProxy.LayerViewProxy()
@ -29,6 +33,7 @@ class LayerView(View):
self._max_layers = 10
self._current_layer_num = 10
self._current_layer_mesh = None
self._current_layer_jumps = None
self._activity = False
self._solid_layers = 5
@ -47,18 +52,15 @@ class LayerView(View):
def resetLayerData(self):
self._current_layer_mesh = None
self._current_layer_jumps = None
def beginRendering(self):
scene = self.getController().getScene()
renderer = self.getRenderer()
renderer.setRenderSelection(False)
if not self._material:
self._material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "vertexcolor.frag"))
self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0])
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))
if not self._selection_shader:
self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
self._selection_shader.setUniformValue("u_color", Color(32, 32, 32, 128))
for node in DepthFirstIterator(scene.getRoot()):
# 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 node.getMeshData() and node.isVisible():
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")
if not layer_data:
continue
@ -85,7 +87,7 @@ class LayerView(View):
end += counts
# 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
if not self._current_layer_mesh:
@ -100,15 +102,38 @@ class LayerView(View):
continue
except:
continue
self._current_layer_mesh.addVertices(layer_mesh.getVertices())
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())
# 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_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):
if self._current_layer_num != value:
@ -119,6 +144,7 @@ class LayerView(View):
self._current_layer_num = self._max_layers
self._current_layer_mesh = None
self._current_layer_jumps = None
self.currentLayerNumChanged.emit()
currentLayerNumChanged = Signal()
@ -126,31 +152,27 @@ class LayerView(View):
def calculateMaxLayers(self):
scene = self.getController().getScene()
renderer = self.getRenderer()
if renderer and self._material:
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
self._activity = True
if new_max_layers < len(layer_data.getLayers()):
new_max_layers = len(layer_data.getLayers()) - 1
self._old_max_layers = self._max_layers
## 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:
self._max_layers = new_max_layers
self.maxLayersChanged.emit()
self._current_layer_num = self._max_layers
if new_max_layers < len(layer_data.getLayers()):
new_max_layers = len(layer_data.getLayers()) - 1
# This makes sure we update the current layer
self.setLayer(int(self._max_layers))
self.currentLayerNumChanged.emit()
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
self._max_layers = new_max_layers
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()
currentLayerNumChanged = Signal()

View file

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

View file

@ -1,6 +1,8 @@
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
from UM.Application import Application
import LayerView
class LayerViewProxy(QObject):
def __init__(self, parent = None):
super().__init__(parent)
@ -52,4 +54,4 @@ class LayerViewProxy(QObject):
active_view = self._controller.getActiveView()
if type(active_view) == LayerView.LayerView.LayerView:
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.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")
if not gcode_writer:
Logger.log("e", "Could not find GCode writer, not writing to removable drive %s", self.getName())
raise OutputDeviceError.WriteRequestFailedError()
file_name = None
for n in BreadthFirstIterator(node):
if n.getMeshData():
file_name = n.getName()
if file_name:
break
if file_name == None:
for n in BreadthFirstIterator(node):
if n.getMeshData():
file_name = n.getName()
if file_name:
break
if not file_name:
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.show()
self.writeStarted.emit(self)
job._message = message
self._writing = True
job.start()
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
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
def _onProgress(self, job, progress):
@ -68,6 +78,8 @@ class RemovableDriveOutputDevice(OutputDevice):
if hasattr(job, "_message"):
job._message.hide()
job._message = None
self._writing = False
self.writeFinished.emit(self)
if job.getResult():
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()
def ejectDevice(self, device):
result = self.performEjectDevice(device)
try:
result = self.performEjectDevice(device)
except Exception as e:
result = False
if result:
message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName()))
message.show()

View file

@ -20,17 +20,17 @@ catalog = i18nCatalog("cura")
# WinAPI Constants that we need
# 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_WRITE = 1073741824
GENERIC_READ = 2147483648 # [CodeStyle: Windows Enum value]
GENERIC_WRITE = 1073741824 # [CodeStyle: Windows Enum value]
FILE_SHARE_READ = 1
FILE_SHARE_WRITE = 2
FILE_SHARE_READ = 1 # [CodeStyle: Windows Enum value]
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
class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
@ -65,11 +65,11 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
continue
# 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)
if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(freeBytes), None, None) == 0:
free_bytes = ctypes.c_longlong(0)
if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(free_bytes), None, None) == 0:
continue
if freeBytes.value < 1:
if free_bytes.value < 1:
continue
drives[drive] = "{0} ({1}:)".format(volume_name, letter)
@ -88,13 +88,10 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
result = None
# Then, try and tell it to eject
try:
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:
if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None):
result = False
else:
result = True
# Finally, close the handle
windll.kernel32.CloseHandle(handle)

View file

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

View file

@ -11,6 +11,7 @@ from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from UM.Qt.ListModel import ListModel
from UM.Message import Message
from cura.CuraApplication import CuraApplication
@ -54,6 +55,16 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
addConnectionSignal = Signal()
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):
self._check_updates = True
self._update_thread.start()
@ -84,12 +95,18 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
self._firmware_view.show()
@pyqtSlot()
def updateAllFirmware(self):
if not self._printer_connections:
Message("Cannot update firmware, there were no connected printers found.").show()
return
self.spawnFirmwareInterface("")
for printer_connection in self._printer_connections:
try:
self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
except FileNotFoundError:
Logger.log("w", "No firmware found for printer %s", printer_connection)
continue
@pyqtSlot(str, result = bool)
@ -148,11 +165,22 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
continue
self._serial_port_list = list(serial_ports)
connections_to_remove = []
for port, connection in self._printer_connections.items():
if port not in self._serial_port_list:
connection.close()
connections_to_remove.append(port)
for port in connections_to_remove:
del self._printer_connections[port]
## Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
def addConnection(self, serial_port):
connection = PrinterConnection.PrinterConnection(serial_port)
connection.connect()
connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
connection.progressChanged.connect(self.progressChanged)
self._printer_connections[serial_port] = connection
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/*")
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.
"""
avrChipDB = {
'ATMega1280': {
'signature': [0x1E, 0x97, 0x03],
'pageSize': 128,
'pageCount': 512,
avr_chip_db = {
"ATMega1280": {
"signature": [0x1E, 0x97, 0x03],
"pageSize": 128,
"pageCount": 512,
},
'ATMega2560': {
'signature': [0x1E, 0x98, 0x01],
'pageSize': 128,
'pageCount': 1024,
"ATMega2560": {
"signature": [0x1E, 0x98, 0x01],
"pageSize": 128,
"pageCount": 1024,
},
}
def getChipFromDB(sig):
for chip in avrChipDB.values():
if chip['signature'] == sig:
for chip in avr_chip_db.values():
if chip["signature"] == sig:
return chip
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.
"""
data = []
extraAddr = 0
extra_addr = 0
f = io.open(filename, "r")
for line in f:
line = line.strip()
if len(line) < 1:
continue
if line[0] != ':':
if line[0] != ":":
raise Exception("Hex file has a line not starting with ':'")
recLen = int(line[1:3], 16)
addr = int(line[3:7], 16) + extraAddr
recType = int(line[7:9], 16)
if len(line) != recLen * 2 + 11:
rec_len = int(line[1:3], 16)
addr = int(line[3:7], 16) + extra_addr
rec_type = int(line[7:9], 16)
if len(line) != rec_len * 2 + 11:
raise Exception("Error in hex file: " + line)
checkSum = 0
for i in range(0, recLen + 5):
checkSum += int(line[i*2+1:i*2+3], 16)
checkSum &= 0xFF
if checkSum != 0:
check_sum = 0
for i in range(0, rec_len + 5):
check_sum += int(line[i*2+1:i*2+3], 16)
check_sum &= 0xFF
if check_sum != 0:
raise Exception("Checksum error in hex file: " + line)
if recType == 0:#Data record
while len(data) < addr + recLen:
if rec_type == 0:#Data record
while len(data) < addr + rec_len:
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)
elif recType == 1: #End Of File record
elif rec_type == 1: #End Of File record
pass
elif recType == 2: #Extended Segment Address Record
extraAddr = int(line[9:13], 16) * 16
elif rec_type == 2: #Extended Segment Address Record
extra_addr = int(line[9:13], 16) * 16
else:
print(recType, recLen, addr, checkSum, line)
print(rec_type, rec_len, addr, check_sum, line)
f.close()
return data

View file

@ -14,18 +14,18 @@ class IspBase():
Base class for ISP based AVR programmers.
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. """
self.curExtAddr = -1
self.cur_ext_addr = -1
self.chip = chipDB.getChipFromDB(self.getSignature())
if not self.chip:
raise IspError("Chip with signature: " + str(self.getSignature()) + "not found")
self.chipErase()
print("Flashing %i bytes" % len(flashData))
self.writeFlash(flashData)
print("Verifying %i bytes" % len(flashData))
self.verifyFlash(flashData)
print("Flashing %i bytes" % len(flash_data))
self.writeFlash(flash_data)
print("Verifying %i bytes" % len(flash_data))
self.verifyFlash(flash_data)
print("Completed")
def getSignature(self):
@ -45,20 +45,20 @@ class IspBase():
"""
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.
"""
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.
"""
raise IspError("Called undefined verifyFlash")
class IspError(BaseException):
class IspError(Exception):
def __init__(self, value):
self.value = value

View file

@ -19,10 +19,10 @@ class Stk500v2(ispBase.IspBase):
def __init__(self):
self.serial = None
self.seq = 1
self.lastAddr = -1
self.progressCallback = None
self.last_addr = -1
self.progress_callback = None
def connect(self, port = 'COM22', speed = 115200):
def connect(self, port = "COM22", speed = 115200):
if self.serial is not None:
self.close()
try:
@ -82,49 +82,49 @@ class Stk500v2(ispBase.IspBase):
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
page_size = self.chip['pageSize'] * 2
flashSize = page_size * self.chip['pageCount']
page_size = self.chip["pageSize"] * 2
flash_size = page_size * self.chip["pageCount"]
print("Writing flash")
if flashSize > 0xFFFF:
if flash_size > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
load_count = (len(flash_data) + page_size - 1) / page_size
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)])
if self.progressCallback is not None:
if self.progress_callback is not None:
if self._has_checksum:
self.progressCallback(i + 1, load_count)
self.progress_callback(i + 1, load_count)
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:
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])
checksum_recv = res[2] | (res[3] << 8)
checksum = 0
for d in flashData:
for d in flash_data:
checksum += d
checksum &= 0xFFFF
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:
#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']
if flashSize > 0xFFFF:
flash_size = self.chip["pageSize"] * 2 * self.chip["pageCount"]
if flash_size > 0xFFFF:
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
else:
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
loadCount = (len(flashData) + 0xFF) / 0x100
for i in range(0, int(loadCount)):
load_count = (len(flash_data) + 0xFF) / 0x100
for i in range(0, int(load_count)):
recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102]
if self.progressCallback is not None:
self.progressCallback(loadCount + i + 1, loadCount*2)
if self.progress_callback is not None:
self.progress_callback(load_count + i + 1, load_count*2)
for j in range(0, 0x100):
if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]:
raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + 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))
def sendMessage(self, data):
message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E)
@ -138,12 +138,12 @@ class Stk500v2(ispBase.IspBase):
self.serial.write(message)
self.serial.flush()
except SerialTimeoutException:
raise ispBase.IspError('Serial send timeout')
raise ispBase.IspError("Serial send timeout")
self.seq = (self.seq + 1) & 0xFF
return self.recvMessage()
def recvMessage(self):
state = 'Start'
state = "Start"
checksum = 0
while True:
s = self.serial.read()
@ -152,31 +152,31 @@ class Stk500v2(ispBase.IspBase):
b = struct.unpack(">B", s)[0]
checksum ^= b
#print(hex(b))
if state == 'Start':
if state == "Start":
if b == 0x1B:
state = 'GetSeq'
state = "GetSeq"
checksum = 0x1B
elif state == 'GetSeq':
state = 'MsgSize1'
elif state == 'MsgSize1':
msgSize = b << 8
state = 'MsgSize2'
elif state == 'MsgSize2':
msgSize |= b
state = 'Token'
elif state == 'Token':
elif state == "GetSeq":
state = "MsgSize1"
elif state == "MsgSize1":
msg_size = b << 8
state = "MsgSize2"
elif state == "MsgSize2":
msg_size |= b
state = "Token"
elif state == "Token":
if b != 0x0E:
state = 'Start'
state = "Start"
else:
state = 'Data'
state = "Data"
data = []
elif state == 'Data':
elif state == "Data":
data.append(b)
if len(data) == msgSize:
state = 'Checksum'
elif state == 'Checksum':
if len(data) == msg_size:
state = "Checksum"
elif state == "Checksum":
if checksum != 0:
state = 'Start'
state = "Start"
else:
return data
@ -190,7 +190,7 @@ def portList():
values = _winreg.EnumValue(key, i)
except:
return ret
if 'USBSER' in values[0]:
if "USBSER" in values[0]:
ret.append(values[1])
i+=1
return ret
@ -205,7 +205,7 @@ def runProgrammer(port, filename):
def main():
""" Entry point to call the stk500v2 programmer from the commandline. """
import threading
if sys.argv[1] == 'AUTO':
if sys.argv[1] == "AUTO":
print(portList())
for port in portList():
threading.Thread(target=runProgrammer, args=(port,sys.argv[2])).start()
@ -216,5 +216,5 @@ def main():
programmer.programChip(intelHex.readHex(sys.argv[2]))
sys.exit(1)
if __name__ == '__main__':
if __name__ == "__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"
"Report-Msgid-Bugs-To: \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"
"Language-Team: \n"
"Language: fi_FI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:20
msgctxt "@title:window"
msgid "Oops!"
msgstr ""
msgstr "Hups!"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CrashHandler.py:26
msgctxt "@label"
@ -30,11 +30,14 @@ msgid ""
"below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/"
"issues\">http://github.com/Ultimaker/Cura/issues</a></p>"
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
msgctxt "@action:button"
msgid "Open Web Page"
msgstr ""
msgstr "Avaa verkkosivu"
#: /home/ahiemstra/dev/15.10/src/cura/cura/CuraApplication.py:135
#, fuzzy
@ -51,34 +54,32 @@ msgstr "Ladataan käyttöliittymää..."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:12
msgctxt "@label"
msgid "3MF Reader"
msgstr ""
msgstr "3MF Reader"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/3MFReader/__init__.py:15
msgctxt "@info:whatsthis"
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
#, fuzzy
msgctxt "@item:inlistbox"
msgid "3MF File"
msgstr "&Tiedosto"
msgstr "3MF-tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:12
#, fuzzy
msgctxt "@label"
msgid "Change Log"
msgstr "Moottorin loki"
msgstr "Muutosloki"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/ChangeLogPlugin/__init__.py:15
msgctxt "@info:whatsthis"
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
msgctxt "@label"
msgid "CuraEngine Backend"
msgstr ""
msgstr "CuraEngine-taustaosa"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/__init__.py:15
#, fuzzy
@ -90,25 +91,22 @@ msgstr "Linkki CuraEngine-viipalointiin taustalla"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py:111
msgctxt "@info:status"
msgid "Processing Layers"
msgstr ""
msgstr "Käsitellään kerroksia"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/CuraEngineBackend/CuraEngineBackend.py:169
#, fuzzy
msgctxt "@info:status"
msgid "Slicing..."
msgstr "Lasketaan..."
msgstr "Viipaloidaan..."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:12
#, fuzzy
msgctxt "@label"
msgid "GCode Writer"
msgstr "GCode-tiedosto"
msgstr "GCode Writer"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/GCodeWriter/__init__.py:15
#, fuzzy
msgctxt "@info:whatsthis"
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
#, fuzzy
@ -117,19 +115,16 @@ msgid "GCode File"
msgstr "GCode-tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:13
#, fuzzy
msgctxt "@label"
msgid "Layer View"
msgstr "Kerrokset"
msgstr "Kerrosnäkymä"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/LayerView/__init__.py:16
#, fuzzy
msgctxt "@info:whatsthis"
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
#, fuzzy
msgctxt "@item:inlistbox"
msgid "Layers"
msgstr "Kerrokset"
@ -137,28 +132,27 @@ msgstr "Kerrokset"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:20
msgctxt "@action:button"
msgid "Save to Removable Drive"
msgstr ""
msgstr "Tallenna siirrettävälle asemalle"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:21
#, fuzzy, python-brace-format
#, python-brace-format
msgctxt "@item:inlistbox"
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
#, python-brace-format
msgctxt "@info:progress"
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
#, fuzzy, python-brace-format
#, python-brace-format
msgctxt "@info:status"
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
#, fuzzy
msgctxt "@action:button"
msgid "Eject"
msgstr "Poista"
@ -167,96 +161,98 @@ msgstr "Poista"
#, python-brace-format
msgctxt "@action"
msgid "Eject removable device {0}"
msgstr ""
msgstr "Poista siirrettävä asema {0}"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:79
#, python-brace-format
msgctxt "@info:status"
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
msgctxt "@item:intext"
msgid "Removable Drive"
msgstr ""
msgstr "Siirrettävä asema"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py:42
#, python-brace-format
msgctxt "@info:status"
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
#, python-brace-format
msgctxt "@info:status"
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
msgctxt "@label"
msgid "Removable Drive Output Device Plugin"
msgstr ""
msgstr "Irrotettavan aseman lisäosa"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/RemovableDriveOutputDevice/__init__.py:14
msgctxt "@info:whatsthis"
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
msgctxt "@label"
msgid "Slice info"
msgstr ""
msgstr "Viipalointitiedot"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/SliceInfoPlugin/__init__.py:13
msgctxt "@info:whatsthis"
msgid "Submits anonymous slice info. Can be disabled through preferences."
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
msgctxt "@info"
msgid ""
"Cura automatically sends slice info. You can disable this in preferences"
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
msgctxt "@action:button"
msgid "Dismiss"
msgstr ""
msgstr "Ohita"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:35
msgctxt "@item:inmenu"
msgid "USB printing"
msgstr ""
msgstr "USB-tulostus"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:36
msgctxt "@action:button"
msgid "Print with USB"
msgstr ""
msgstr "Tulosta USB:llä"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/PrinterConnection.py:37
msgctxt "@info:tooltip"
msgid "Print with USB"
msgstr ""
msgstr "Tulostus USB:n kautta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:13
msgctxt "@label"
msgid "USB printing"
msgstr ""
msgstr "USB-tulostus"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/__init__.py:17
#, fuzzy
msgctxt "@info:whatsthis"
msgid ""
"Accepts G-Code and sends them to a printer. Plugin can also update firmware."
msgstr ""
"Hyväksyy G-Coden ja lähettää ne tulostimeen. Lisäosa voi myös päivittää "
"laiteohjelmiston"
"Hyväksyy G-Code-määrittelyt ja lähettää ne tulostimeen. Lisäosa voi myös "
"päivittää laiteohjelmiston."
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:46
#, fuzzy
msgctxt "@title:menu"
msgid "Firmware"
msgstr "Päivitä laiteohjelmisto"
msgstr "Laiteohjelmisto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/USBPrinterManager.py:47
#, fuzzy
@ -267,7 +263,7 @@ msgstr "Päivitä laiteohjelmisto"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:17
msgctxt "@title:window"
msgid "Print with USB"
msgstr ""
msgstr "Tulostus USB:n kautta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/ControlWindow.qml:28
#, fuzzy
@ -294,10 +290,9 @@ msgid "Cancel"
msgstr "Peruuta"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:20
#, fuzzy
msgctxt "@title:window"
msgid "Firmware Update"
msgstr "Laiteohjelmiston päivitys suoritettu."
msgstr "Laiteohjelmiston päivitys"
#: /home/ahiemstra/dev/15.10/src/cura/plugins/USBPrinting/FirmwareUpdateWindow.qml:38
#, fuzzy
@ -347,12 +342,12 @@ msgstr "Moottorin loki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:31
msgctxt "@label"
msgid "Variant:"
msgstr ""
msgstr "Variantti:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/ProfileSetup.qml:82
msgctxt "@label"
msgid "Global Profile:"
msgstr ""
msgstr "Yleisprofiili:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:60
#, fuzzy
@ -370,31 +365,29 @@ msgstr "Tulostimen nimi:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:29
msgctxt "@title"
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/UpgradeFirmware.qml:29
#, fuzzy
msgctxt "@title"
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/UltimakerCheckup.qml:37
#, fuzzy
msgctxt "@title"
msgid "Check Printer"
msgstr "Lisää tulostin"
msgstr "Tarkista tulostin"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/AddMachine.qml:220
msgctxt "@title"
msgid "Bed Levelling"
msgstr ""
msgstr "Pöydän tasaaminen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:25
msgctxt "@title"
msgid "Bed Leveling"
msgstr ""
msgstr "Pöydän tasaaminen"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:34
msgctxt "@label"
@ -403,6 +396,8 @@ msgid ""
"buildplate. When you click 'Move to Next Position' the nozzle will move to "
"the different positions that can be adjusted."
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
msgctxt "@label"
@ -411,16 +406,19 @@ msgid ""
"print bed height. The print bed height is right when the paper is slightly "
"gripped by the tip of the nozzle."
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
msgctxt "@action:button"
msgid "Move to Next Position"
msgstr ""
msgstr "Siirry seuraavaan positioon"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/Bedleveling.qml:66
msgctxt "@action:button"
msgid "Skip Bedleveling"
msgstr ""
msgstr "Ohita pöydän tasaus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:39
msgctxt "@label"
@ -428,27 +426,28 @@ msgid ""
"To assist you in having better default settings for your Ultimaker. Cura "
"would like to know which upgrades you have in your machine:"
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
#, fuzzy
msgctxt "@option:check"
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
msgctxt "@option:check"
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
msgctxt "@option:check"
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
msgctxt "@option:check"
msgid "Dual extrusion (experimental)"
msgstr ""
msgstr "Kaksoispursotus (kokeellinen)"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/SelectUpgradedParts.qml:70
msgctxt "@label"
@ -458,6 +457,11 @@ msgid ""
"improve reliability. This upgrade can be bought from the Ultimaker webshop "
"or found on thingiverse as thing:26094"
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
msgctxt "@label"
@ -465,36 +469,38 @@ msgid ""
"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"
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
msgctxt "@action:button"
msgid "Start Printer Check"
msgstr ""
msgstr "Aloita tulostintarkistus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:56
msgctxt "@action:button"
msgid "Skip Printer Check"
msgstr ""
msgstr "Ohita tulostintarkistus"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:65
msgctxt "@label"
msgid "Connection: "
msgstr ""
msgstr "Yhteys:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69
msgctxt "@info:status"
msgid "Done"
msgstr ""
msgstr "Valmis"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:69
msgctxt "@info:status"
msgid "Incomplete"
msgstr ""
msgstr "Kesken"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:76
msgctxt "@label"
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:91
@ -503,7 +509,7 @@ msgstr ""
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:201
msgctxt "@info:status"
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:91
@ -512,40 +518,39 @@ msgstr ""
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:163
msgctxt "@info:status"
msgid "Not checked"
msgstr ""
msgstr "Ei tarkistettu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:87
msgctxt "@label"
msgid "Min endstop Y: "
msgstr ""
msgstr "Min. päätyraja Y:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:99
msgctxt "@label"
msgid "Min endstop Z: "
msgstr ""
msgstr "Min. päätyraja Z:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:111
msgctxt "@label"
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:149
msgctxt "@action:button"
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:154
msgctxt "@info:progress"
msgid "Checking"
msgstr ""
msgstr "Tarkistetaan"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/WizardPages/UltimakerCheckup.qml:141
#, fuzzy
msgctxt "@label"
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
msgctxt "@label"
@ -554,6 +559,9 @@ msgid ""
"firmware controls the step motors, regulates the temperature and ultimately "
"makes your printer work."
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
msgctxt "@label"
@ -561,6 +569,8 @@ msgid ""
"The firmware shipping with new Ultimakers works, but upgrades have been made "
"to make better prints, and make calibration easier."
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
msgctxt "@label"
@ -568,37 +578,36 @@ msgid ""
"Cura requires these new features and thus your firmware will most likely "
"need to be upgraded. You can do so now."
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
#, fuzzy
msgctxt "@action:button"
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
msgctxt "@action:button"
msgid "Skip Upgrade"
msgstr ""
msgstr "Ohita päivitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:15
#, fuzzy
msgctxt "@title:window"
msgid "About Cura"
msgstr "Tietoja Curasta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:54
#, fuzzy
msgctxt "@label"
msgid "End-to-end solution for fused filament 3D printing."
msgstr "Kokonaisvaltainen sulatettavan tulostuslangan 3D-tulostusratkaisu."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/AboutDialog.qml:66
#, fuzzy
msgctxt "@info:credit"
msgid ""
"Cura has been developed by Ultimaker B.V. in cooperation with the community."
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
#, fuzzy
@ -618,11 +627,13 @@ msgid ""
"Highlight unsupported areas of the model in red. Without support these areas "
"will nog print properly."
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
msgctxt "@action:button"
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
msgctxt "@info:tooltip"
@ -630,11 +641,13 @@ msgid ""
"Moves the camera so the object is in the center of the view when an object "
"is selected"
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
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr ""
msgstr "Vaihda &koko näyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:58
#, fuzzy
@ -655,10 +668,9 @@ msgid "&Quit"
msgstr "&Lopeta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:82
#, fuzzy
msgctxt "@action:inmenu"
msgid "&Preferences..."
msgstr "&Suosikkiasetukset..."
msgstr "&Lisäasetukset..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:89
#, fuzzy
@ -667,15 +679,14 @@ msgid "&Add Printer..."
msgstr "L&isää tulostin..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:95
#, fuzzy
msgctxt "@action:inmenu"
msgid "Manage Pr&inters..."
msgstr "L&isää tulostin..."
msgstr "Tulostinten &hallinta..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:102
msgctxt "@action:inmenu"
msgid "Manage Profiles..."
msgstr ""
msgstr "Profiilien hallinta..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:109
#, fuzzy
@ -696,74 +707,64 @@ msgid "&About..."
msgstr "Ti&etoja..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:130
#, fuzzy
msgctxt "@action:inmenu"
msgid "Delete &Selection"
msgstr "Poista valinta"
msgstr "&Poista valinta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:138
#, fuzzy
msgctxt "@action:inmenu"
msgid "Delete Object"
msgstr "Poista kohde"
msgstr "Poista kappale"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:146
#, fuzzy
msgctxt "@action:inmenu"
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
msgctxt "@action:inmenu"
msgid "&Group Objects"
msgstr ""
msgstr "&Ryhmitä kappaleet"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:160
msgctxt "@action:inmenu"
msgid "Ungroup Objects"
msgstr ""
msgstr "Pura kappaleiden ryhmitys"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:168
#, fuzzy
msgctxt "@action:inmenu"
msgid "&Merge Objects"
msgstr "Poista kohde"
msgstr "&Yhdistä kappaleet"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:176
#, fuzzy
msgctxt "@action:inmenu"
msgid "&Duplicate Object"
msgstr "Monista kohde"
msgstr "&Monista kappale"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:183
#, fuzzy
msgctxt "@action:inmenu"
msgid "&Clear Build Platform"
msgstr "Tyhjennä alusta"
msgstr "&Tyhjennä alusta"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:190
#, fuzzy
msgctxt "@action:inmenu"
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
#, fuzzy
msgctxt "@action:inmenu"
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
#, fuzzy
msgctxt "@action:inmenu"
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
#, fuzzy
msgctxt "@action:inmenu"
msgid "&Open File..."
msgstr "Avaa tiedosto"
msgstr "&Avaa tiedosto..."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Actions.qml:217
#, fuzzy
@ -775,84 +776,87 @@ msgstr "Näytä moottorin l&oki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:461
msgctxt "@title:tab"
msgid "General"
msgstr ""
msgstr "Yleiset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:36
msgctxt "@label"
msgid "Language"
msgstr ""
msgstr "Kieli"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:47
msgctxt "@item:inlistbox"
msgid "Bulgarian"
msgstr ""
msgstr "bulgaria"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:48
msgctxt "@item:inlistbox"
msgid "Czech"
msgstr ""
msgstr "tsekki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:49
msgctxt "@item:inlistbox"
msgid "English"
msgstr ""
msgstr "englanti"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:50
msgctxt "@item:inlistbox"
msgid "Finnish"
msgstr ""
msgstr "suomi"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:51
msgctxt "@item:inlistbox"
msgid "French"
msgstr ""
msgstr "ranska"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:52
msgctxt "@item:inlistbox"
msgid "German"
msgstr ""
msgstr "saksa"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:53
msgctxt "@item:inlistbox"
msgid "Italian"
msgstr ""
msgstr "italia"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:54
msgctxt "@item:inlistbox"
msgid "Polish"
msgstr ""
msgstr "puola"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:55
msgctxt "@item:inlistbox"
msgid "Russian"
msgstr ""
msgstr "venäjä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:56
msgctxt "@item:inlistbox"
msgid "Spanish"
msgstr ""
msgstr "espanja"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:98
msgctxt "@label"
msgid ""
"You will need to restart the application for language changes to have effect."
msgstr ""
"Sovellus on käynnistettävä uudelleen, jotta kielimuutokset tulevat voimaan."
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:114
msgctxt "@option:check"
msgid "Ensure objects are kept apart"
msgstr ""
msgstr "Pidä kappaleet erillään"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:118
msgctxt "@info:tooltip"
msgid ""
"Should objects on the platform be moved so that they no longer intersect."
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
msgctxt "@option:check"
msgid "Send (Anonymous) Print Information"
msgstr ""
msgstr "Lähetä (anonyymit) tulostustiedot"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:151
msgctxt "@info:tooltip"
@ -861,103 +865,102 @@ msgid ""
"models, IP addresses or other personally identifiable information is sent or "
"stored."
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
msgctxt "@option:check"
msgid "Scale Too Large Files"
msgstr ""
msgstr "Skaalaa liian isot tiedostot"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/GeneralPage.qml:183
msgctxt "@info:tooltip"
msgid ""
"Should opened files be scaled to the build volume when they are too large?"
msgstr ""
"Pitäisikö avoimia tiedostoja skaalata rakennustilavuuteen, jos ne ovat liian "
"isoja?"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:70
#, fuzzy
msgctxt "@label:textbox"
msgid "Printjob Name"
msgstr "Tulostimen nimi:"
msgstr "Tulostustyön nimi"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:167
msgctxt "@label %1 is length of filament"
msgid "%1 m"
msgstr ""
msgstr "%1 m"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SaveButton.qml:229
msgctxt "@info:tooltip"
msgid "Select the active output device"
msgstr ""
msgstr "Valitse aktiivinen oheislaite"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:34
msgctxt "@label"
msgid "Infill:"
msgstr ""
msgstr "Täyttö:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:119
msgctxt "@label"
msgid "Sparse"
msgstr ""
msgstr "Harva"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:121
msgctxt "@label"
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
msgctxt "@label"
msgid "Dense"
msgstr ""
msgstr "Tiheä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:127
msgctxt "@label"
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
msgctxt "@label"
msgid "Solid"
msgstr ""
msgstr "Kiinteä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:133
msgctxt "@label"
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
#, fuzzy
msgctxt "@label:listbox"
msgid "Helpers:"
msgstr "&Ohje"
msgstr "Avustimet:"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:169
msgctxt "@option:check"
msgid "Enable Skirt Adhesion"
msgstr ""
msgstr "Ota helman tarttuvuus käyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarSimple.qml:187
#, fuzzy
msgctxt "@option:check"
msgid "Enable Support"
msgstr "Ota tuki käyttöön"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:123
#, fuzzy
msgctxt "@title:tab"
msgid "Simple"
msgstr "Perusasetukset"
msgstr "Suppea"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Sidebar.qml:124
#, fuzzy
msgctxt "@title:tab"
msgid "Advanced"
msgstr "Lisäasetukset"
msgstr "Laajennettu"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:30
#, fuzzy
msgctxt "@label:listbox"
msgid "Print Setup"
msgstr "Tulostimen asennus"
msgstr "Tulostusasetukset"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/SidebarHeader.qml:100
#, fuzzy
@ -980,18 +983,17 @@ msgstr "&Tiedosto"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:43
msgctxt "@title:menu"
msgid "Open &Recent"
msgstr ""
msgstr "Avaa &viimeisin"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:72
msgctxt "@action:inmenu"
msgid "&Save Selection to File"
msgstr ""
msgstr "&Tallenna valinta tiedostoon"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:80
#, fuzzy
msgctxt "@title:menu"
msgid "Save &All"
msgstr "Tallenna tiedosto"
msgstr "Tallenna &kaikki"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:108
#, fuzzy
@ -1000,10 +1002,9 @@ msgid "&Edit"
msgstr "&Muokkaa"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:125
#, fuzzy
msgctxt "@title:menu"
msgid "&View"
msgstr "Näytä"
msgstr "&Näytä"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:151
#, fuzzy
@ -1014,7 +1015,7 @@ msgstr "&Laite"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:197
msgctxt "@title:menu"
msgid "&Profile"
msgstr ""
msgstr "&Profiili"
#: /home/ahiemstra/dev/15.10/src/cura/resources/qml/Cura.qml:224
#, 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

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

View file

@ -2,13 +2,13 @@
"id": "rigidbotbig",
"version": 1,
"name": "RigidBot",
"manufacturer": "Invent-A-Part",
"manufacturer": "Other",
"author": "RBC",
"platform": "rigidbot_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"machine_width": { "default": 254 },
"machine_depth": { "default": 254 },
@ -18,6 +18,8 @@
"machine_nozzle_size": { "default": 0.4,
"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_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 },
@ -26,14 +28,12 @@
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
"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": {
"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 },
"shell_thickness": { "default": 0.8 },
"wall_thickness": { "default": 0.8 },

View file

@ -2,13 +2,13 @@
"id": "rigidbotbig",
"version": 1,
"name": "RigidBotBig",
"manufacturer": "Invent-A-Part",
"manufacturer": "Other",
"author": "RBC",
"platform": "rigidbotbig_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"machine_width": { "default": 400 },
"machine_depth": { "default": 300 },
@ -16,6 +16,8 @@
"machine_heated_bed": { "default": true },
"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_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 },
@ -24,14 +26,12 @@
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
"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": {
"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 },
"shell_thickness": { "default": 0.8},
"wall_thickness": { "default": 0.8 },

View file

@ -2,12 +2,12 @@
"id": "bq_hephestos",
"version": 1,
"name": "BQ Prusa i3 Hephestos",
"manufacturer": "BQ",
"manufacturer": "Other",
"author": "BQ",
"platform": "hephestos_platform.stl",
"platform": "bq_hephestos_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"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 --"
},
@ -33,10 +33,8 @@
"default": "RepRap"
},
"machine_platform_offset": {
"default": [0, 100, 0]
}
},
"overrides": {
"default": [0, -82, 0]
},
"layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false },
"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",
"version": 1,
"name": "BQ Prusa i3 Hephestos XL",
"manufacturer": "BQ",
"manufacturer": "Other",
"author": "BQ",
"platform": "hephestos_platform.stl",
"platform": "bq_hephestos_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"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 --"
},
@ -33,10 +33,8 @@
"default": "RepRap"
},
"machine_platform_offset": {
"default": [0, 100, 0]
}
},
"overrides": {
"default": [0, -82, 0]
},
"layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false },
"shell_thickness": { "default": 1.0 },

View file

@ -2,12 +2,12 @@
"id": "bq_witbox",
"version": 1,
"name": "BQ Witbox",
"manufacturer": "BQ",
"manufacturer": "Other",
"author": "BQ",
"platform": "witbox_platform.stl",
"platform": "bq_witbox_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"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 --"
},
@ -34,9 +34,7 @@
},
"machine_platform_offset": {
"default": [0, -145, -38]
}
},
"overrides": {
},
"layer_height": { "default": 0.2 },
"layer_height_0": { "default": 0.2, "visible": false },
"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,
"name": "German RepRap Neo",
"manufacturer": "Other",
"author": "other",
"author": "Other",
"icon": "icon_ultimaker.png",
"platform": "grr_neo_platform.stl",
"inherits": "fdmprinter.json",
"visible": "true",
"machine_settings": {
"overrides": {
"machine_width": { "default": 150 },
"machine_height": { "default": 150 },
"machine_depth": { "default": 150 },
"machine_center_is_zero": { "default": false },
"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_y": { "default": 18 },
"machine_head_shape_max_x": { "default": 18 },
@ -28,10 +30,8 @@
},
"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"
}
},
},
"overrides": {
"material_bed_temperature": { "visible": false }
}
}

View file

@ -9,7 +9,7 @@
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"machine_width": { "default": 210 },
"machine_depth": { "default": 185 },
"machine_height": { "default": 200 },
@ -17,6 +17,8 @@
"machine_center_is_zero": { "default": false },
"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_y": { "default": 0 },
"machine_head_shape_max_x": { "default": 0 },
@ -28,10 +30,8 @@
"machine_nozzle_tip_outer_diameter": { "default": 1.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_0": { "default": 0.2, "visible": false },
"wall_line_count": { "default": 2, "visible": true },

View file

@ -2,19 +2,22 @@
"id": "prusa_i3",
"version": 1,
"name": "Prusa i3",
"manufacturer": "Prusa",
"author": "other",
"manufacturer": "Other",
"author": "Other",
"icon": "icon_ultimaker2.png",
"platform": "prusai3_platform.stl",
"inherits": "fdmprinter.json",
"machine_settings": {
"overrides": {
"machine_heated_bed": { "default": true },
"machine_width": { "default": 200 },
"machine_height": { "default": 200 },
"machine_depth": { "default": 200 },
"machine_center_is_zero": { "default": false },
"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_y": { "default": 18 },
"machine_head_shape_max_x": { "default": 18 },
@ -28,9 +31,5 @@
"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"
}
},
"overrides": {
"material_bed_temperature": { "visible": true }
}
}

View file

@ -11,10 +11,13 @@
"inherits": "fdmprinter.json",
"machine_extruder_trains": [
{
"machine_nozzle_size": {
"default": 0.4
"machine_extruder_trains": {
"0": {
"machine_nozzle_heat_up_speed": {
"default": 2.0
},
"machine_nozzle_cool_down_speed": {
"default": 2.0
},
"machine_nozzle_tip_outer_diameter": {
"default": 1
@ -29,8 +32,8 @@
"default": 16
}
}
],
"machine_settings": {
},
"overrides": {
"machine_start_gcode" : { "default": "" },
"machine_end_gcode" : { "default": "" },
"machine_width": { "default": 230 },
@ -42,25 +45,27 @@
{
"default": [
[
-40,
30
-42,
12
],
[
-40,
-10
-42,
-32
],
[
60,
-10
62,
12
],
[
60,
30
62,
-32
]
]
},
"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 },
"machine_use_extruder_offset_to_offset_coords": { "default": true },
"machine_gcode_flavor": { "default": "UltiGCode" },
@ -74,10 +79,8 @@
"machine_nozzle_tip_outer_diameter": { "default": 1.0 },
"machine_nozzle_head_distance": { "default": 3.0 },
"machine_nozzle_expansion_angle": { "default": 45 }
},
"overrides": {
"machine_nozzle_expansion_angle": { "default": 45 },
"material_print_temperature": { "enabled": "False" },
"material_bed_temperature": { "enabled": "False" },
"material_diameter": { "enabled": "False" },

View file

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

View file

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

View file

@ -16,10 +16,13 @@
"BedLeveling"
],
"machine_extruder_trains": [
{
"machine_nozzle_size": {
"default": 0.4
"machine_extruder_trains": {
"0": {
"machine_nozzle_heat_up_speed": {
"default": 2.0
},
"machine_nozzle_cool_down_speed": {
"default": 2.0
},
"machine_nozzle_tip_outer_diameter": {
"default": 1
@ -34,13 +37,15 @@
"default": 16
}
}
],
"machine_settings": {
},
"overrides": {
"machine_width": { "default": 205 },
"machine_height": { "default": 200 },
"machine_depth": { "default": 205 },
"machine_center_is_zero": { "default": false },
"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":
{
"default": [
@ -71,10 +76,8 @@
},
"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"
}
},
},
"overrides": {
"material_bed_temperature": { "visible": false }
"machine_extruder_drive_upgrade": { "default": false }
}
}

View file

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

View file

@ -22,6 +22,19 @@ UM.MainWindow
id: backgroundItem;
anchors.fill: parent;
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
{
id: menu
@ -72,7 +85,7 @@ UM.MainWindow
text: catalog.i18nc("@action:inmenu", "&Save Selection to File");
enabled: UM.Selection.hasSelection;
iconName: "document-save-as";
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file");
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", Printer.jobName);
}
Menu
{
@ -88,7 +101,7 @@ UM.MainWindow
MenuItem
{
text: model.description;
onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id);
onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, Printer.jobName);
}
onObjectAdded: saveAllMenu.insertItem(index, object)
onObjectRemoved: saveAllMenu.removeItem(object)
@ -139,10 +152,6 @@ UM.MainWindow
onObjectRemoved: top_view_menu.removeItem(object)
}
ExclusiveGroup { id: view_menu_top_group; }
MenuSeparator { }
MenuItem { action: actions.toggleFullScreen; }
}
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
{
anchors
{
horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: -(UM.Theme.sizes.logo.width/ 2)
top: parent.verticalCenter;
bottom: parent.bottom;
horizontalCenterOffset: -(UM.Theme.sizes.sidebar.width/ 2)
verticalCenter: parent.verticalCenter;
}
}
@ -321,8 +340,7 @@ UM.MainWindow
//anchors.bottom: parent.bottom
anchors.top: viewModeButton.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height;
anchors.right: sidebar.left;
anchors.rightMargin: UM.Theme.sizes.window_margin.width;
anchors.left: viewModeButton.left;
//anchors.bottom: buttons.top;
//anchors.bottomMargin: UM.Theme.sizes.default_margin.height;
@ -335,10 +353,9 @@ UM.MainWindow
{
id: openFileButton;
//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");
iconSource: UM.Theme.icons.load
style: UM.Theme.styles.open_file_button
style: UM.Theme.styles.tool_button
tooltip: '';
anchors
{
@ -364,6 +381,7 @@ UM.MainWindow
source: UM.Theme.images.logo;
width: UM.Theme.sizes.logo.width;
height: UM.Theme.sizes.logo.height;
z: -1;
sourceSize.width: width;
sourceSize.height: height;
@ -372,12 +390,12 @@ UM.MainWindow
Button
{
id: viewModeButton
property bool verticalTooltip: true
anchors
{
top: parent.top;
right: sidebar.left;
rightMargin: UM.Theme.sizes.window_margin.width;
top: toolbar.bottom;
topMargin: UM.Theme.sizes.window_margin.height;
left: parent.left;
}
text: catalog.i18nc("@action:button","View Mode");
iconSource: UM.Theme.icons.viewmode;
@ -389,12 +407,13 @@ UM.MainWindow
id: viewMenu;
Instantiator
{
id: viewMenuInstantiator
model: UM.ViewModel { }
MenuItem
{
text: model.name;
text: model.name
checkable: true;
checked: model.active;
checked: model.active
exclusiveGroup: viewMenuGroup;
onTriggered: UM.Controller.setActiveView(model.id);
}
@ -411,12 +430,9 @@ UM.MainWindow
id: toolbar;
anchors {
left: parent.left
top: parent.top
topMargin: 74
//horizontalCenter: parent.horizontalCenter
//horizontalCenterOffset: -(UM.Theme.sizes.sidebar.width / 2)
//top: parent.top;
top: openFileButton.bottom;
topMargin: UM.Theme.sizes.window_margin.height;
left: parent.left;
}
}
@ -463,14 +479,27 @@ UM.MainWindow
{
//; Remove & re-add the general page as we want to use our own instead of uranium standard.
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
insertPage(1, catalog.i18nc("@title:tab","View"), "view-preview", Qt.resolvedUrl("./ViewPage.qml"));
insertPage(1, catalog.i18nc("@title:tab","View"), viewPage);
//Force refresh
setPage(0)
}
Item {
visible: false
GeneralPage
{
id: generalPage
}
ViewPage
{
id: viewPage
}
}
}
Actions
@ -543,8 +572,8 @@ UM.MainWindow
addMachine.onTriggered: addMachineWizard.visible = true;
preferences.onTriggered: preferences.visible = true;
configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(2); }
preferences.onTriggered: { preferences.visible = true; preferences.setPage(0); }
configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(3); }
manageProfiles.onTriggered: { preferences.visible = true; preferences.setPage(4); }
documentation.onTriggered: CuraActions.openDocumentation();
@ -605,7 +634,7 @@ UM.MainWindow
id: openDialog;
//: 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;
//TODO: Support multiple file selection, workaround bug in KDE file dialog
//selectMultiple: true
@ -621,6 +650,11 @@ UM.MainWindow
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)
openDialog.sendMeshName(fileUrl.toString())
}
@ -648,14 +682,34 @@ UM.MainWindow
onRequestAddPrinter:
{
addMachineWizard.visible = true
addMachineWizard.firstRun = true
addMachineWizard.firstRun = false
}
}
Component.onCompleted:
{
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
title: catalog.i18nc("@title:tab","General");
function setDefaultLanguage(languageCode)
{
//loops trough the languageList and sets the language using the languageCode
for(var i = 0; i < languageList.count; i++)
{
if (languageComboBox.model.get(i).code == languageCode)
{
languageComboBox.currentIndex = i
}
}
}
function reset()
{
UM.Preferences.resetPreference("general/language")
@ -22,7 +34,8 @@ UM.PreferencesPage
pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free"))
sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info"))
scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit"))
languageComboBox.currentIndex = 0
var defaultLanguage = UM.Preferences.getValue("general/language")
setDefaultLanguage(defaultLanguage)
}
GridLayout
@ -44,16 +57,16 @@ UM.PreferencesPage
id: languageList
Component.onCompleted: {
append({ text: catalog.i18nc("@item:inlistbox", "Bulgarian"), code: "bg" })
append({ text: catalog.i18nc("@item:inlistbox", "Czech"), code: "cs" })
// append({ text: catalog.i18nc("@item:inlistbox", "Bulgarian"), code: "bg" })
// append({ text: catalog.i18nc("@item:inlistbox", "Czech"), code: "cs" })
append({ text: catalog.i18nc("@item:inlistbox", "English"), code: "en" })
append({ text: catalog.i18nc("@item:inlistbox", "Finnish"), code: "fi" })
append({ text: catalog.i18nc("@item:inlistbox", "French"), code: "fr" })
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", "Russian"), code: "ru" })
append({ text: catalog.i18nc("@item:inlistbox", "Spanish"), code: "es" })
// append({ text: catalog.i18nc("@item:inlistbox", "Russian"), code: "ru" })
// 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
Column{
Item{
id: base;
UM.I18nCatalog { id: catalog; name:"cura"}
property int totalHeightProfileSetup: childrenRect.height
property Action manageProfilesAction
spacing: 0
Rectangle{
id: variantItem;
height: UM.Theme.sizes.sidebar_setup.height
Rectangle {
id: variantRow
anchors.top: base.top
width: base.width
visible: UM.MachineManager.hasVariants;
height: UM.Theme.sizes.sidebar_setup.height
//visible: UM.MachineManager.hasVariants;
visible: true
Rectangle {
id: variantRow
width: base.width
height: parent.heigth
Label{
id: variantLabel
text: catalog.i18nc("@label","Variant:");
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{
id: variantLabel
text: catalog.i18nc("@label","Variant:");
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;
}
ToolButton {
id: variantSelection
text: UM.MachineManager.activeMachineVariant
width: parent.width/100*55
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
ToolButton {
id: variantSelection
text: UM.MachineManager.activeMachineVariant
width: parent.width/100*55
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
menu: Menu
{
id: variantsSelectionMenu
Instantiator
{
id: variantsSelectionMenu
Instantiator
model: UM.MachineVariantsModel { id: variantsModel }
MenuItem
{
model: UM.MachineVariantsModel { }
MenuItem
{
text: model.name;
checkable: true;
checked: model.active;
exclusiveGroup: variantSelectionMenuGroup;
onTriggered: UM.MachineManager.setActiveMachineVariant(model.getItem(index).name)
}
text: model.name;
checkable: true;
checked: model.active;
exclusiveGroup: variantSelectionMenuGroup;
onTriggered: UM.MachineManager.setActiveMachineVariant(variantsModel.getItem(index).name)
}
ExclusiveGroup { id: variantSelectionMenuGroup; }
onObjectAdded: variantsSelectionMenu.insertItem(index, object)
onObjectRemoved: variantsSelectionMenu.removeItem(object)
}
ExclusiveGroup { id: variantSelectionMenuGroup; }
}
}
}
Rectangle{
id: globalProfileRow;
anchors.top: UM.MachineManager.hasVariants ? variantRow.bottom : base.top
//anchors.top: variantRow.bottom
height: UM.Theme.sizes.sidebar_setup.height
width: base.width
@ -82,6 +81,7 @@ Column{
text: catalog.i18nc("@label","Global Profile:");
width: parent.width/100*45
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 {
id: base;
UM.I18nCatalog { id: catalog; name:"cura"}
property real progress: UM.Backend.progress;
property bool activity: Printer.getPlatformActivity;
Behavior on progress { NumberAnimation { duration: 250; } }
property int totalHeight: childrenRect.height
//Behavior on progress { NumberAnimation { duration: 250; } }
property int totalHeight: childrenRect.height + UM.Theme.sizes.default_margin.height
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;
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]
}
}
property string statusText: {
if(progress == 0) {
if(!activity) {
return catalog.i18nc("@label:PrintjobStatus","Please load a 3d model");
} else {
return catalog.i18nc("@label:PrintjobStatus","Preparing to slice...");
}
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 {
target: openDialog
onHasMesh: {
base.fileBaseName = name
base.createFileName()
}
Label {
id: statusLabel
width: parent.width - 2 * UM.Theme.sizes.default_margin.width
anchors.top: parent.top
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{
id: printJobRow
implicitWidth: base.width;
implicitHeight: UM.Theme.sizes.sidebar_header.height
anchors.top: parent.top
color: UM.Theme.colors.sidebar_header_bar
Label{
id: printJobTextfieldLabel
text: catalog.i18nc("@label:textbox", "Printjob Name");
anchors.left: parent.left
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;
}
}
}
}
id: progressBar
width: parent.width - 2 * UM.Theme.sizes.default_margin.width
height: UM.Theme.sizes.progressbar.height
anchors.top: statusLabel.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height/4
anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width
radius: UM.Theme.sizes.progressbar_radius.width
color: UM.Theme.colors.progressbar_background
Rectangle {
id: specsRow
implicitWidth: base.width
implicitHeight: UM.Theme.sizes.sidebar_specs_bar.height
anchors.top: printJobRow.bottom
Item{
id: time
width: childrenRect.width;
Rectangle{
width: Math.max(parent.width * base.progress)
height: parent.height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width
anchors.top: parent.top
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)
}
color: UM.Theme.colors.progressbar_control
radius: UM.Theme.sizes.progressbar_radius.width
visible: base.progress > 0.99 ? false : true
}
}
Rectangle{
id: saveRow
width: base.width
height: saveToButton.height + (UM.Theme.sizes.default_margin.height / 2) // height + bottomMargin
anchors.top: specsRow.bottom
height: saveToButton.height
anchors.top: progressBar.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.left: parent.left
Button {
id: saveToButton
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;
enabled: base.progress > 0.99 && base.activity == true
height: UM.Theme.sizes.save_button_save_to_button.height
@ -188,35 +83,32 @@ Rectangle {
text: UM.OutputDeviceManager.activeDeviceShortDescription
onClicked:
{
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice)
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, Printer.jobName)
}
style: ButtonStyle {
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; } }
width: {
var w = 0;
if (base.width*0.55 > actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)){
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;
saveToButton.resizedWidth = actualLabel.width + (UM.Theme.sizes.default_margin.width * 2)
return saveToButton.resizedWidth
}
Label {
id: actualLabel
//Behavior on opacity { NumberAnimation { duration: 50; } }
anchors.centerIn: parent
color: UM.Theme.colors.load_save_button_text
font: UM.Theme.fonts.default
color: !control.enabled ? UM.Theme.colors.action_button_disabled_text :
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;
}
}
@ -232,12 +124,18 @@ Rectangle {
anchors.rightMargin: UM.Theme.sizes.default_margin.width
width: 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];
style: ButtonStyle {
background: Rectangle {
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; } }
anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width / 2;
@ -245,15 +143,16 @@ Rectangle {
height: parent.height
UM.RecolorImage {
id: lengthIcon
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: UM.Theme.sizes.standard_arrow.width
height: UM.Theme.sizes.standard_arrow.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.colors.load_save_button_text
source: UM.Theme.icons.arrow_bottom
sourceSize.height: height
color: !control.enabled ? UM.Theme.colors.action_button_disabled_text :
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{ }

View file

@ -15,6 +15,7 @@ Rectangle
property Action addMachineAction;
property Action configureMachinesAction;
property Action manageProfilesAction;
property int currentModeIndex;
color: UM.Theme.colors.sidebar;
UM.I18nCatalog { id: catalog; name:"cura"}
@ -49,37 +50,118 @@ Rectangle
addMachineAction: base.addMachineAction;
configureMachinesAction: base.configureMachinesAction;
modesModel: modesListModel;
}
currentModeIndex:
{
var index = parseInt(UM.Preferences.getValue("cura/active_mode"))
if(index)
{
return index;
}
return 0;
}
onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex);
Rectangle {
id: headerSeparator
width: parent.width
height: UM.Theme.sizes.sidebar_lining.height
color: UM.Theme.colors.sidebar_lining
anchors.top: header.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
}
ProfileSetup {
id: profileItem
manageProfilesAction: base.manageProfilesAction
anchors.top: header.bottom
anchors.top: settingsModeSelection.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
width: parent.width
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
{
id: sidebarContents;
anchors.bottom: saveButton.top
anchors.bottom: footerSeparator.top
anchors.top: profileItem.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.left: base.left
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;
@ -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
{
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", "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;
// Machine Setup
property variant modesModel;
property int currentModeIndex: 0;
property Action addMachineAction;
property Action configureMachinesAction;
UM.I18nCatalog { id: catalog; name:"cura"}
@ -21,77 +19,29 @@ Item
Rectangle {
id: settingsModeRow
width: base.width
height: UM.Theme.sizes.sidebar_header.height
height: 0
anchors.top: parent.top
color: UM.Theme.colors.sidebar_header_bar
}
Label{
id: settingsModeLabel
text: catalog.i18nc("@label:listbox","Print Setup");
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;
color: UM.Theme.colors.text_white
}
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;
}
}
Label{
id: printjobTabLabel
text: catalog.i18nc("@label:listbox","Print Job");
anchors.left: parent.left
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
anchors.top: settingsModeRow.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: machineSelectionRow
width: base.width
height: UM.Theme.sizes.sidebar_header.height
anchors.top: settingsModeRow.bottom
height: UM.Theme.sizes.sidebar_setup.height
anchors.top: printjobTabLabel.bottom
anchors.topMargin: UM.Theme.sizes.default_margin.height
anchors.horizontalCenter: parent.horizontalCenter
Label{
@ -102,6 +52,7 @@ Item
anchors.leftMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter
font: UM.Theme.fonts.default;
color: UM.Theme.colors.text;
}
ToolButton {
@ -110,7 +61,6 @@ Item
width: parent.width/100*55
height: UM.Theme.sizes.setting_control.height
tooltip: UM.MachineManager.activeMachineInstance;
//style: UM.Theme.styles.sidebar_header_button;
anchors.right: parent.right
anchors.rightMargin: UM.Theme.sizes.default_margin.width
anchors.verticalCenter: parent.verticalCenter

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