mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-06-04 11:14:21 +08:00
Merge branch 'master' of https://github.com/Ultimaker/Cura into layerview_dev
This commit is contained in:
commit
c6790981d6
@ -1,7 +1,9 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Scene.Platform import Platform
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
@ -17,6 +19,30 @@ from UM.View.GL.OpenGL import OpenGL
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
import numpy
|
||||
import copy
|
||||
|
||||
|
||||
# Setting for clearance around the prime
|
||||
PRIME_CLEARANCE = 10
|
||||
|
||||
|
||||
def approximatedCircleVertices(r):
|
||||
"""
|
||||
Return vertices from an approximated circle.
|
||||
:param r: radius
|
||||
:return: numpy 2-array with the vertices
|
||||
"""
|
||||
|
||||
return numpy.array([
|
||||
[-r, 0],
|
||||
[-r * 0.707, r * 0.707],
|
||||
[0, r],
|
||||
[r * 0.707, r * 0.707],
|
||||
[r, 0],
|
||||
[r * 0.707, -r * 0.707],
|
||||
[0, -r],
|
||||
[-r * 0.707, -r * 0.707]
|
||||
], numpy.float32)
|
||||
|
||||
|
||||
## Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas.
|
||||
@ -41,6 +67,11 @@ class BuildVolume(SceneNode):
|
||||
self.setCalculateBoundingBox(False)
|
||||
self._volume_aabb = None
|
||||
|
||||
self._raft_thickness = 0.0
|
||||
self._adhesion_type = None
|
||||
self._raft_mesh = None
|
||||
self._platform = Platform(self)
|
||||
|
||||
self._active_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
@ -72,6 +103,9 @@ class BuildVolume(SceneNode):
|
||||
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, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
|
||||
if self._raft_mesh and self._adhesion_type == "raft":
|
||||
renderer.queueNode(self, mesh=self._raft_mesh, transparent=True, backface_cull=True, sort=-9)
|
||||
|
||||
return True
|
||||
|
||||
## Recalculates the build volume & disallowed areas.
|
||||
@ -88,6 +122,7 @@ class BuildVolume(SceneNode):
|
||||
|
||||
mb = MeshBuilder()
|
||||
|
||||
# Outline 'cube' of the build volume
|
||||
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)
|
||||
@ -112,11 +147,23 @@ class BuildVolume(SceneNode):
|
||||
Vector(max_w, min_h - 0.2, max_d),
|
||||
Vector(min_w, min_h - 0.2, max_d)
|
||||
)
|
||||
|
||||
for n in range(0, 6):
|
||||
v = mb.getVertex(n)
|
||||
mb.setVertexUVCoordinates(n, v[0], v[2])
|
||||
self._grid_mesh = mb.build()
|
||||
|
||||
# Build raft mesh: a plane on the height of the raft.
|
||||
mb = MeshBuilder()
|
||||
mb.addQuad(
|
||||
Vector(min_w, self._raft_thickness, min_d),
|
||||
Vector(max_w, self._raft_thickness, min_d),
|
||||
Vector(max_w, self._raft_thickness, max_d),
|
||||
Vector(min_w, self._raft_thickness, max_d),
|
||||
color=Color(128, 128, 128, 64)
|
||||
)
|
||||
self._raft_mesh = mb.build()
|
||||
|
||||
disallowed_area_height = 0.1
|
||||
disallowed_area_size = 0
|
||||
if self._disallowed_areas:
|
||||
@ -144,7 +191,9 @@ class BuildVolume(SceneNode):
|
||||
else:
|
||||
self._disallowed_area_mesh = None
|
||||
|
||||
self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
|
||||
self._volume_aabb = AxisAlignedBox(
|
||||
minimum = Vector(min_w, min_h - 1.0, min_d),
|
||||
maximum = Vector(max_w, max_h - self._raft_thickness, max_d))
|
||||
|
||||
skirt_size = 0.0
|
||||
|
||||
@ -157,7 +206,7 @@ class BuildVolume(SceneNode):
|
||||
# The +1 and -1 is added as there is always a bit of extra room required to work properly.
|
||||
scale_to_max_bounds = AxisAlignedBox(
|
||||
minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1),
|
||||
maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1)
|
||||
maximum = Vector(max_w - skirt_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + skirt_size - 1)
|
||||
)
|
||||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
@ -172,6 +221,21 @@ class BuildVolume(SceneNode):
|
||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||
" with printed objects."), lifetime=10).show()
|
||||
|
||||
def _updateRaftThickness(self):
|
||||
old_raft_thickness = self._raft_thickness
|
||||
self._adhesion_type = self._active_container_stack.getProperty("adhesion_type", "value")
|
||||
self._raft_thickness = 0.0
|
||||
if self._adhesion_type == "raft":
|
||||
self._raft_thickness = (
|
||||
self._active_container_stack.getProperty("raft_base_thickness", "value") +
|
||||
self._active_container_stack.getProperty("raft_interface_thickness", "value") +
|
||||
self._active_container_stack.getProperty("raft_surface_layers", "value") *
|
||||
self._active_container_stack.getProperty("raft_surface_thickness", "value") +
|
||||
self._active_container_stack.getProperty("raft_airgap", "value"))
|
||||
# Rounding errors do not matter, we check if raft_thickness has changed at all
|
||||
if old_raft_thickness != self._raft_thickness:
|
||||
self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World)
|
||||
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
@ -190,6 +254,7 @@ class BuildVolume(SceneNode):
|
||||
self._depth = self._active_container_stack.getProperty("machine_depth", "value")
|
||||
|
||||
self._updateDisallowedAreas()
|
||||
self._updateRaftThickness()
|
||||
|
||||
self.rebuild()
|
||||
|
||||
@ -197,40 +262,62 @@ class BuildVolume(SceneNode):
|
||||
if property_name != "value":
|
||||
return
|
||||
|
||||
rebuild_me = False
|
||||
if setting_key == "print_sequence":
|
||||
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
self._height = self._active_container_stack.getProperty("gantry_height", "value")
|
||||
self._buildVolumeMessage()
|
||||
else:
|
||||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
self.rebuild()
|
||||
rebuild_me = True
|
||||
|
||||
if setting_key in self._skirt_settings:
|
||||
self._updateDisallowedAreas()
|
||||
rebuild_me = True
|
||||
|
||||
if setting_key in self._raft_settings:
|
||||
self._updateRaftThickness()
|
||||
rebuild_me = True
|
||||
|
||||
if rebuild_me:
|
||||
self.rebuild()
|
||||
|
||||
def _updateDisallowedAreas(self):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
|
||||
disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value")
|
||||
disallowed_areas = copy.deepcopy(
|
||||
self._active_container_stack.getProperty("machine_disallowed_areas", "value"))
|
||||
areas = []
|
||||
|
||||
# Add extruder prime locations as disallowed areas.
|
||||
# Probably needs some rework after coordinate system change.
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
extruders = extruder_manager.getMachineExtruders(self._active_container_stack.getId())
|
||||
machine_width = self._active_container_stack.getProperty("machine_width", "value")
|
||||
machine_depth = self._active_container_stack.getProperty("machine_depth", "value")
|
||||
for single_extruder in extruders:
|
||||
extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
|
||||
extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
|
||||
# TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
|
||||
# Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
|
||||
# (center as origin, y from back to front)
|
||||
prime_x = extruder_prime_pos_x - machine_width / 2
|
||||
prime_y = machine_depth / 2 - extruder_prime_pos_y
|
||||
disallowed_areas.append([
|
||||
[prime_x - PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE],
|
||||
[prime_x + PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE],
|
||||
[prime_x + PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
|
||||
[prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
|
||||
])
|
||||
|
||||
skirt_size = self._getSkirtSize(self._active_container_stack)
|
||||
|
||||
if disallowed_areas:
|
||||
# Extend every area already in the disallowed_areas with the skirt size.
|
||||
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)))
|
||||
poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(skirt_size)))
|
||||
|
||||
areas.append(poly)
|
||||
|
||||
@ -269,7 +356,7 @@ class BuildVolume(SceneNode):
|
||||
|
||||
self._disallowed_areas = areas
|
||||
|
||||
## Convenience function to calculate the size of the bed adhesion.
|
||||
## Convenience function to calculate the size of the bed adhesion in directions x, y.
|
||||
def _getSkirtSize(self, container_stack):
|
||||
skirt_size = 0.0
|
||||
|
||||
@ -295,3 +382,4 @@ class BuildVolume(SceneNode):
|
||||
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"]
|
||||
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
|
||||
|
@ -157,7 +157,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
vertex_data = mesh.getConvexHullTransformedVertices(world_transform)
|
||||
# Don't use data below 0.
|
||||
# TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
||||
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
||||
vertex_data = vertex_data[vertex_data[:,1] >= -0.01]
|
||||
|
||||
if len(vertex_data) >= 4:
|
||||
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
|
||||
|
@ -4,7 +4,6 @@
|
||||
from UM.Qt.QtApplication import QtApplication
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Camera import Camera
|
||||
from UM.Scene.Platform import Platform as Scene_Platform
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.Quaternion import Quaternion
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
@ -126,6 +125,7 @@ class CuraApplication(QtApplication):
|
||||
)
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType)
|
||||
|
||||
@ -143,7 +143,6 @@ class CuraApplication(QtApplication):
|
||||
])
|
||||
self._physics = None
|
||||
self._volume = None
|
||||
self._platform = None
|
||||
self._output_devices = {}
|
||||
self._print_information = None
|
||||
self._previous_active_tool = None
|
||||
@ -195,6 +194,14 @@ class CuraApplication(QtApplication):
|
||||
Preferences.getInstance().addPreference("view/center_on_select", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True)
|
||||
|
||||
for key in [
|
||||
"dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin
|
||||
"dialog_profile_path",
|
||||
"dialog_material_path"]:
|
||||
|
||||
Preferences.getInstance().addPreference("local_file/%s" % key, "~/")
|
||||
|
||||
Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
|
||||
|
||||
Preferences.getInstance().setDefault("general/visible_settings", """
|
||||
@ -333,10 +340,15 @@ class CuraApplication(QtApplication):
|
||||
f.write(data)
|
||||
|
||||
|
||||
@pyqtSlot(result = QUrl)
|
||||
def getDefaultPath(self):
|
||||
return QUrl.fromLocalFile(os.path.expanduser("~/"))
|
||||
|
||||
@pyqtSlot(str, result = QUrl)
|
||||
def getDefaultPath(self, key):
|
||||
default_path = Preferences.getInstance().getValue("local_file/%s" % key)
|
||||
return QUrl.fromLocalFile(default_path)
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def setDefaultPath(self, key, default_path):
|
||||
Preferences.getInstance().setValue("local_file/%s" % key, default_path)
|
||||
|
||||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistery
|
||||
def _loadPlugins(self):
|
||||
@ -376,8 +388,8 @@ class CuraApplication(QtApplication):
|
||||
Selection.selectionChanged.connect(self.onSelectionChanged)
|
||||
|
||||
root = controller.getScene().getRoot()
|
||||
self._platform = Scene_Platform(root)
|
||||
|
||||
# The platform is a child of BuildVolume
|
||||
self._volume = BuildVolume.BuildVolume(root)
|
||||
|
||||
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
|
||||
@ -399,8 +411,7 @@ class CuraApplication(QtApplication):
|
||||
|
||||
# Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||
cura.Settings.ExtruderManager.getInstance()
|
||||
qmlRegisterSingletonType(cura.Settings.MachineManager, "Cura", 1, 0, "MachineManager",
|
||||
cura.Settings.MachineManager.createMachineManager)
|
||||
qmlRegisterSingletonType(cura.Settings.MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
|
||||
|
||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
||||
@ -419,6 +430,11 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self.exec_()
|
||||
|
||||
def getMachineManager(self, *args):
|
||||
if self._machine_manager is None:
|
||||
self._machine_manager = cura.Settings.MachineManager.createMachineManager()
|
||||
return self._machine_manager
|
||||
|
||||
## Get the machine action manager
|
||||
# We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
||||
# It wants to give this function an engine and script engine, but we don't care about that.
|
||||
@ -559,15 +575,18 @@ class CuraApplication(QtApplication):
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
group_node = None
|
||||
if node.getParent():
|
||||
group_node = node.getParent()
|
||||
if not group_node.callDecoration("isGroup"):
|
||||
op = RemoveSceneNodeOperation(node)
|
||||
else:
|
||||
while group_node.getParent().callDecoration("isGroup"):
|
||||
group_node = group_node.getParent()
|
||||
op = RemoveSceneNodeOperation(group_node)
|
||||
op = RemoveSceneNodeOperation(node)
|
||||
|
||||
op.push()
|
||||
if group_node:
|
||||
if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"):
|
||||
group_node.getChildren()[0].translate(group_node.getPosition())
|
||||
group_node.getChildren()[0].setParent(group_node.getParent())
|
||||
op = RemoveSceneNodeOperation(group_node)
|
||||
op.push()
|
||||
|
||||
## Create a number of copies of existing object.
|
||||
@pyqtSlot("quint64", int)
|
||||
@ -578,18 +597,16 @@ class CuraApplication(QtApplication):
|
||||
node = Selection.getSelectedObject(0)
|
||||
|
||||
if node:
|
||||
current_node = node
|
||||
# Find the topmost group
|
||||
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
|
||||
current_node = current_node.getParent()
|
||||
|
||||
new_node = copy.deepcopy(current_node)
|
||||
|
||||
op = GroupedOperation()
|
||||
for _ in range(count):
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
new_node = copy.deepcopy(node.getParent()) #Copy the group node.
|
||||
new_node.callDecoration("recomputeConvexHull")
|
||||
|
||||
op.addOperation(AddSceneNodeOperation(new_node,node.getParent().getParent()))
|
||||
else:
|
||||
new_node = copy.deepcopy(node)
|
||||
new_node.callDecoration("recomputeConvexHull")
|
||||
op.addOperation(AddSceneNodeOperation(new_node, node.getParent()))
|
||||
|
||||
op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent()))
|
||||
op.push()
|
||||
|
||||
## Center object on platform.
|
||||
@ -835,7 +852,11 @@ class CuraApplication(QtApplication):
|
||||
|
||||
def _reloadMeshFinished(self, job):
|
||||
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
|
||||
job._node.setMeshData(job.getResult().getMeshData())
|
||||
mesh_data = job.getResult().getMeshData()
|
||||
if mesh_data:
|
||||
job._node.setMeshData(mesh_data)
|
||||
else:
|
||||
Logger.log("w", "Could not find a mesh in reloaded node.")
|
||||
|
||||
def _openFile(self, file):
|
||||
job = ReadMeshJob(os.path.abspath(file))
|
||||
@ -852,3 +873,6 @@ class CuraApplication(QtApplication):
|
||||
@pyqtSlot("QSize")
|
||||
def setMinimumWindowSize(self, size):
|
||||
self.getMainWindow().setMinimumSize(size)
|
||||
|
||||
def getBuildVolume(self):
|
||||
return self._volume
|
@ -40,6 +40,7 @@ class PlatformPhysics:
|
||||
return
|
||||
|
||||
root = self._controller.getScene().getRoot()
|
||||
|
||||
for node in BreadthFirstIterator(root):
|
||||
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
||||
continue
|
||||
@ -47,7 +48,14 @@ class PlatformPhysics:
|
||||
bbox = node.getBoundingBox()
|
||||
|
||||
# Ignore intersections with the bottom
|
||||
build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001)
|
||||
build_volume_bounding_box = self._build_volume.getBoundingBox()
|
||||
if build_volume_bounding_box:
|
||||
# It's over 9000!
|
||||
build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
|
||||
else:
|
||||
# No bounding box. This is triggered when running Cura from command line with a model for the first time
|
||||
# In that situation there is a model, but no machine (and therefore no build volume.
|
||||
return
|
||||
node._outside_buildarea = False
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
@ -58,10 +66,7 @@ class PlatformPhysics:
|
||||
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 = move_vector.set(y=-bbox.bottom + z_offset)
|
||||
elif bbox.bottom < z_offset:
|
||||
move_vector = move_vector.set(y=(-bbox.bottom) - z_offset)
|
||||
move_vector = move_vector.set(y=-bbox.bottom + z_offset)
|
||||
|
||||
# If there is no convex hull for the node, start calculating it and continue.
|
||||
if not node.getDecorator(ConvexHullDecorator):
|
||||
|
@ -7,6 +7,8 @@ from UM.Application import Application
|
||||
from UM.Qt.Duration import Duration
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
import cura.Settings.ExtruderManager
|
||||
|
||||
import math
|
||||
import os.path
|
||||
import unicodedata
|
||||
@ -44,7 +46,8 @@ class PrintInformation(QObject):
|
||||
|
||||
self._current_print_time = Duration(None, self)
|
||||
|
||||
self._material_amounts = []
|
||||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
if self._backend:
|
||||
@ -62,11 +65,17 @@ class PrintInformation(QObject):
|
||||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
|
||||
materialAmountsChanged = pyqtSignal()
|
||||
materialLengthsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialAmountsChanged)
|
||||
def materialAmounts(self):
|
||||
return self._material_amounts
|
||||
@pyqtProperty("QVariantList", notify = materialLengthsChanged)
|
||||
def materialLengths(self):
|
||||
return self._material_lengths
|
||||
|
||||
materialWeightsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialWeightsChanged)
|
||||
def materialWeights(self):
|
||||
return self._material_weights
|
||||
|
||||
def _onPrintDurationMessage(self, total_time, material_amounts):
|
||||
self._current_print_time.setDuration(total_time)
|
||||
@ -74,13 +83,28 @@ class PrintInformation(QObject):
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
||||
self._material_amounts = []
|
||||
for amount in material_amounts:
|
||||
self._material_amounts.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
|
||||
self.materialAmountsChanged.emit()
|
||||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
extruder_stacks = list(cura.Settings.ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId()))
|
||||
for index, amount in enumerate(material_amounts):
|
||||
## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
|
||||
# list comprehension filtering to solve this for us.
|
||||
if extruder_stacks: # Multi extrusion machine
|
||||
extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0]
|
||||
density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
|
||||
else: # Machine with no extruder stacks
|
||||
density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0)
|
||||
|
||||
self._material_weights.append(float(amount) * float(density))
|
||||
self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
|
||||
self.materialLengthsChanged.emit()
|
||||
self.materialWeightsChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
# Ensure that we don't use entire path but only filename
|
||||
name = os.path.basename(name)
|
||||
|
||||
# when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
|
||||
# extension. This cuts the extension off if necessary.
|
||||
name = os.path.splitext(name)[0]
|
||||
|
@ -12,6 +12,6 @@ class ProfileReader(PluginObject):
|
||||
|
||||
## Read profile data from a file and return a filled profile.
|
||||
#
|
||||
# \return \type{Profile} The profile that was obtained from the file.
|
||||
# \return \type{Profile|Profile[]} The profile that was obtained from the file or a list of Profiles.
|
||||
def read(self, file_name):
|
||||
raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")
|
@ -1,3 +1,6 @@
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
|
@ -133,32 +133,45 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
|
||||
profile_reader = plugin_registry.getPluginObject(plugin_id)
|
||||
try:
|
||||
profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
|
||||
profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader.
|
||||
except Exception as e:
|
||||
#Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
|
||||
Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
|
||||
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
|
||||
if profile: #Success!
|
||||
profile.setReadOnly(False)
|
||||
|
||||
new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
|
||||
catalog.i18nc("@label", "Custom profile"))
|
||||
profile.setName(new_name)
|
||||
profile._id = new_name
|
||||
|
||||
if self._machineHasOwnQualities():
|
||||
profile.setDefinition(self._activeDefinition())
|
||||
if self._machineHasOwnMaterials():
|
||||
profile.addMetaDataEntry("material", self._activeMaterialId())
|
||||
if profile_or_list: # Success!
|
||||
name_seed = os.path.splitext(os.path.basename(file_name))[0]
|
||||
if type(profile_or_list) is not list:
|
||||
profile = profile_or_list
|
||||
self._configureProfile(profile, name_seed)
|
||||
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
|
||||
else:
|
||||
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
|
||||
ContainerRegistry.getInstance().addContainer(profile)
|
||||
for profile in profile_or_list:
|
||||
self._configureProfile(profile, name_seed)
|
||||
|
||||
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
|
||||
if len(profile_or_list) == 1:
|
||||
return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
|
||||
else:
|
||||
profile_names = ", ".join([profile.getName() for profile in profile_or_list])
|
||||
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profiles {0}", profile_names) }
|
||||
|
||||
#If it hasn't returned by now, none of the plugins loaded the profile successfully.
|
||||
return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
|
||||
|
||||
def _configureProfile(self, profile, name_seed):
|
||||
profile.setReadOnly(False)
|
||||
|
||||
new_name = self.createUniqueName("quality", "", name_seed, catalog.i18nc("@label", "Custom profile"))
|
||||
profile.setName(new_name)
|
||||
profile._id = new_name
|
||||
|
||||
if self._machineHasOwnQualities():
|
||||
profile.setDefinition(self._activeDefinition())
|
||||
if self._machineHasOwnMaterials():
|
||||
profile.addMetaDataEntry("material", self._activeMaterialId())
|
||||
else:
|
||||
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
|
||||
ContainerRegistry.getInstance().addContainer(profile)
|
||||
|
||||
## Gets a list of profile writer plugins
|
||||
# \return List of tuples of (plugin_id, meta_data).
|
||||
def _getIOPlugins(self, io_type):
|
||||
|
@ -35,10 +35,10 @@ class ExtruderManager(QObject):
|
||||
@pyqtProperty(str, notify = activeExtruderChanged)
|
||||
def activeExtruderStackId(self):
|
||||
if not UM.Application.getInstance().getGlobalContainerStack():
|
||||
return None #No active machine, so no active extruder.
|
||||
return None # No active machine, so no active extruder.
|
||||
try:
|
||||
return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)].getId()
|
||||
except KeyError: #Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
|
||||
return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId()
|
||||
except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
|
||||
return None
|
||||
|
||||
## The instance of the singleton pattern.
|
||||
@ -74,47 +74,43 @@ class ExtruderManager(QObject):
|
||||
def getActiveExtruderStack(self):
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
global_definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
if global_definition_container:
|
||||
if global_definition_container.getId() in self._extruder_trains:
|
||||
if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]:
|
||||
return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)]
|
||||
if global_container_stack.getId() in self._extruder_trains:
|
||||
if str(self._active_extruder_index) in self._extruder_trains[global_container_stack.getId()]:
|
||||
return self._extruder_trains[global_container_stack.getId()][str(self._active_extruder_index)]
|
||||
return None
|
||||
|
||||
## Adds all extruders of a specific machine definition to the extruder
|
||||
# manager.
|
||||
#
|
||||
# \param machine_definition The machine to add the extruders for.
|
||||
def addMachineExtruders(self, machine_definition):
|
||||
# \param machine_definition The machine definition to add the extruders for.
|
||||
# \param machine_id The machine_id to add the extruders for.
|
||||
def addMachineExtruders(self, machine_definition, machine_id):
|
||||
changed = False
|
||||
machine_id = machine_definition.getId()
|
||||
machine_definition_id = machine_definition.getId()
|
||||
if machine_id not in self._extruder_trains:
|
||||
self._extruder_trains[machine_id] = { }
|
||||
changed = True
|
||||
|
||||
container_registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
if container_registry:
|
||||
|
||||
#Add the extruder trains that don't exist yet.
|
||||
for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()):
|
||||
# Add the extruder trains that don't exist yet.
|
||||
for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition_id):
|
||||
position = extruder_definition.getMetaDataEntry("position", None)
|
||||
if not position:
|
||||
UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId())
|
||||
if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet.
|
||||
self.createExtruderTrain(extruder_definition, machine_definition, position)
|
||||
if not container_registry.findContainerStacks(machine = machine_id, position = position): # Doesn't exist yet.
|
||||
self.createExtruderTrain(extruder_definition, machine_definition, position, machine_id)
|
||||
changed = True
|
||||
|
||||
#Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId())
|
||||
# Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_id)
|
||||
for extruder_train in extruder_trains:
|
||||
self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
|
||||
|
||||
#Ensure that the extruder train stacks are linked to global stack.
|
||||
# Ensure that the extruder train stacks are linked to global stack.
|
||||
extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
self.extrudersChanged.emit(machine_definition)
|
||||
self.extrudersChanged.emit(machine_id)
|
||||
|
||||
## Creates a container stack for an extruder train.
|
||||
#
|
||||
@ -124,31 +120,29 @@ class ExtruderManager(QObject):
|
||||
#
|
||||
# The resulting container stack is added to the registry.
|
||||
#
|
||||
# \param extruder_definition The extruder to create the extruder train
|
||||
# for.
|
||||
# \param machine_definition The machine that the extruder train belongs
|
||||
# to.
|
||||
# \param position The position of this extruder train in the extruder
|
||||
# slots of the machine.
|
||||
def createExtruderTrain(self, extruder_definition, machine_definition, position):
|
||||
#Cache some things.
|
||||
# \param extruder_definition The extruder to create the extruder train for.
|
||||
# \param machine_definition The machine that the extruder train belongs to.
|
||||
# \param position The position of this extruder train in the extruder slots of the machine.
|
||||
# \param machine_id The id of the "global" stack this extruder is linked to.
|
||||
def createExtruderTrain(self, extruder_definition, machine_definition, position, machine_id):
|
||||
# Cache some things.
|
||||
container_registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
machine_id = machine_definition.getId()
|
||||
machine_definition_id = machine_definition.getId()
|
||||
|
||||
#Create a container stack for this extruder.
|
||||
# Create a container stack for this extruder.
|
||||
extruder_stack_id = container_registry.uniqueName(extruder_definition.getId())
|
||||
container_stack = UM.Settings.ContainerStack(extruder_stack_id)
|
||||
container_stack.setName(extruder_definition.getName()) #Take over the display name to display the stack with.
|
||||
container_stack.setName(extruder_definition.getName()) # Take over the display name to display the stack with.
|
||||
container_stack.addMetaDataEntry("type", "extruder_train")
|
||||
container_stack.addMetaDataEntry("machine", machine_definition.getId())
|
||||
container_stack.addMetaDataEntry("machine", machine_id)
|
||||
container_stack.addMetaDataEntry("position", position)
|
||||
container_stack.addContainer(extruder_definition)
|
||||
|
||||
#Find the variant to use for this extruder.
|
||||
variant = container_registry.getEmptyInstanceContainer()
|
||||
# Find the variant to use for this extruder.
|
||||
variant = container_registry.findInstanceContainers(id = "empty_variant")[0]
|
||||
if machine_definition.getMetaDataEntry("has_variants"):
|
||||
#First add any variant. Later, overwrite with preference if the preference is valid.
|
||||
variants = container_registry.findInstanceContainers(definition = machine_id, type = "variant")
|
||||
# First add any variant. Later, overwrite with preference if the preference is valid.
|
||||
variants = container_registry.findInstanceContainers(definition = machine_definition_id, type = "variant")
|
||||
if len(variants) >= 1:
|
||||
variant = variants[0]
|
||||
preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant")
|
||||
@ -158,17 +152,17 @@ class ExtruderManager(QObject):
|
||||
variant = preferred_variants[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id)
|
||||
#And leave it at the default variant.
|
||||
# And leave it at the default variant.
|
||||
container_stack.addContainer(variant)
|
||||
|
||||
#Find a material to use for this variant.
|
||||
material = container_registry.getEmptyInstanceContainer()
|
||||
# Find a material to use for this variant.
|
||||
material = container_registry.findInstanceContainers(id = "empty_material")[0]
|
||||
if machine_definition.getMetaDataEntry("has_materials"):
|
||||
#First add any material. Later, overwrite with preference if the preference is valid.
|
||||
# First add any material. Later, overwrite with preference if the preference is valid.
|
||||
if machine_definition.getMetaDataEntry("has_variant_materials", default = "False") == "True":
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_id, variant = variant.getId())
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id, variant = variant.getId())
|
||||
else:
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_id)
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id)
|
||||
if len(materials) >= 1:
|
||||
material = materials[0]
|
||||
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
@ -187,32 +181,41 @@ class ExtruderManager(QObject):
|
||||
material = preferred_materials[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
|
||||
#And leave it at the default material.
|
||||
# And leave it at the default material.
|
||||
container_stack.addContainer(material)
|
||||
|
||||
#Find a quality to use for this extruder.
|
||||
# Find a quality to use for this extruder.
|
||||
quality = container_registry.getEmptyInstanceContainer()
|
||||
|
||||
#First add any quality. Later, overwrite with preference if the preference is valid.
|
||||
qualities = container_registry.findInstanceContainers(type = "quality")
|
||||
if len(qualities) >= 1:
|
||||
quality = qualities[0]
|
||||
preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality_id:
|
||||
preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id, type = "quality")
|
||||
if len(preferred_quality) >= 1:
|
||||
quality = preferred_quality[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality_id, machine_id)
|
||||
#And leave it at the default quality.
|
||||
search_criteria = { "type": "quality" }
|
||||
if machine_definition.getMetaDataEntry("has_machine_quality"):
|
||||
search_criteria["definition"] = machine_definition.id
|
||||
if machine_definition.getMetaDataEntry("has_materials") and material:
|
||||
search_criteria["material"] = material.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_quality = machine_definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality:
|
||||
search_criteria["id"] = preferred_quality
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if not containers and preferred_quality:
|
||||
UM.Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id)
|
||||
search_criteria.pop("id", None)
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
quality = containers[0]
|
||||
|
||||
container_stack.addContainer(quality)
|
||||
|
||||
user_profile = container_registry.findInstanceContainers(id = extruder_stack_id + "_current_settings")
|
||||
if user_profile: #There was already a user profile, loaded from settings.
|
||||
user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id)
|
||||
if user_profile: # There was already a user profile, loaded from settings.
|
||||
user_profile = user_profile[0]
|
||||
else:
|
||||
user_profile = UM.Settings.InstanceContainer(extruder_stack_id + "_current_settings") #Add an empty user profile.
|
||||
user_profile = UM.Settings.InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile.
|
||||
user_profile.addMetaDataEntry("type", "user")
|
||||
user_profile.addMetaDataEntry("extruder", extruder_stack_id)
|
||||
user_profile.setDefinition(machine_definition)
|
||||
container_registry.addContainer(user_profile)
|
||||
container_stack.addContainer(user_profile)
|
||||
@ -221,7 +224,17 @@ class ExtruderManager(QObject):
|
||||
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
## Generates extruders for a specific machine.
|
||||
## Removes the container stack and user profile for the extruders for a specific machine.
|
||||
#
|
||||
# \param machine_id The machine to remove the extruders for.
|
||||
def removeMachineExtruders(self, machine_id):
|
||||
for extruder in self.getMachineExtruders(machine_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", extruder = extruder.getId())
|
||||
for container in containers:
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId())
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(extruder.getId())
|
||||
|
||||
## Returns extruders for a specific machine.
|
||||
#
|
||||
# \param machine_id The machine to get the extruders of.
|
||||
def getMachineExtruders(self, machine_id):
|
||||
@ -239,4 +252,4 @@ class ExtruderManager(QObject):
|
||||
def _addCurrentMachineExtruders(self):
|
||||
global_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack and global_stack.getBottom():
|
||||
self.addMachineExtruders(global_stack.getBottom())
|
||||
self.addMachineExtruders(global_stack.getBottom(), global_stack.getId())
|
||||
|
@ -115,7 +115,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
changed = True
|
||||
|
||||
manager = ExtruderManager.getInstance()
|
||||
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
||||
for extruder in manager.getMachineExtruders(global_container_stack.getId()):
|
||||
extruder_name = extruder.getName()
|
||||
material = extruder.findContainer({ "type": "material" })
|
||||
if material:
|
||||
|
@ -126,6 +126,9 @@ class MachineManager(QObject):
|
||||
self._auto_change_material_hotend_flood_time = time.time()
|
||||
self._auto_change_material_hotend_flood_last_choice = button
|
||||
|
||||
if button == QMessageBox.No:
|
||||
return
|
||||
|
||||
Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, hotend_id))
|
||||
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
@ -174,6 +177,9 @@ class MachineManager(QObject):
|
||||
self._auto_change_material_hotend_flood_time = time.time()
|
||||
self._auto_change_material_hotend_flood_last_choice = button
|
||||
|
||||
if button == QMessageBox.No:
|
||||
return
|
||||
|
||||
Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id))
|
||||
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
@ -227,7 +233,7 @@ class MachineManager(QObject):
|
||||
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
|
||||
|
||||
self.globalValidationChanged.emit()
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
material.nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
@ -239,7 +245,6 @@ class MachineManager(QObject):
|
||||
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
|
||||
self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
|
||||
|
||||
self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
@ -292,7 +297,7 @@ class MachineManager(QObject):
|
||||
new_global_stack.addContainer(quality_instance_container)
|
||||
new_global_stack.addContainer(current_settings_instance_container)
|
||||
|
||||
ExtruderManager.getInstance().addMachineExtruders(definition)
|
||||
ExtruderManager.getInstance().addMachineExtruders(definition, new_global_stack.getId())
|
||||
|
||||
Application.getInstance().setGlobalContainerStack(new_global_stack)
|
||||
|
||||
@ -494,6 +499,7 @@ class MachineManager(QObject):
|
||||
self.activeQualityChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
@pyqtSlot()
|
||||
def updateQualityContainerFromUserContainer(self, quality_id = None):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
@ -635,8 +641,12 @@ class MachineManager(QObject):
|
||||
# If the machine that is being removed is the currently active machine, set another machine as the active machine.
|
||||
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
|
||||
|
||||
current_settings_id = machine_id + "_current_settings"
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = current_settings_id)
|
||||
stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
|
||||
if not stacks:
|
||||
return
|
||||
ExtruderManager.getInstance().removeMachineExtruders(stacks[0].getBottom().getId())
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
|
||||
for container in containers:
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId())
|
||||
UM.Settings.ContainerRegistry.getInstance().removeContainer(machine_id)
|
||||
@ -688,7 +698,7 @@ class MachineManager(QObject):
|
||||
return containers[0].getBottom().getId()
|
||||
|
||||
@staticmethod
|
||||
def createMachineManager(engine, script_engine):
|
||||
def createMachineManager(engine=None, script_engine=None):
|
||||
return MachineManager()
|
||||
|
||||
def _updateVariantContainer(self, definition):
|
||||
@ -778,13 +788,10 @@ class MachineManager(QObject):
|
||||
return self._empty_quality_container
|
||||
|
||||
def _onMachineNameChanged(self):
|
||||
print("machine name changed")
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
def _onMaterialNameChanged(self):
|
||||
print("material name changed")
|
||||
self.activeMaterialChanged.emit()
|
||||
|
||||
def _onQualityNameChanged(self):
|
||||
print("quality name changed")
|
||||
self.activeQualityChanged.emit()
|
||||
|
@ -111,6 +111,8 @@ class ThreeMFReader(MeshReader):
|
||||
if len(objects) > 1:
|
||||
group_decorator = GroupDecorator()
|
||||
result.addDecorator(group_decorator)
|
||||
elif len(objects) == 1:
|
||||
result = result.getChildren()[0] # Only one object found, return that.
|
||||
except Exception as e:
|
||||
Logger.log("e", "exception occured in 3mf reader: %s", e)
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
[2.1.3]
|
||||
|
||||
*Material Profiles
|
||||
New material profiles for CPE+, PC, Nylon and TPU for the Ultimaker 2+ family.
|
||||
|
||||
[2.1.2]
|
||||
|
||||
Cura has been completely reengineered from the ground up for an even more seamless integration between hardware, software and materials. Together with its intuitive new user interface, it’s now also ready for any future developments. For the beginner Cura makes 3D printing incredibly easy, and for more advanced users, there are over 140 new customisable settings.
|
||||
Cura has been completely reengineered from the ground up for an even more seamless integration between hardware, software and materials. Together with its intuitive new user interface, it’s now also ready for any future developments. For the beginner Cura makes 3D printing incredibly easy, and for more advanced users, there are over 200 customizable settings.
|
||||
|
||||
*Select Multiple Objects
|
||||
You now have the freedom to select and manipulate multiple objects at the same time.
|
||||
@ -27,22 +32,61 @@ An optimized 64-bit Windows Cura version is now available. This allows you to lo
|
||||
Cura allows you to set a number of lines/layers instead of millimeters. The engine automatically calculates the right settings.
|
||||
|
||||
*Per-Object Settings
|
||||
You can now override individual settings for different objects in advanced mode.
|
||||
Per-object settings allow you to override individual profile settings per object.
|
||||
|
||||
*Fuzzy Skin
|
||||
Prints the outer walls with a jittering motion to give your object a diffused finish.
|
||||
*Engine Features
|
||||
|
||||
*Wire Printing
|
||||
The object is printed with a mid-air / net-like structure, following the mesh surface. The build plate will move up and down during diagonal segments. Though not visible in layer view, you can view the result in other software, such as Repetier Host or http://chilipeppr.com/tinyg.
|
||||
*Line Width
|
||||
Line width settings added per feature: Global, Walls, Top/Bottom, Infill, Skirt, Support.
|
||||
|
||||
* Conical Support
|
||||
An experimental filament, cost-reduction feature, for support.
|
||||
*Pattern Settings
|
||||
Pattern settings improved per feature: Top/Bottom, Infill, Support.
|
||||
|
||||
*Shell
|
||||
|
||||
*Alternate Skin Rotation
|
||||
Helps to combat the pillowing problem on top layers.
|
||||
|
||||
*Alternate Extra Wall
|
||||
For better infill adhesion.
|
||||
|
||||
*Horizontal Expansion
|
||||
Allows to compensate model x,y-size to get a 1:1 result.
|
||||
|
||||
*Travel
|
||||
|
||||
*Avoid Printed Parts
|
||||
When moving to the next part to print, avoid collisions between the nozzle and other parts which are already printed.
|
||||
|
||||
*Support
|
||||
|
||||
*Stair Step Height
|
||||
Sets the balance between sturdy and hard to remove support. By setting steps of the stair-like bottom of the support resting on the model.
|
||||
|
||||
*ZigZag
|
||||
A new, infill type that’s easily breakable, introduced specially for support.
|
||||
|
||||
*Support Roofs
|
||||
A new sub-feature to reduce scars the support leaves on overhangs.
|
||||
|
||||
*Support Towers
|
||||
Specialized support for tiny overhang areas.
|
||||
|
||||
*ZigZag infill
|
||||
A new, infill type that’s easily breakable, introduced specially for support.
|
||||
*Special Modes
|
||||
|
||||
* Avoid Printed Parts
|
||||
While combing, the print head moves around printed parts, avoiding collisions with the nozzle and a part that’s already printed.
|
||||
*Surface Mode
|
||||
This mode will print the surface of the mesh instead of the enclosed volume. This used to be called ‘Only follow mesh surface’. In addition to the ‘surface mode’ and ‘normal’, a ‘both’ mode has now been added. This ensures all closed volumes are printed as normal and all loose geometry as single walls.
|
||||
|
||||
*Experimental Features
|
||||
|
||||
*Conical Support
|
||||
An experimental filament, cost-reduction feature, for support.
|
||||
|
||||
*Draft Shield
|
||||
Prints a protective wall at a set distance around the object that prevents air from hitting the print, reducing warping.
|
||||
|
||||
*Fuzzy Skin
|
||||
Prints the outer walls with a jittering motion to give your object a diffuse finish.
|
||||
|
||||
*Wire Printing
|
||||
The object is printed with a mid-air / net-like structure, following the mesh surface. The build plate will move up and down during diagonal segments. Though not visible in layer view, you can view the result in other software, such as Repetier Host or http://chilipeppr.com/tinyg.
|
||||
|
@ -22,6 +22,7 @@ from . import StartSliceJob
|
||||
|
||||
import os
|
||||
import sys
|
||||
from time import time
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
@ -91,6 +92,7 @@ class CuraEngineBackend(Backend):
|
||||
self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
|
||||
self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.
|
||||
|
||||
self._backend_log_max_lines = 200 # Maximal count of lines to buffer
|
||||
self._error_message = None #Pop-up message that shows errors.
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
@ -100,6 +102,8 @@ class CuraEngineBackend(Backend):
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
self._slice_start_time = None
|
||||
|
||||
## Called when closing the application.
|
||||
#
|
||||
# This function should terminate the engine process.
|
||||
@ -128,6 +132,7 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
self._slice_start_time = time()
|
||||
if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
|
||||
# try again in a short time
|
||||
self._change_timer.start()
|
||||
@ -217,6 +222,7 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
# Preparation completed, send it to the backend.
|
||||
self._socket.sendMessage(job.getSliceMessage())
|
||||
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
|
||||
|
||||
## Listener for when the scene has changed.
|
||||
#
|
||||
@ -286,7 +292,7 @@ class CuraEngineBackend(Backend):
|
||||
self.processingProgress.emit(1.0)
|
||||
|
||||
self._slicing = False
|
||||
|
||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
||||
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
|
||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
|
||||
self._process_layers_job.start()
|
||||
|
@ -9,6 +9,7 @@ from UM.Mesh.MeshData import MeshData
|
||||
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
@ -17,7 +18,7 @@ from cura import LayerDataDecorator
|
||||
from cura import LayerPolygon
|
||||
|
||||
import numpy
|
||||
|
||||
from time import time
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
@ -45,6 +46,7 @@ class ProcessSlicedLayersJob(Job):
|
||||
if len(self._layers) == 0:
|
||||
return
|
||||
|
||||
start_time = time()
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
||||
self._progress.show()
|
||||
@ -155,7 +157,10 @@ class ProcessSlicedLayersJob(Job):
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot()) # Note: After this we can no longer abort!
|
||||
# Set build volume as parent, the build volume can move as a result of raft settings.
|
||||
# It makes sense to set the build volume as parent: the print is actually printed on it.
|
||||
new_node_parent = Application.getInstance().getBuildVolume()
|
||||
new_node.setParent(new_node_parent) # Note: After this we can no longer abort!
|
||||
|
||||
settings = Application.getInstance().getGlobalContainerStack()
|
||||
if not settings.getProperty("machine_center_is_zero", "value"):
|
||||
@ -174,6 +179,8 @@ class ProcessSlicedLayersJob(Job):
|
||||
# Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
|
||||
self._layers = None
|
||||
|
||||
Logger.log("d", "Processing layers took %s seconds", time() - start_time)
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
if self.isRunning():
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
|
@ -129,7 +129,7 @@ class StartSliceJob(Job):
|
||||
|
||||
self._buildGlobalSettingsMessage(stack)
|
||||
|
||||
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()):
|
||||
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
||||
self._buildExtruderMessage(extruder_stack)
|
||||
|
||||
for group in object_groups:
|
||||
@ -174,10 +174,17 @@ class StartSliceJob(Job):
|
||||
def _buildExtruderMessage(self, stack):
|
||||
message = self._slice_message.addRepeatedMessage("extruders")
|
||||
message.id = int(stack.getMetaDataEntry("position"))
|
||||
|
||||
material_instance_container = stack.findContainer({"type": "material"})
|
||||
|
||||
for key in stack.getAllKeys():
|
||||
setting = message.getMessage("settings").addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
|
||||
if key == "material_guid" and material_instance_container:
|
||||
# Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
|
||||
setting.value = str(material_instance_container.getMetaDataEntry("GUID", "")).encode("utf-8")
|
||||
else:
|
||||
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
|
||||
Job.yieldThread()
|
||||
|
||||
## Sends all global settings to the engine.
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
import os.path
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
|
||||
from cura.ProfileReader import ProfileReader
|
||||
|
@ -1,10 +1,9 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import re #Regular expressions for parsing escape characters in the settings.
|
||||
import json
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
@ -22,7 +21,7 @@ class GCodeProfileReader(ProfileReader):
|
||||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 2
|
||||
version = 3
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
@ -66,21 +65,37 @@ class GCodeProfileReader(ProfileReader):
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
# Un-escape the serialized profile.
|
||||
pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
|
||||
|
||||
# Perform the replacement with a regular expression.
|
||||
serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized)
|
||||
serialized = unescapeGcodeComment(serialized)
|
||||
Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized)))
|
||||
|
||||
# Create an empty profile - the id and name will be changed by the ContainerRegistry
|
||||
profile = InstanceContainer("")
|
||||
try:
|
||||
profile.deserialize(serialized)
|
||||
except Exception as e: # Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
json_data = json.loads(serialized)
|
||||
|
||||
profile.addMetaDataEntry("type", "quality")
|
||||
profile_strings = [json_data["global_quality"]]
|
||||
profile_strings.extend(json_data.get("extruder_quality", []))
|
||||
|
||||
return profile
|
||||
return [readQualityProfileFromString(profile_string) for profile_string in profile_strings]
|
||||
|
||||
## Unescape a string which has been escaped for use in a gcode comment.
|
||||
#
|
||||
# \param string The string to unescape.
|
||||
# \return \type{str} The unscaped string.
|
||||
def unescapeGcodeComment(string):
|
||||
# Un-escape the serialized profile.
|
||||
pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
|
||||
|
||||
# Perform the replacement with a regular expression.
|
||||
return pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], string)
|
||||
|
||||
## Read in a profile from a serialized string.
|
||||
#
|
||||
# \param profile_string The profile data in serialized form.
|
||||
# \return \type{Profile} the resulting Profile object or None if it could not be read.
|
||||
def readQualityProfileFromString(profile_string):
|
||||
# Create an empty profile - the id and name will be changed by the ContainerRegistry
|
||||
profile = InstanceContainer("")
|
||||
try:
|
||||
profile.deserialize(profile_string)
|
||||
except Exception as e: # Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
return profile
|
||||
|
@ -4,8 +4,13 @@
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Logger import Logger
|
||||
from UM.Application import Application
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To create a complete setting profile to store in the g-code.
|
||||
import UM.Settings.ContainerRegistry
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
import re #For escaping characters in the settings.
|
||||
import json
|
||||
|
||||
## Writes g-code to a file.
|
||||
#
|
||||
@ -23,7 +28,7 @@ class GCodeWriter(MeshWriter):
|
||||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 2
|
||||
version = 3
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
@ -64,25 +69,49 @@ class GCodeWriter(MeshWriter):
|
||||
#
|
||||
# \param settings A container stack to serialise.
|
||||
# \return A serialised string of the settings.
|
||||
def _serialiseSettings(self, settings):
|
||||
def _serialiseSettings(self, stack):
|
||||
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
|
||||
prefix_length = len(prefix)
|
||||
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
container_with_profile = global_stack.findContainer({"type": "quality"})
|
||||
serialized = container_with_profile.serialize()
|
||||
container_with_profile = stack.findContainer({"type": "quality"})
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
# Duplicate the current quality profile and update it with any user settings.
|
||||
flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId())
|
||||
flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0]
|
||||
user_settings = stack.getTop()
|
||||
for key in user_settings.getAllKeys():
|
||||
flat_quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
|
||||
|
||||
serialized = flat_quality.serialize()
|
||||
|
||||
data = {"global_quality": serialized}
|
||||
|
||||
manager = ExtruderManager.getInstance()
|
||||
for extruder in manager.getMachineExtruders(stack.getId()):
|
||||
extruder_quality = extruder.findContainer({"type": "quality"})
|
||||
|
||||
flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId())
|
||||
flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0]
|
||||
extruder_user_settings = extruder.getTop()
|
||||
for key in extruder_user_settings.getAllKeys():
|
||||
flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value"))
|
||||
|
||||
extruder_serialized = flat_extruder_quality.serialize()
|
||||
data.setdefault("extruder_quality", []).append(extruder_serialized)
|
||||
|
||||
json_string = json.dumps(data)
|
||||
|
||||
# Escape characters that have a special meaning in g-code comments.
|
||||
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
|
||||
|
||||
# Perform the replacement with a regular expression.
|
||||
serialized = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialized)
|
||||
escaped_string = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string)
|
||||
|
||||
# Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
|
||||
result = ""
|
||||
|
||||
# Lines have 80 characters, so the payload of each line is 80 - prefix.
|
||||
for pos in range(0, len(serialized), 80 - prefix_length):
|
||||
result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n"
|
||||
serialized = result
|
||||
|
||||
return serialized
|
||||
for pos in range(0, len(escaped_string), 80 - prefix_length):
|
||||
result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n"
|
||||
return result
|
||||
|
@ -3,10 +3,12 @@
|
||||
|
||||
from UM.Tool import Tool
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
|
||||
## This tool allows the user to add & change settings per node in the scene.
|
||||
# The settings per object are kept in a ContainerStack, which is linked to a node by decorator.
|
||||
class PerObjectSettingsTool(Tool):
|
||||
@ -69,6 +71,11 @@ class PerObjectSettingsTool(Tool):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
||||
if not self._multi_extrusion:
|
||||
# Ensure that all extruder data is reset
|
||||
root_node = Application.getInstance().getController().getScene().getRoot()
|
||||
for node in DepthFirstIterator(root_node):
|
||||
node.callDecoration("setActiveExtruder", global_container_stack.getId())
|
||||
self._updateEnabled()
|
||||
|
||||
def _updateEnabled(self):
|
||||
|
@ -59,7 +59,7 @@ class SliceInfo(Extension):
|
||||
material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value")
|
||||
|
||||
# TODO: Send material per extruder instead of mashing it on a pile
|
||||
material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used
|
||||
material_used = math.pi * material_radius * material_radius * sum(print_information.materialLengths) #Volume of all materials used
|
||||
|
||||
# Get model information (bounding boxes, hashes and transformation matrix)
|
||||
models_info = []
|
||||
@ -93,7 +93,6 @@ class SliceInfo(Extension):
|
||||
"printtime": print_information.currentPrintTime.getDisplayString(),
|
||||
"filament": material_used,
|
||||
"language": Preferences.getInstance().getValue("general/language"),
|
||||
"materials_profiles ": {}
|
||||
}
|
||||
for container in global_container_stack.getContainers():
|
||||
container_id = container.getId()
|
||||
|
@ -8,14 +8,12 @@ import time
|
||||
import queue
|
||||
import re
|
||||
import functools
|
||||
import os.path
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
|
||||
from UM.Message import Message
|
||||
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
@ -137,7 +135,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
# \param gcode_list List with gcode (strings).
|
||||
def printGCode(self, gcode_list):
|
||||
if self._progress or self._connection_state != ConnectionState.connected:
|
||||
self._error_message = Message(i18n_catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job."))
|
||||
self._error_message = Message(catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job."))
|
||||
self._error_message.show()
|
||||
Logger.log("d", "Printer is busy or not connected, aborting print")
|
||||
self.writeError.emit(self)
|
||||
@ -504,6 +502,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
# It will be normalized (based on max_progress) to range 0 - 100
|
||||
def setProgress(self, progress, max_progress = 100):
|
||||
self._progress = (progress / max_progress) * 100 # Convert to scale of 0-100
|
||||
if self._progress == 100:
|
||||
# Printing is done, reset progress
|
||||
self._gcode_position = 0
|
||||
self.setProgress(0)
|
||||
self._is_printing = False
|
||||
self._is_paused = False
|
||||
self._updateJobState("ready")
|
||||
self.progressChanged.emit()
|
||||
|
||||
## Cancel the current print. Printer connection wil continue to listen.
|
||||
|
@ -197,7 +197,6 @@ Cura.MachineAction
|
||||
Button
|
||||
{
|
||||
text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
|
||||
//
|
||||
onClicked:
|
||||
{
|
||||
if (checkupMachineAction.heatupHotendStarted)
|
||||
|
@ -42,7 +42,7 @@ Cura.MachineAction
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
text: catalog.i18nc("@label", "Self-built heated bed")
|
||||
text: catalog.i18nc("@label", "Heated bed (official kit or self-built)")
|
||||
checked: manager.hasHeatedBed
|
||||
onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed()
|
||||
}
|
||||
|
@ -47,11 +47,11 @@ class MachineInstance:
|
||||
raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.")
|
||||
|
||||
self._type_name = config.get("general", "type")
|
||||
self._variant_name = config.get("general", "variant", fallback = "empty")
|
||||
self._variant_name = config.get("general", "variant", fallback = "empty_variant")
|
||||
self._name = config.get("general", "name", fallback = "")
|
||||
self._key = config.get("general", "key", fallback = None)
|
||||
self._active_profile_name = config.get("general", "active_profile", fallback = "empty")
|
||||
self._active_material_name = config.get("general", "material", fallback = "empty")
|
||||
self._active_profile_name = config.get("general", "active_profile", fallback = "empty_quality")
|
||||
self._active_material_name = config.get("general", "material", fallback = "empty_material")
|
||||
|
||||
self._machine_setting_overrides = {}
|
||||
for key, value in config["machine_settings"].items():
|
||||
|
@ -18,7 +18,11 @@ _printer_translations = {
|
||||
_profile_translations = {
|
||||
"PLA": "generic_pla",
|
||||
"ABS": "generic_abs",
|
||||
"CPE": "generic_cpe"
|
||||
"CPE": "generic_cpe",
|
||||
"Low Quality": "low",
|
||||
"Normal Quality": "normal",
|
||||
"High Quality": "high",
|
||||
"Ulti Quality": "high" #This one doesn't have an equivalent. Map it to high.
|
||||
}
|
||||
|
||||
## How to translate setting names from the old version to the new.
|
||||
|
@ -145,6 +145,28 @@
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"extruder_prime_pos_z":
|
||||
{
|
||||
"label": "Extruder Prime Z Position",
|
||||
"description": "The Z coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value": "machine_height",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform_adhesion":
|
||||
{
|
||||
"label": "Platform Adhesion",
|
||||
"type": "category",
|
||||
"icon": "category_adhesion",
|
||||
"description": "Adhesion",
|
||||
"children":
|
||||
{
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"label": "Extruder Prime X Position",
|
||||
@ -152,10 +174,11 @@
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"minimum_value_warning": "machine_nozzle_offset_x",
|
||||
"maximum_value": "machine_width",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
@ -164,22 +187,11 @@
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"minimum_value_warning": "machine_nozzle_offset_y",
|
||||
"maximum_value_warning": "machine_depth",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_z":
|
||||
{
|
||||
"label": "Extruder Prime Z Position",
|
||||
"description": "The Z coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,14 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"material_guid":
|
||||
{
|
||||
"label": "Material GUID",
|
||||
"description": "GUID of the material. This is set automatically. ",
|
||||
"default_value": "",
|
||||
"type": "str",
|
||||
"enabled": false
|
||||
},
|
||||
"material_bed_temp_wait":
|
||||
{
|
||||
"label": "Wait for bed heatup",
|
||||
@ -326,30 +334,6 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"label": "Extruder Prime X Position",
|
||||
"description": "The X coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
"label": "Extruder Prime Y Position",
|
||||
"description": "The Y coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_z":
|
||||
{
|
||||
"label": "Extruder Prime Z Position",
|
||||
@ -357,8 +341,8 @@
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value": "machine_height",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
@ -370,6 +354,136 @@
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_max_feedrate_x": {
|
||||
"label": "Maximum Speed X",
|
||||
"description": "The maximum speed for the motor of the X-direction.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 500,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_feedrate_y": {
|
||||
"label": "Maximum Speed Y",
|
||||
"description": "The maximum speed for the motor of the Y-direction.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 500,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_feedrate_z": {
|
||||
"label": "Maximum Speed Z",
|
||||
"description": "The maximum speed for the motor of the Z-direction.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_feedrate_e": {
|
||||
"label": "Maximum Feedrate",
|
||||
"description": "The maximum speed of the filament.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 25,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_acceleration_x": {
|
||||
"label": "Maximum Acceleration X",
|
||||
"description": "Maximum acceleration for the motor of the X-direction",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 9000,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_acceleration_y": {
|
||||
"label": "Maximum Acceleration Y",
|
||||
"description": "Maximum acceleration for the motor of the Y-direction.",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 9000,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_acceleration_z": {
|
||||
"label": "Maximum Acceleration Z",
|
||||
"description": "Maximum acceleration for the motor of the Z-direction.",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 100,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_acceleration_e": {
|
||||
"label": "Maximum Filament Acceleration",
|
||||
"description": "Maximum acceleration for the motor of the filament.",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 10000,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_acceleration": {
|
||||
"label": "Default Acceleration",
|
||||
"description": "The default acceleration of print head movement.",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 4000,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_jerk_xy": {
|
||||
"label": "Default X-Y Jerk",
|
||||
"description": "Default jerk for movement in the horizontal plane.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20.0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_jerk_z": {
|
||||
"label": "Default Z Jerk",
|
||||
"description": "Default jerk for the motor of the Z-direction.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 0.4,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_max_jerk_e": {
|
||||
"label": "Default Filament Jerk",
|
||||
"description": "Default jerk for the motor of the filament.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 5.0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_minimum_feedrate": {
|
||||
"label": "Minimum Feedrate",
|
||||
"description": "The minimal movement speed of the print head.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 0.0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -592,7 +706,7 @@
|
||||
"default_value": 0.8,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0.6",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"maximum_value": "machine_height",
|
||||
"type": "float",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
@ -604,7 +718,7 @@
|
||||
"unit": "mm",
|
||||
"default_value": 0.8,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"maximum_value": "machine_height",
|
||||
"type": "float",
|
||||
"value": "top_bottom_thickness",
|
||||
"settable_per_mesh": true,
|
||||
@ -632,7 +746,7 @@
|
||||
"minimum_value": "0",
|
||||
"type": "float",
|
||||
"value": "top_bottom_thickness",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"maximum_value": "machine_height",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
{
|
||||
@ -774,7 +888,7 @@
|
||||
"type": "float",
|
||||
"default_value": 2,
|
||||
"minimum_value": "0",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else (3 if infill_pattern == \"cubic\" else 1)))",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" or infill_pattern == \"cubic\" else (4 if infill_pattern == \"tetrahedral\" else 1)))",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
@ -782,14 +896,15 @@
|
||||
"infill_pattern":
|
||||
{
|
||||
"label": "Infill Pattern",
|
||||
"description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, cubic, triangle and concentric patterns are fully printed every layer.",
|
||||
"description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, cubic, tetrahedral and concentric patterns are fully printed every layer. Cubic and tetrahedral infill change with every layer to provide a more equal distribution of strength over each direction.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"grid": "Grid",
|
||||
"lines": "Lines",
|
||||
"cubic": "Cubic",
|
||||
"triangles": "Triangles",
|
||||
"cubic": "Cubic",
|
||||
"tetrahedral": "Tetrahedral",
|
||||
"concentric": "Concentric",
|
||||
"zigzag": "Zig Zag"
|
||||
},
|
||||
@ -877,6 +992,29 @@
|
||||
"value": "layer_height",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"gradual_infill_steps":
|
||||
{
|
||||
"label": "Gradual Infill Steps",
|
||||
"description": "Number of times to reduce the infill density by half when getting further below top surfaces. Areas which are closer to top surfaces get a higher density, up to the Infill Density.",
|
||||
"default_value": 0,
|
||||
"type": "int",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "4",
|
||||
"maximum_value": "20 - math.log(infill_line_distance) / math.log(2)",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"gradual_infill_step_height":
|
||||
{
|
||||
"label": "Gradual Infill Step Height",
|
||||
"description": "The height of infill of a given density before switching to half the density.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5.0,
|
||||
"minimum_value": "0.0001",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled": "gradual_infill_steps > 0",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_before_walls":
|
||||
{
|
||||
"label": "Infill Before Walls",
|
||||
@ -1304,7 +1442,7 @@
|
||||
"maximum_value_warning": "150",
|
||||
"default_value": 60,
|
||||
"value": "speed_print",
|
||||
"enabled": "support_roof_enable",
|
||||
"enabled": "support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children":
|
||||
@ -1506,7 +1644,7 @@
|
||||
"maximum_value_warning": "10000",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_print",
|
||||
"enabled": "acceleration_enabled and support_roof_enable",
|
||||
"enabled": "acceleration_enabled and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children": {
|
||||
@ -1534,7 +1672,7 @@
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "100",
|
||||
"maximum_value_warning": "10000",
|
||||
"enabled": "acceleration_enabled and support_roof_enable",
|
||||
"enabled": "acceleration_enabled and support_roof_enable and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
@ -1608,7 +1746,7 @@
|
||||
"jerk_print": {
|
||||
"label": "Print Jerk",
|
||||
"description": "The maximum instantaneous velocity change of the print head.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1620,7 +1758,7 @@
|
||||
"jerk_infill": {
|
||||
"label": "Infill Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which infill is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1633,7 +1771,7 @@
|
||||
"jerk_wall": {
|
||||
"label": "Wall Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the walls are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1646,7 +1784,7 @@
|
||||
"jerk_wall_0": {
|
||||
"label": "Outer Wall Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the outermost walls are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1659,7 +1797,7 @@
|
||||
"jerk_wall_x": {
|
||||
"label": "Inner Wall Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which all inner walls are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1674,7 +1812,7 @@
|
||||
"jerk_topbottom": {
|
||||
"label": "Top/Bottom Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which top/bottom layers are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1687,21 +1825,21 @@
|
||||
"jerk_support": {
|
||||
"label": "Support Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the support structure is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
"maximum_value_warning": "50",
|
||||
"default_value": 20,
|
||||
"value": "jerk_print",
|
||||
"enabled": "jerk_enabled and support_roof_enable",
|
||||
"enabled": "jerk_enabled and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children": {
|
||||
"jerk_support_infill": {
|
||||
"label": "Support Infill Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the infill of support is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "jerk_support",
|
||||
@ -1715,14 +1853,14 @@
|
||||
"jerk_support_roof": {
|
||||
"label": "Support Roof Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the roofs of support are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "jerk_support",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
"maximum_value_warning": "50",
|
||||
"enabled": "jerk_enabled and support_roof_enable",
|
||||
"enabled": "jerk_enabled and support_roof_enable and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
@ -1731,7 +1869,7 @@
|
||||
"jerk_prime_tower": {
|
||||
"label": "Prime Tower Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the prime tower is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"minimum_value": "0.1",
|
||||
"minimum_value_warning": "5",
|
||||
@ -1746,7 +1884,7 @@
|
||||
"jerk_travel": {
|
||||
"label": "Travel Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which travel moves are made.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 30,
|
||||
"minimum_value": "0.1",
|
||||
@ -1759,7 +1897,7 @@
|
||||
"jerk_layer_0": {
|
||||
"label": "Initial Layer Jerk",
|
||||
"description": "The print maximum instantaneous velocity change for the initial layer.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "jerk_print",
|
||||
@ -1772,7 +1910,7 @@
|
||||
"jerk_skirt": {
|
||||
"label": "Skirt Jerk",
|
||||
"description": "The maximum instantaneous velocity change with which the skirt and brim are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"minimum_value": "0.1",
|
||||
@ -2211,7 +2349,7 @@
|
||||
"default_value": 1,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "support_roof_enable",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_roof_density":
|
||||
@ -2223,7 +2361,7 @@
|
||||
"default_value": 100,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled":"support_roof_enable",
|
||||
"enabled":"support_roof_enable and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children":
|
||||
@ -2237,7 +2375,7 @@
|
||||
"default_value": 0.4,
|
||||
"minimum_value": "0",
|
||||
"value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))",
|
||||
"enabled": "support_roof_enable",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
@ -2257,7 +2395,7 @@
|
||||
"zigzag": "Zig Zag"
|
||||
},
|
||||
"default_value": "concentric",
|
||||
"enabled": "support_roof_enable",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -2317,6 +2455,32 @@
|
||||
"description": "Adhesion",
|
||||
"children":
|
||||
{
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"label": "Extruder Prime X Position",
|
||||
"description": "The X coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "machine_width",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
"label": "Extruder Prime Y Position",
|
||||
"description": "The Y coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "machine_depth",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
},
|
||||
"adhesion_type":
|
||||
{
|
||||
"label": "Platform Adhesion Type",
|
||||
@ -2701,7 +2865,7 @@
|
||||
"raft_jerk": {
|
||||
"label": "Raft Print Jerk",
|
||||
"description": "The jerk with which the raft is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"minimum_value": "0.1",
|
||||
@ -2714,7 +2878,7 @@
|
||||
"raft_surface_jerk": {
|
||||
"label": "Raft Top Print Jerk",
|
||||
"description": "The jerk with which the top raft layers are printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "raft_jerk",
|
||||
@ -2727,7 +2891,7 @@
|
||||
"raft_interface_jerk": {
|
||||
"label": "Raft Middle Print Jerk",
|
||||
"description": "The jerk with which the middle raft layer is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "raft_jerk",
|
||||
@ -2740,7 +2904,7 @@
|
||||
"raft_base_jerk": {
|
||||
"label": "Raft Base Print Jerk",
|
||||
"description": "The jerk with which the base raft layer is printed.",
|
||||
"unit": "mm/s³",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"value": "raft_jerk",
|
||||
|
@ -87,6 +87,21 @@
|
||||
"material_bed_temperature": {
|
||||
"enabled": "False"
|
||||
},
|
||||
"machine_max_feedrate_x": {
|
||||
"default_value": 300
|
||||
},
|
||||
"machine_max_feedrate_y": {
|
||||
"default_value": 300
|
||||
},
|
||||
"machine_max_feedrate_z": {
|
||||
"default_value": 40
|
||||
},
|
||||
"machine_max_feedrate_e": {
|
||||
"default_value": 45
|
||||
},
|
||||
"machine_acceleration": {
|
||||
"default_value": 3000
|
||||
},
|
||||
"material_diameter": {
|
||||
"enabled": "False"
|
||||
},
|
||||
|
@ -17,6 +17,9 @@
|
||||
"overrides": {
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_max_feedrate_z": {
|
||||
"default_value": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,18 @@ UM.MainWindow
|
||||
Component.onCompleted:
|
||||
{
|
||||
Printer.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
|
||||
|
||||
// Workaround silly issues with QML Action's shortcut property.
|
||||
//
|
||||
// Currently, there is no way to define shortcuts as "Application Shortcut".
|
||||
// This means that all Actions are "Window Shortcuts". The code for this
|
||||
// implements a rather naive check that just checks if any of the action's parents
|
||||
// are a window. Since the "Actions" object is a singleton it has no parent by
|
||||
// default. If we set its parent to something contained in this window, the
|
||||
// shortcut will activate properly because one of its parents is a window.
|
||||
//
|
||||
// This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property.
|
||||
Cura.Actions.parent = backgroundItem
|
||||
}
|
||||
|
||||
Item
|
||||
@ -309,23 +321,7 @@ UM.MainWindow
|
||||
sourceSize.height: height;
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: viewModeButton
|
||||
|
||||
anchors
|
||||
{
|
||||
top: toolbar.bottom;
|
||||
topMargin: UM.Theme.getSize("window_margin").height;
|
||||
left: parent.left;
|
||||
}
|
||||
text: catalog.i18nc("@action:button","View Mode");
|
||||
iconSource: UM.Theme.getIcon("viewmode");
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
menu: ViewMenu { }
|
||||
}
|
||||
|
||||
Toolbar
|
||||
{
|
||||
@ -355,6 +351,24 @@ UM.MainWindow
|
||||
width: UM.Theme.getSize("sidebar").width;
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: viewModeButton
|
||||
|
||||
anchors
|
||||
{
|
||||
top: toolbar.bottom;
|
||||
topMargin: UM.Theme.getSize("window_margin").height;
|
||||
left: parent.left;
|
||||
}
|
||||
text: catalog.i18nc("@action:button","View Mode");
|
||||
iconSource: UM.Theme.getIcon("viewmode");
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
menu: ViewMenu { }
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: viewportOverlay
|
||||
@ -506,6 +520,17 @@ UM.MainWindow
|
||||
onTriggered: preferences.getCurrentItem().showProfileNameDialog()
|
||||
}
|
||||
|
||||
// BlurSettings is a way to force the focus away from any of the setting items.
|
||||
// We need to do this in order to keep the bindings intact.
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onBlurSettings:
|
||||
{
|
||||
contentItem.focus = true
|
||||
}
|
||||
}
|
||||
|
||||
Menu
|
||||
{
|
||||
id: objectContextMenu;
|
||||
@ -615,7 +640,7 @@ UM.MainWindow
|
||||
//TODO: Support multiple file selection, workaround bug in KDE file dialog
|
||||
//selectMultiple: true
|
||||
nameFilters: UM.MeshFileHandler.supportedReadFileTypes;
|
||||
folder: Printer.getDefaultPath()
|
||||
folder: CuraApplication.getDefaultPath("dialog_load_path")
|
||||
onAccepted:
|
||||
{
|
||||
//Because several implementations of the file dialog only update the folder
|
||||
@ -623,6 +648,7 @@ UM.MainWindow
|
||||
var f = folder;
|
||||
folder = f;
|
||||
|
||||
CuraApplication.setDefaultPath("dialog_load_path", folder);
|
||||
UM.MeshFileHandler.readLocalFile(fileUrl)
|
||||
var meshName = backgroundItem.getMeshName(fileUrl.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
|
@ -24,7 +24,8 @@ Rectangle {
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
property variant printDuration: PrintInformation.currentPrintTime
|
||||
property variant printMaterialAmounts: PrintInformation.materialAmounts
|
||||
property variant printMaterialLengths: PrintInformation.materialLengths
|
||||
property variant printMaterialWeights: PrintInformation.materialWeights
|
||||
|
||||
height: childrenRect.height
|
||||
color: "transparent"
|
||||
@ -195,9 +196,9 @@ Rectangle {
|
||||
text:
|
||||
{
|
||||
var amounts = [];
|
||||
if(base.printMaterialAmounts) {
|
||||
for(var index = 0; index < base.printMaterialAmounts.length; index++) {
|
||||
amounts.push(base.printMaterialAmounts[index].toFixed(2));
|
||||
if(base.printMaterialLengths) {
|
||||
for(var index = 0; index < base.printMaterialLengths.length; index++) {
|
||||
amounts.push(base.printMaterialLengths[index].toFixed(2));
|
||||
}
|
||||
} else {
|
||||
amounts = ["0.00"];
|
||||
|
@ -74,6 +74,7 @@ UM.ManagementPage
|
||||
Flow
|
||||
{
|
||||
id: machineActions
|
||||
visible: currentItem && currentItem.id == Cura.MachineManager.activeMachineId
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: machineName.bottom
|
||||
|
@ -200,7 +200,7 @@ UM.ManagementPage
|
||||
title: catalog.i18nc("@title:window", "Import Material");
|
||||
selectExisting: true;
|
||||
nameFilters: Cura.ContainerManager.getContainerNameFilters("material")
|
||||
folder: CuraApplication.getDefaultPath()
|
||||
folder: CuraApplication.getDefaultPath("dialog_material_path")
|
||||
onAccepted:
|
||||
{
|
||||
var result = Cura.ContainerManager.importContainer(fileUrl)
|
||||
@ -221,6 +221,7 @@ UM.ManagementPage
|
||||
messageDialog.icon = StandardIcon.Critical
|
||||
}
|
||||
messageDialog.open()
|
||||
CuraApplication.setDefaultPath("dialog_material_path", folder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +231,7 @@ UM.ManagementPage
|
||||
title: catalog.i18nc("@title:window", "Export Material");
|
||||
selectExisting: false;
|
||||
nameFilters: Cura.ContainerManager.getContainerNameFilters("material")
|
||||
folder: CuraApplication.getDefaultPath()
|
||||
folder: CuraApplication.getDefaultPath("dialog_material_path")
|
||||
onAccepted:
|
||||
{
|
||||
if(base.currentItem.metadata.base_file)
|
||||
@ -255,6 +256,7 @@ UM.ManagementPage
|
||||
messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to <filename>%1</filename>").arg(fileUrl)
|
||||
messageDialog.open()
|
||||
}
|
||||
CuraApplication.setDefaultPath("dialog_material_path", folder)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ UM.ManagementPage
|
||||
title: catalog.i18nc("@title:window", "Import Profile");
|
||||
selectExisting: true;
|
||||
nameFilters: base.model.getFileNameFilters("profile_reader")
|
||||
folder: base.model.getDefaultPath()
|
||||
folder: CuraApplication.getDefaultPath("dialog_profile_path")
|
||||
onAccepted:
|
||||
{
|
||||
var result = base.model.importProfile(fileUrl)
|
||||
@ -309,6 +309,7 @@ UM.ManagementPage
|
||||
messageDialog.icon = StandardIcon.Critical
|
||||
}
|
||||
messageDialog.open()
|
||||
CuraApplication.setDefaultPath("dialog_profile_path", folder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,7 +319,7 @@ UM.ManagementPage
|
||||
title: catalog.i18nc("@title:window", "Export Profile");
|
||||
selectExisting: false;
|
||||
nameFilters: base.model.getFileNameFilters("profile_writer")
|
||||
folder: base.model.getDefaultPath()
|
||||
folder: CuraApplication.getDefaultPath("dialog_profile_path")
|
||||
onAccepted:
|
||||
{
|
||||
var result = base.model.exportProfile(base.currentItem.id, fileUrl, selectedNameFilter)
|
||||
@ -329,6 +330,7 @@ UM.ManagementPage
|
||||
messageDialog.open()
|
||||
}
|
||||
// else pop-up Message thing from python code
|
||||
CuraApplication.setDefaultPath("dialog_profile_path", folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,8 +215,9 @@ Item {
|
||||
// This ensures that the value in any of the deeper containers need not be removed, which is
|
||||
// needed for the reset button (which deletes the top value) to correctly go back to profile
|
||||
// defaults.
|
||||
propertyProvider.setPropertyValue("value", propertyProvider.getPropertyValue("value", last_entry))
|
||||
propertyProvider.setPropertyValue("state", "InstanceState.Calculated")
|
||||
propertyProvider.setPropertyValue("value", propertyProvider.getPropertyValue("value", last_entry))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,14 +248,5 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onBlurSettings:
|
||||
{
|
||||
revertButton.focus = true
|
||||
}
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ Item
|
||||
}
|
||||
Label{
|
||||
id: infillLabel
|
||||
font: UM.Theme.getFont("default")
|
||||
anchors.top: infillIconLining.bottom
|
||||
anchors.horizontalCenter: infillIconLining.horizontalCenter
|
||||
color: infillListView.activeIndex == index ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_border")
|
||||
|
Loading…
x
Reference in New Issue
Block a user