mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-15 10:47:49 -06:00
Fix merge conflicts after multi-buildplate refactor
This commit is contained in:
commit
c7c989f2a0
87 changed files with 12891 additions and 740 deletions
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
timeout(time: 2, unit: "HOURS") {
|
||||||
parallel_nodes(['linux && cura', 'windows && cura']) {
|
parallel_nodes(['linux && cura', 'windows && cura']) {
|
||||||
// Prepare building
|
// Prepare building
|
||||||
stage('Prepare') {
|
stage('Prepare') {
|
||||||
|
@ -43,3 +44,4 @@ parallel_nodes(['linux && cura', 'windows && cura']) {
|
||||||
notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master', '2.'])
|
notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master', '2.'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,16 +24,23 @@ function(cura_add_test)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
string(REPLACE "|" "\\;" _PYTHONPATH ${_PYTHONPATH})
|
string(REPLACE "|" "\\;" _PYTHONPATH ${_PYTHONPATH})
|
||||||
|
set(_PYTHONPATH "${_PYTHONPATH}\\;$ENV{PYTHONPATH}")
|
||||||
else()
|
else()
|
||||||
string(REPLACE "|" ":" _PYTHONPATH ${_PYTHONPATH})
|
string(REPLACE "|" ":" _PYTHONPATH ${_PYTHONPATH})
|
||||||
|
set(_PYTHONPATH "${_PYTHONPATH}:$ENV{PYTHONPATH}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
get_test_property(${_NAME} ENVIRONMENT test_exists) #Find out if the test exists by getting a property from it that always exists (such as ENVIRONMENT because we set that ourselves).
|
||||||
|
if (NOT ${test_exists})
|
||||||
add_test(
|
add_test(
|
||||||
NAME ${_NAME}
|
NAME ${_NAME}
|
||||||
COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
||||||
)
|
)
|
||||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
||||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
||||||
|
else()
|
||||||
|
message(WARNING "Duplicate test ${_NAME}!")
|
||||||
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
cura_add_test(NAME pytest-main DIRECTORY ${CMAKE_SOURCE_DIR}/tests PYTHONPATH "${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
|
cura_add_test(NAME pytest-main DIRECTORY ${CMAKE_SOURCE_DIR}/tests PYTHONPATH "${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
|
||||||
|
|
25
cura/Arrange.py → cura/Arranging/Arrange.py
Executable file → Normal file
25
cura/Arrange.py → cura/Arranging/Arrange.py
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from cura.ShapeArray import ShapeArray
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
from cura import ZOffsetDecorator
|
from cura.Scene import ZOffsetDecorator
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ class Arrange:
|
||||||
self._offset_x = offset_x
|
self._offset_x = offset_x
|
||||||
self._offset_y = offset_y
|
self._offset_y = offset_y
|
||||||
self._last_priority = 0
|
self._last_priority = 0
|
||||||
|
self._is_empty = True
|
||||||
|
|
||||||
## Helper to create an Arranger instance
|
## Helper to create an Arranger instance
|
||||||
#
|
#
|
||||||
|
@ -38,8 +39,8 @@ class Arrange:
|
||||||
# \param scene_root Root for finding all scene nodes
|
# \param scene_root Root for finding all scene nodes
|
||||||
# \param fixed_nodes Scene nodes to be placed
|
# \param fixed_nodes Scene nodes to be placed
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5):
|
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 220, y = 220):
|
||||||
arranger = Arrange(220, 220, 110, 110, scale = scale)
|
arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
|
||||||
arranger.centerFirst()
|
arranger.centerFirst()
|
||||||
|
|
||||||
if fixed_nodes is None:
|
if fixed_nodes is None:
|
||||||
|
@ -64,7 +65,7 @@ class Arrange:
|
||||||
for area in disallowed_areas:
|
for area in disallowed_areas:
|
||||||
points = copy.deepcopy(area._points)
|
points = copy.deepcopy(area._points)
|
||||||
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
|
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
|
||||||
arranger.place(0, 0, shape_arr)
|
arranger.place(0, 0, shape_arr, update_empty = False)
|
||||||
return arranger
|
return arranger
|
||||||
|
|
||||||
## Find placement for a node (using offset shape) and place it (using hull shape)
|
## Find placement for a node (using offset shape) and place it (using hull shape)
|
||||||
|
@ -168,7 +169,8 @@ class Arrange:
|
||||||
# \param x x-coordinate
|
# \param x x-coordinate
|
||||||
# \param y y-coordinate
|
# \param y y-coordinate
|
||||||
# \param shape_arr ShapeArray object
|
# \param shape_arr ShapeArray object
|
||||||
def place(self, x, y, shape_arr):
|
# \param update_empty updates the _is_empty, used when adding disallowed areas
|
||||||
|
def place(self, x, y, shape_arr, update_empty = True):
|
||||||
x = int(self._scale * x)
|
x = int(self._scale * x)
|
||||||
y = int(self._scale * y)
|
y = int(self._scale * y)
|
||||||
offset_x = x + self._offset_x + shape_arr.offset_x
|
offset_x = x + self._offset_x + shape_arr.offset_x
|
||||||
|
@ -181,10 +183,17 @@ class Arrange:
|
||||||
max_y = min(max(offset_y + shape_arr.arr.shape[0], 0), shape_y - 1)
|
max_y = min(max(offset_y + shape_arr.arr.shape[0], 0), shape_y - 1)
|
||||||
occupied_slice = self._occupied[min_y:max_y, min_x:max_x]
|
occupied_slice = self._occupied[min_y:max_y, min_x:max_x]
|
||||||
# we use a slice of shape because it can be out of bounds
|
# we use a slice of shape because it can be out of bounds
|
||||||
occupied_slice[numpy.where(shape_arr.arr[
|
new_occupied = numpy.where(shape_arr.arr[
|
||||||
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 1
|
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)
|
||||||
|
if update_empty and new_occupied:
|
||||||
|
self._is_empty = False
|
||||||
|
occupied_slice[new_occupied] = 1
|
||||||
|
|
||||||
# Set priority to low (= high number), so it won't get picked at trying out.
|
# Set priority to low (= high number), so it won't get picked at trying out.
|
||||||
prio_slice = self._priority[min_y:max_y, min_x:max_x]
|
prio_slice = self._priority[min_y:max_y, min_x:max_x]
|
||||||
prio_slice[numpy.where(shape_arr.arr[
|
prio_slice[numpy.where(shape_arr.arr[
|
||||||
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 999
|
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 999
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isEmpty(self):
|
||||||
|
return self._is_empty
|
154
cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py
Normal file
154
cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.Job import Job
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Math.Vector import Vector
|
||||||
|
from UM.Operations.TranslateOperation import TranslateOperation
|
||||||
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
|
from UM.Message import Message
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||||
|
from cura.Arranging.Arrange import Arrange
|
||||||
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class ArrangeArray:
|
||||||
|
def __init__(self, x: int, y: int, fixed_nodes: List[SceneNode]):
|
||||||
|
self._x = x
|
||||||
|
self._y = y
|
||||||
|
self._fixed_nodes = fixed_nodes
|
||||||
|
self._count = 0
|
||||||
|
self._first_empty = None
|
||||||
|
self._has_empty = False
|
||||||
|
self._arrange = []
|
||||||
|
|
||||||
|
def _update_first_empty(self):
|
||||||
|
for i, a in enumerate(self._arrange):
|
||||||
|
if a.isEmpty:
|
||||||
|
self._first_empty = i
|
||||||
|
self._has_empty = True
|
||||||
|
return
|
||||||
|
self._first_empty = None
|
||||||
|
self._has_empty = False
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
new_arrange = Arrange.create(x = self._x, y = self._y, fixed_nodes = self._fixed_nodes)
|
||||||
|
self._arrange.append(new_arrange)
|
||||||
|
self._count += 1
|
||||||
|
self._update_first_empty()
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return self._count
|
||||||
|
|
||||||
|
def get(self, index):
|
||||||
|
return self._arrange[index]
|
||||||
|
|
||||||
|
def getFirstEmpty(self):
|
||||||
|
if not self._is_empty:
|
||||||
|
self.add()
|
||||||
|
return self._arrange[self._first_empty]
|
||||||
|
|
||||||
|
|
||||||
|
class ArrangeObjectsAllBuildPlatesJob(Job):
|
||||||
|
def __init__(self, nodes: List[SceneNode], min_offset = 8):
|
||||||
|
super().__init__()
|
||||||
|
self._nodes = nodes
|
||||||
|
self._min_offset = min_offset
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"),
|
||||||
|
lifetime = 0,
|
||||||
|
dismissable=False,
|
||||||
|
progress = 0,
|
||||||
|
title = i18n_catalog.i18nc("@info:title", "Finding Location"))
|
||||||
|
status_message.show()
|
||||||
|
|
||||||
|
|
||||||
|
# Collect nodes to be placed
|
||||||
|
nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr)
|
||||||
|
for node in self._nodes:
|
||||||
|
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
|
||||||
|
nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))
|
||||||
|
|
||||||
|
# Sort the nodes with the biggest area first.
|
||||||
|
nodes_arr.sort(key=lambda item: item[0])
|
||||||
|
nodes_arr.reverse()
|
||||||
|
|
||||||
|
x, y = 200, 200
|
||||||
|
|
||||||
|
arrange_array = ArrangeArray(x = x, y = y, fixed_nodes = [])
|
||||||
|
arrange_array.add()
|
||||||
|
|
||||||
|
# Place nodes one at a time
|
||||||
|
start_priority = 0
|
||||||
|
grouped_operation = GroupedOperation()
|
||||||
|
found_solution_for_all = True
|
||||||
|
left_over_nodes = [] # nodes that do not fit on an empty build plate
|
||||||
|
|
||||||
|
for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr):
|
||||||
|
# For performance reasons, we assume that when a location does not fit,
|
||||||
|
# it will also not fit for the next object (while what can be untrue).
|
||||||
|
# We also skip possibilities by slicing through the possibilities (step = 10)
|
||||||
|
|
||||||
|
try_placement = True
|
||||||
|
|
||||||
|
current_build_plate_number = 0 # always start with the first one
|
||||||
|
|
||||||
|
# # Only for first build plate
|
||||||
|
# if last_size == size and last_build_plate_number == current_build_plate_number:
|
||||||
|
# # This optimization works if many of the objects have the same size
|
||||||
|
# # Continue with same build plate number
|
||||||
|
# start_priority = last_priority
|
||||||
|
# else:
|
||||||
|
# start_priority = 0
|
||||||
|
|
||||||
|
while try_placement:
|
||||||
|
# make sure that current_build_plate_number is not going crazy or you'll have a lot of arrange objects
|
||||||
|
while current_build_plate_number >= arrange_array.count():
|
||||||
|
arrange_array.add()
|
||||||
|
arranger = arrange_array.get(current_build_plate_number)
|
||||||
|
|
||||||
|
best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10)
|
||||||
|
x, y = best_spot.x, best_spot.y
|
||||||
|
node.removeDecorator(ZOffsetDecorator)
|
||||||
|
if node.getBoundingBox():
|
||||||
|
center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
|
||||||
|
else:
|
||||||
|
center_y = 0
|
||||||
|
if x is not None: # We could find a place
|
||||||
|
arranger.place(x, y, hull_shape_arr) # place the object in the arranger
|
||||||
|
|
||||||
|
node.callDecoration("setBuildPlateNumber", current_build_plate_number)
|
||||||
|
grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
|
||||||
|
try_placement = False
|
||||||
|
else:
|
||||||
|
# very naive, because we skip to the next build plate if one model doesn't fit.
|
||||||
|
if arranger.isEmpty:
|
||||||
|
# apparently we can never place this object
|
||||||
|
left_over_nodes.append(node)
|
||||||
|
try_placement = False
|
||||||
|
else:
|
||||||
|
# try next build plate
|
||||||
|
current_build_plate_number += 1
|
||||||
|
try_placement = True
|
||||||
|
|
||||||
|
status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
|
||||||
|
Job.yieldThread()
|
||||||
|
|
||||||
|
for node in left_over_nodes:
|
||||||
|
node.callDecoration("setBuildPlateNumber", -1) # these are not on any build plate
|
||||||
|
found_solution_for_all = False
|
||||||
|
|
||||||
|
grouped_operation.push()
|
||||||
|
|
||||||
|
status_message.hide()
|
||||||
|
|
||||||
|
if not found_solution_for_all:
|
||||||
|
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"),
|
||||||
|
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"))
|
||||||
|
no_full_solution_message.show()
|
7
cura/ArrangeObjectsJob.py → cura/Arranging/ArrangeObjectsJob.py
Executable file → Normal file
7
cura/ArrangeObjectsJob.py → cura/Arranging/ArrangeObjectsJob.py
Executable file → Normal file
|
@ -4,7 +4,6 @@
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
|
||||||
from UM.Operations.TranslateOperation import TranslateOperation
|
from UM.Operations.TranslateOperation import TranslateOperation
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
@ -12,9 +11,9 @@ from UM.Message import Message
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||||
from cura.Arrange import Arrange
|
from cura.Arranging.Arrange import Arrange
|
||||||
from cura.ShapeArray import ShapeArray
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
7
cura/ShapeArray.py → cura/Arranging/ShapeArray.py
Executable file → Normal file
7
cura/ShapeArray.py → cura/Arranging/ShapeArray.py
Executable file → Normal file
|
@ -43,13 +43,12 @@ class ShapeArray:
|
||||||
transform_x = transform._data[0][3]
|
transform_x = transform._data[0][3]
|
||||||
transform_y = transform._data[2][3]
|
transform_y = transform._data[2][3]
|
||||||
hull_verts = node.callDecoration("getConvexHull")
|
hull_verts = node.callDecoration("getConvexHull")
|
||||||
|
# If a model is too small then it will not contain any points
|
||||||
|
if hull_verts is None or not hull_verts.getPoints().any():
|
||||||
|
return None, None
|
||||||
# For one_at_a_time printing you need the convex hull head.
|
# For one_at_a_time printing you need the convex hull head.
|
||||||
hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts
|
hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts
|
||||||
|
|
||||||
# If a model is to small then it will not contain any points
|
|
||||||
if not hull_verts.getPoints().any():
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
|
offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
|
||||||
offset_points = copy.deepcopy(offset_verts._points) # x, y
|
offset_points = copy.deepcopy(offset_verts._points) # x, y
|
||||||
offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
|
offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
|
0
cura/Arranging/__init__.py
Normal file
0
cura/Arranging/__init__.py
Normal file
53
cura/BuildPlateModel.py
Normal file
53
cura/BuildPlateModel.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
|
from UM.Qt.ListModel import ListModel
|
||||||
|
from UM.Scene.Selection import Selection
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Application import Application
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPlateModel(ListModel):
|
||||||
|
maxBuildPlateChanged = pyqtSignal()
|
||||||
|
activeBuildPlateChanged = pyqtSignal()
|
||||||
|
selectionChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
|
||||||
|
Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
|
||||||
|
|
||||||
|
self._max_build_plate = 1 # default
|
||||||
|
self._active_build_plate = -1
|
||||||
|
self._selection_build_plates = []
|
||||||
|
|
||||||
|
def setMaxBuildPlate(self, max_build_plate):
|
||||||
|
self._max_build_plate = max_build_plate
|
||||||
|
self.maxBuildPlateChanged.emit()
|
||||||
|
|
||||||
|
## Return the highest build plate number
|
||||||
|
@pyqtProperty(int, notify = maxBuildPlateChanged)
|
||||||
|
def maxBuildPlate(self):
|
||||||
|
return self._max_build_plate
|
||||||
|
|
||||||
|
def setActiveBuildPlate(self, nr):
|
||||||
|
self._active_build_plate = nr
|
||||||
|
self.activeBuildPlateChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify = activeBuildPlateChanged)
|
||||||
|
def activeBuildPlate(self):
|
||||||
|
return self._active_build_plate
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createBuildPlateModel():
|
||||||
|
return BuildPlateModel()
|
||||||
|
|
||||||
|
def _updateSelectedObjectBuildPlateNumbers(self, *args):
|
||||||
|
result = set()
|
||||||
|
for node in Selection.getAllSelectedObjects():
|
||||||
|
result.add(node.callDecoration("getBuildPlateNumber"))
|
||||||
|
self._selection_build_plates = list(result)
|
||||||
|
self.selectionChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantList", notify = selectionChanged)
|
||||||
|
def selectionBuildPlates(self):
|
||||||
|
return self._selection_build_plates
|
|
@ -13,12 +13,18 @@ from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||||
|
from UM.Operations.TranslateOperation import TranslateOperation
|
||||||
|
|
||||||
from cura.SetParentOperation import SetParentOperation
|
from cura.Operations.SetParentOperation import SetParentOperation
|
||||||
from cura.MultiplyObjectsJob import MultiplyObjectsJob
|
from cura.MultiplyObjectsJob import MultiplyObjectsJob
|
||||||
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
|
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
|
from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOperation
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
|
||||||
class CuraActions(QObject):
|
class CuraActions(QObject):
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
@ -54,7 +60,11 @@ class CuraActions(QObject):
|
||||||
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
|
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
|
||||||
current_node = current_node.getParent()
|
current_node = current_node.getParent()
|
||||||
|
|
||||||
center_operation = SetTransformOperation(current_node, Vector())
|
# This was formerly done with SetTransformOperation but because of
|
||||||
|
# unpredictable matrix deconstruction it was possible that mirrors
|
||||||
|
# could manifest as rotations. Centering is therefore done by
|
||||||
|
# moving the node to negative whatever its position is:
|
||||||
|
center_operation = TranslateOperation(current_node, -current_node._position)
|
||||||
operation.addOperation(center_operation)
|
operation.addOperation(center_operation)
|
||||||
operation.push()
|
operation.push()
|
||||||
|
|
||||||
|
@ -124,5 +134,31 @@ class CuraActions(QObject):
|
||||||
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
|
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
|
||||||
operation.push()
|
operation.push()
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
|
||||||
|
Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
|
||||||
|
operation = GroupedOperation()
|
||||||
|
|
||||||
|
root = Application.getInstance().getController().getScene().getRoot()
|
||||||
|
|
||||||
|
nodes_to_change = []
|
||||||
|
for node in Selection.getAllSelectedObjects():
|
||||||
|
parent_node = node # Find the parent node to change instead
|
||||||
|
while parent_node.getParent() != root:
|
||||||
|
parent_node = parent_node.getParent()
|
||||||
|
|
||||||
|
for single_node in BreadthFirstIterator(parent_node):
|
||||||
|
nodes_to_change.append(single_node)
|
||||||
|
|
||||||
|
if not nodes_to_change:
|
||||||
|
Logger.log("d", "Nothing to change.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for node in nodes_to_change:
|
||||||
|
operation.addOperation(SetBuildPlateNumberOperation(node, build_plate_nr))
|
||||||
|
operation.push()
|
||||||
|
|
||||||
|
Selection.clear()
|
||||||
|
|
||||||
def _openUrl(self, url):
|
def _openUrl(self, url):
|
||||||
QDesktopServices.openUrl(url)
|
QDesktopServices.openUrl(url)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright (c) 2017 Ultimaker B.V.
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from PyQt5.QtNetwork import QLocalServer
|
from PyQt5.QtNetwork import QLocalServer
|
||||||
from PyQt5.QtNetwork import QLocalSocket
|
from PyQt5.QtNetwork import QLocalSocket
|
||||||
|
@ -16,7 +17,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
from UM.Mesh.ReadMeshJob import ReadMeshJob
|
from UM.Mesh.ReadMeshJob import ReadMeshJob
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
from UM.SaveFile import SaveFile
|
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
from UM.Scene.GroupDecorator import GroupDecorator
|
from UM.Scene.GroupDecorator import GroupDecorator
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
|
@ -32,15 +32,19 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||||
|
|
||||||
from cura.Arrange import Arrange
|
from cura.Arranging.Arrange import Arrange
|
||||||
from cura.ShapeArray import ShapeArray
|
from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
|
||||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
|
||||||
from cura.SetParentOperation import SetParentOperation
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
|
||||||
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
|
||||||
|
|
||||||
from cura.ArrangeObjectsJob import ArrangeObjectsJob
|
|
||||||
from cura.MultiplyObjectsJob import MultiplyObjectsJob
|
from cura.MultiplyObjectsJob import MultiplyObjectsJob
|
||||||
|
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
from cura.Operations.SetParentOperation import SetParentOperation
|
||||||
|
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
|
from cura.Scene.BlockSlicingDecorator import BlockSlicingDecorator
|
||||||
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneController import CuraSceneController
|
||||||
|
|
||||||
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
@ -53,12 +57,13 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
||||||
from cura.Settings.UserProfilesModel import UserProfilesModel
|
from cura.Settings.UserProfilesModel import UserProfilesModel
|
||||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||||
|
|
||||||
|
|
||||||
from . import PlatformPhysics
|
from . import PlatformPhysics
|
||||||
from . import BuildVolume
|
from . import BuildVolume
|
||||||
from . import CameraAnimation
|
from . import CameraAnimation
|
||||||
from . import PrintInformation
|
from . import PrintInformation
|
||||||
from . import CuraActions
|
from . import CuraActions
|
||||||
from . import ZOffsetDecorator
|
from cura.Scene import ZOffsetDecorator
|
||||||
from . import CuraSplashScreen
|
from . import CuraSplashScreen
|
||||||
from . import CameraImageProvider
|
from . import CameraImageProvider
|
||||||
from . import MachineActionManager
|
from . import MachineActionManager
|
||||||
|
@ -72,8 +77,9 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
|
||||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||||
from cura.Settings.QualitySettingsModel import QualitySettingsModel
|
from cura.Settings.QualitySettingsModel import QualitySettingsModel
|
||||||
from cura.Settings.ContainerManager import ContainerManager
|
from cura.Settings.ContainerManager import ContainerManager
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
|
||||||
from cura.Settings.ExtruderStack import ExtruderStack
|
from cura.ObjectsModel import ObjectsModel
|
||||||
|
from cura.BuildPlateModel import BuildPlateModel
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
|
@ -85,7 +91,6 @@ import sys
|
||||||
import os.path
|
import os.path
|
||||||
import numpy
|
import numpy
|
||||||
import copy
|
import copy
|
||||||
import urllib.parse
|
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
@ -203,8 +208,11 @@ class CuraApplication(QtApplication):
|
||||||
self._machine_manager = None # This is initialized on demand.
|
self._machine_manager = None # This is initialized on demand.
|
||||||
self._extruder_manager = None
|
self._extruder_manager = None
|
||||||
self._material_manager = None
|
self._material_manager = None
|
||||||
|
self._object_manager = None
|
||||||
|
self._build_plate_model = None
|
||||||
self._setting_inheritance_manager = None
|
self._setting_inheritance_manager = None
|
||||||
self._simple_mode_settings_manager = None
|
self._simple_mode_settings_manager = None
|
||||||
|
self._cura_scene_controller = None
|
||||||
|
|
||||||
self._additional_components = {} # Components to add to certain areas in the interface
|
self._additional_components = {} # Components to add to certain areas in the interface
|
||||||
|
|
||||||
|
@ -311,12 +319,15 @@ class CuraApplication(QtApplication):
|
||||||
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
||||||
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
|
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
|
||||||
preferences.addPreference("cura/choice_on_open_project", "always_ask")
|
preferences.addPreference("cura/choice_on_open_project", "always_ask")
|
||||||
|
preferences.addPreference("cura/arrange_objects_on_load", True)
|
||||||
|
preferences.addPreference("cura/use_multi_build_plate", False)
|
||||||
|
|
||||||
preferences.addPreference("cura/currency", "€")
|
preferences.addPreference("cura/currency", "€")
|
||||||
preferences.addPreference("cura/material_settings", "{}")
|
preferences.addPreference("cura/material_settings", "{}")
|
||||||
|
|
||||||
preferences.addPreference("view/invert_zoom", False)
|
preferences.addPreference("view/invert_zoom", False)
|
||||||
preferences.addPreference("cura/sidebar_collapse", False)
|
preferences.addPreference("view/filter_current_build_plate", False)
|
||||||
|
preferences.addPreference("cura/sidebar_collapsed", False)
|
||||||
|
|
||||||
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
|
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
|
||||||
|
|
||||||
|
@ -389,6 +400,8 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
|
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
|
||||||
|
|
||||||
|
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
||||||
|
|
||||||
def _onEngineCreated(self):
|
def _onEngineCreated(self):
|
||||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||||
|
|
||||||
|
@ -733,6 +746,22 @@ class CuraApplication(QtApplication):
|
||||||
self._material_manager = MaterialManager.createMaterialManager()
|
self._material_manager = MaterialManager.createMaterialManager()
|
||||||
return self._material_manager
|
return self._material_manager
|
||||||
|
|
||||||
|
def getObjectsModel(self, *args):
|
||||||
|
if self._object_manager is None:
|
||||||
|
self._object_manager = ObjectsModel.createObjectsModel()
|
||||||
|
return self._object_manager
|
||||||
|
|
||||||
|
def getBuildPlateModel(self, *args):
|
||||||
|
if self._build_plate_model is None:
|
||||||
|
self._build_plate_model = BuildPlateModel.createBuildPlateModel()
|
||||||
|
|
||||||
|
return self._build_plate_model
|
||||||
|
|
||||||
|
def getCuraSceneController(self, *args):
|
||||||
|
if self._cura_scene_controller is None:
|
||||||
|
self._cura_scene_controller = CuraSceneController.createCuraSceneController()
|
||||||
|
return self._cura_scene_controller
|
||||||
|
|
||||||
def getSettingInheritanceManager(self, *args):
|
def getSettingInheritanceManager(self, *args):
|
||||||
if self._setting_inheritance_manager is None:
|
if self._setting_inheritance_manager is None:
|
||||||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||||
|
@ -777,6 +806,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||||
|
|
||||||
|
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)
|
||||||
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
|
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
|
||||||
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
|
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
|
||||||
qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
|
qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
|
||||||
|
@ -784,6 +814,8 @@ class CuraApplication(QtApplication):
|
||||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
||||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||||
|
|
||||||
|
qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
|
||||||
|
qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
|
||||||
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
||||||
qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
|
qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
|
||||||
qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel)
|
qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel)
|
||||||
|
@ -875,7 +907,7 @@ class CuraApplication(QtApplication):
|
||||||
scene_bounding_box = None
|
scene_bounding_box = None
|
||||||
is_block_slicing_node = False
|
is_block_slicing_node = False
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
|
if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")):
|
||||||
continue
|
continue
|
||||||
if node.callDecoration("isBlockSlicing"):
|
if node.callDecoration("isBlockSlicing"):
|
||||||
is_block_slicing_node = True
|
is_block_slicing_node = True
|
||||||
|
@ -992,7 +1024,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
Selection.clear()
|
Selection.clear()
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
@ -1000,6 +1032,9 @@ class CuraApplication(QtApplication):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue # i.e. node with layer data
|
||||||
|
|
||||||
Selection.add(node)
|
Selection.add(node)
|
||||||
|
|
||||||
## Delete all nodes containing mesh data in the scene.
|
## Delete all nodes containing mesh data in the scene.
|
||||||
|
@ -1011,7 +1046,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if type(node) not in {SceneNode, CuraSceneNode}:
|
||||||
continue
|
continue
|
||||||
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
@ -1027,13 +1062,15 @@ class CuraApplication(QtApplication):
|
||||||
op.push()
|
op.push()
|
||||||
Selection.clear()
|
Selection.clear()
|
||||||
|
|
||||||
|
self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
|
||||||
|
|
||||||
## Reset all translation on nodes with mesh data.
|
## Reset all translation on nodes with mesh data.
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def resetAllTranslation(self):
|
def resetAllTranslation(self):
|
||||||
Logger.log("i", "Resetting all scene translations")
|
Logger.log("i", "Resetting all scene translations")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
@ -1061,13 +1098,13 @@ class CuraApplication(QtApplication):
|
||||||
Logger.log("i", "Resetting all scene transformations")
|
Logger.log("i", "Resetting all scene transformations")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.callDecoration("isSliceable"):
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
|
@ -1085,10 +1122,31 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
## Arrange all objects.
|
## Arrange all objects.
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def arrangeAll(self):
|
def arrangeObjectsToAllBuildPlates(self):
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
|
continue
|
||||||
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||||
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue # i.e. node with layer data
|
||||||
|
# Skip nodes that are too big
|
||||||
|
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
||||||
|
nodes.append(node)
|
||||||
|
job = ArrangeObjectsAllBuildPlatesJob(nodes)
|
||||||
|
job.start()
|
||||||
|
self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
|
||||||
|
|
||||||
|
# Single build plate
|
||||||
|
@pyqtSlot()
|
||||||
|
def arrangeAll(self):
|
||||||
|
nodes = []
|
||||||
|
active_build_plate = self.getBuildPlateModel().activeBuildPlate
|
||||||
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
@ -1096,6 +1154,9 @@ class CuraApplication(QtApplication):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue # i.e. node with layer data
|
||||||
|
if node.callDecoration("getBuildPlateNumber") == active_build_plate:
|
||||||
# Skip nodes that are too big
|
# Skip nodes that are too big
|
||||||
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
@ -1109,7 +1170,7 @@ class CuraApplication(QtApplication):
|
||||||
# What nodes are on the build plate and are not being moved
|
# What nodes are on the build plate and are not being moved
|
||||||
fixed_nodes = []
|
fixed_nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
|
@ -1117,6 +1178,8 @@ class CuraApplication(QtApplication):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue # i.e. node with layer data
|
||||||
if node in nodes: # exclude selected node from fixed_nodes
|
if node in nodes: # exclude selected node from fixed_nodes
|
||||||
continue
|
continue
|
||||||
fixed_nodes.append(node)
|
fixed_nodes.append(node)
|
||||||
|
@ -1135,7 +1198,7 @@ class CuraApplication(QtApplication):
|
||||||
Logger.log("i", "Reloading all loaded mesh data.")
|
Logger.log("i", "Reloading all loaded mesh data.")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode or not node.getMeshData():
|
if not issubclass(type(node), SceneNode) or not node.getMeshData():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
@ -1226,10 +1289,11 @@ class CuraApplication(QtApplication):
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def groupSelected(self):
|
def groupSelected(self):
|
||||||
# Create a group-node
|
# Create a group-node
|
||||||
group_node = SceneNode()
|
group_node = CuraSceneNode()
|
||||||
group_decorator = GroupDecorator()
|
group_decorator = GroupDecorator()
|
||||||
group_node.addDecorator(group_decorator)
|
group_node.addDecorator(group_decorator)
|
||||||
group_node.addDecorator(ConvexHullDecorator())
|
group_node.addDecorator(ConvexHullDecorator())
|
||||||
|
group_node.addDecorator(BuildPlateDecorator(self.getBuildPlateModel().activeBuildPlate))
|
||||||
group_node.setParent(self.getController().getScene().getRoot())
|
group_node.setParent(self.getController().getScene().getRoot())
|
||||||
group_node.setSelectable(True)
|
group_node.setSelectable(True)
|
||||||
center = Selection.getSelectionCenter()
|
center = Selection.getSelectionCenter()
|
||||||
|
@ -1374,8 +1438,15 @@ class CuraApplication(QtApplication):
|
||||||
min_offset = 8
|
min_offset = 8
|
||||||
|
|
||||||
self.fileLoaded.emit(filename)
|
self.fileLoaded.emit(filename)
|
||||||
|
arrange_objects_on_load = (
|
||||||
|
not Preferences.getInstance().getValue("cura/use_multi_build_plate") or
|
||||||
|
Preferences.getInstance().getValue("cura/arrange_objects_on_load"))
|
||||||
|
target_build_plate = self.getBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
|
||||||
|
|
||||||
|
for original_node in nodes:
|
||||||
|
node = CuraSceneNode() # We want our own CuraSceneNode
|
||||||
|
node.setMeshData(original_node.getMeshData())
|
||||||
|
|
||||||
for node in nodes:
|
|
||||||
node.setSelectable(True)
|
node.setSelectable(True)
|
||||||
node.setName(os.path.basename(filename))
|
node.setName(os.path.basename(filename))
|
||||||
|
|
||||||
|
@ -1402,22 +1473,24 @@ class CuraApplication(QtApplication):
|
||||||
if not child.getDecorator(ConvexHullDecorator):
|
if not child.getDecorator(ConvexHullDecorator):
|
||||||
child.addDecorator(ConvexHullDecorator())
|
child.addDecorator(ConvexHullDecorator())
|
||||||
|
|
||||||
|
if arrange_objects_on_load:
|
||||||
if node.callDecoration("isSliceable"):
|
if node.callDecoration("isSliceable"):
|
||||||
# Only check position if it's not already blatantly obvious that it won't fit.
|
# Only check position if it's not already blatantly obvious that it won't fit.
|
||||||
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
if node.getBoundingBox() is None or self._volume.getBoundingBox() is None or node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
||||||
# Find node location
|
# Find node location
|
||||||
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = min_offset)
|
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = min_offset)
|
||||||
|
|
||||||
# If a model is to small then it will not contain any points
|
# If a model is to small then it will not contain any points
|
||||||
if offset_shape_arr is None and hull_shape_arr is None:
|
if offset_shape_arr is None and hull_shape_arr is None:
|
||||||
Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
|
Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
|
||||||
title=self._i18n_catalog.i18nc("@info:title", "Warning")
|
title=self._i18n_catalog.i18nc("@info:title", "Warning")).show()
|
||||||
).show()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
|
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
|
||||||
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
|
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
|
||||||
|
|
||||||
|
node.addDecorator(BuildPlateDecorator(target_build_plate))
|
||||||
|
|
||||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||||
op.push()
|
op.push()
|
||||||
scene.sceneChanged.emit(node)
|
scene.sceneChanged.emit(node)
|
||||||
|
|
|
@ -2,24 +2,15 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from UM.Math.Vector import Vector
|
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
|
||||||
from UM.Operations.TranslateOperation import TranslateOperation
|
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Logger import Logger
|
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
from cura.Arranging.Arrange import Arrange
|
||||||
from cura.Arrange import Arrange
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
from cura.ShapeArray import ShapeArray
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Scene.Selection import Selection
|
|
||||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,6 +56,10 @@ class MultiplyObjectsJob(Job):
|
||||||
new_location = new_location.set(z = 100 - i * 20)
|
new_location = new_location.set(z = 100 - i * 20)
|
||||||
node.setPosition(new_location)
|
node.setPosition(new_location)
|
||||||
|
|
||||||
|
# Same build plate
|
||||||
|
build_plate_number = current_node.callDecoration("getBuildPlateNumber")
|
||||||
|
node.callDecoration("setBuildPlateNumber", build_plate_number)
|
||||||
|
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
current_progress += 1
|
current_progress += 1
|
||||||
status_message.setProgress((current_progress / total_progress) * 100)
|
status_message.setProgress((current_progress / total_progress) * 100)
|
||||||
|
|
49
cura/ObjectsModel.py
Normal file
49
cura/ObjectsModel.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Qt.ListModel import ListModel
|
||||||
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Scene.Selection import Selection
|
||||||
|
from UM.Preferences import Preferences
|
||||||
|
|
||||||
|
|
||||||
|
## Keep track of all objects in the project
|
||||||
|
class ObjectsModel(ListModel):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
Application.getInstance().getController().getScene().sceneChanged.connect(self._update)
|
||||||
|
Preferences.getInstance().preferenceChanged.connect(self._update)
|
||||||
|
|
||||||
|
self._build_plate_number = -1
|
||||||
|
|
||||||
|
def setActiveBuildPlate(self, nr):
|
||||||
|
self._build_plate_number = nr
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _update(self, *args):
|
||||||
|
nodes = []
|
||||||
|
filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate")
|
||||||
|
active_build_plate_number = self._build_plate_number
|
||||||
|
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
|
||||||
|
if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")):
|
||||||
|
continue
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue
|
||||||
|
node_build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||||
|
if filter_current_build_plate and node_build_plate_number != active_build_plate_number:
|
||||||
|
continue
|
||||||
|
nodes.append({
|
||||||
|
"name": node.getName(),
|
||||||
|
"isSelected": Selection.isSelected(node),
|
||||||
|
"isOutsideBuildArea": node.isOutsideBuildArea(),
|
||||||
|
"buildPlateNumber": node_build_plate_number,
|
||||||
|
"node": node
|
||||||
|
})
|
||||||
|
nodes = sorted(nodes, key=lambda n: n["name"])
|
||||||
|
self.setItems(nodes)
|
||||||
|
|
||||||
|
self.itemsChanged.emit()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createObjectsModel():
|
||||||
|
return ObjectsModel()
|
27
cura/Operations/SetBuildPlateNumberOperation.py
Normal file
27
cura/Operations/SetBuildPlateNumberOperation.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Operations.Operation import Operation
|
||||||
|
|
||||||
|
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||||
|
|
||||||
|
## Simple operation to set the buildplate number of a scenenode.
|
||||||
|
class SetBuildPlateNumberOperation(Operation):
|
||||||
|
def __init__(self, node: SceneNode, build_plate_nr: int) -> None:
|
||||||
|
self._node = node
|
||||||
|
self._build_plate_nr = build_plate_nr
|
||||||
|
self._previous_build_plate_nr = None
|
||||||
|
self._decorator_added = False
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._previous_build_plate_nr:
|
||||||
|
self._node.callDecoration("setBuildPlateNumber", self._previous_build_plate_nr)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
stack = self._node.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
|
||||||
|
if not stack:
|
||||||
|
self._node.addDecorator(SettingOverrideDecorator())
|
||||||
|
|
||||||
|
self._previous_build_plate_nr = self._node.callDecoration("getBuildPlateNumber")
|
||||||
|
self._node.callDecoration("setBuildPlateNumber", self._build_plate_nr)
|
0
cura/Operations/__init__.py
Normal file
0
cura/Operations/__init__.py
Normal file
|
@ -10,10 +10,10 @@ from UM.Math.Vector import Vector
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
|
|
||||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
|
||||||
from . import PlatformPhysicsOperation
|
from cura.Operations import PlatformPhysicsOperation
|
||||||
from . import ZOffsetDecorator
|
from cura.Scene import ZOffsetDecorator
|
||||||
|
|
||||||
import random # used for list shuffling
|
import random # used for list shuffling
|
||||||
|
|
||||||
|
@ -58,10 +58,11 @@ class PlatformPhysics:
|
||||||
|
|
||||||
# Only check nodes inside build area.
|
# Only check nodes inside build area.
|
||||||
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
|
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
|
||||||
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
|
||||||
random.shuffle(nodes)
|
random.shuffle(nodes)
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
if node is root or not issubclass(type(node), SceneNode) or node.getBoundingBox() is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
bbox = node.getBoundingBox()
|
bbox = node.getBoundingBox()
|
||||||
|
@ -82,7 +83,7 @@ class PlatformPhysics:
|
||||||
# Check for collisions between convex hulls
|
# Check for collisions between convex hulls
|
||||||
for other_node in BreadthFirstIterator(root):
|
for other_node in BreadthFirstIterator(root):
|
||||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||||
if other_node is root or type(other_node) is not SceneNode or other_node is node:
|
if other_node is root or not issubclass(type(other_node), SceneNode) or other_node is node or other_node.callDecoration("getBuildPlateNumber") != node.callDecoration("getBuildPlateNumber"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Ignore collisions of a group with it's own children
|
# Ignore collisions of a group with it's own children
|
||||||
|
|
|
@ -54,10 +54,10 @@ class PrintInformation(QObject):
|
||||||
|
|
||||||
self.initializeCuraMessagePrintTimeProperties()
|
self.initializeCuraMessagePrintTimeProperties()
|
||||||
|
|
||||||
self._material_lengths = []
|
self._material_lengths = {} # indexed by build plate number
|
||||||
self._material_weights = []
|
self._material_weights = {}
|
||||||
self._material_costs = []
|
self._material_costs = {}
|
||||||
self._material_names = []
|
self._material_names = {}
|
||||||
|
|
||||||
self._pre_sliced = False
|
self._pre_sliced = False
|
||||||
|
|
||||||
|
@ -68,10 +68,15 @@ class PrintInformation(QObject):
|
||||||
self._base_name = ""
|
self._base_name = ""
|
||||||
self._abbr_machine = ""
|
self._abbr_machine = ""
|
||||||
self._job_name = ""
|
self._job_name = ""
|
||||||
|
self._project_name = ""
|
||||||
|
self._active_build_plate = 0
|
||||||
|
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._updateJobName)
|
Application.getInstance().globalContainerStackChanged.connect(self._updateJobName)
|
||||||
Application.getInstance().fileLoaded.connect(self.setBaseName)
|
Application.getInstance().fileLoaded.connect(self.setBaseName)
|
||||||
|
Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
||||||
Application.getInstance().workspaceLoaded.connect(self.setProjectName)
|
Application.getInstance().workspaceLoaded.connect(self.setProjectName)
|
||||||
|
|
||||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||||
|
|
||||||
self._active_material_container = None
|
self._active_material_container = None
|
||||||
|
@ -83,7 +88,7 @@ class PrintInformation(QObject):
|
||||||
# Crate cura message translations and using translation keys initialize empty time Duration object for total time
|
# Crate cura message translations and using translation keys initialize empty time Duration object for total time
|
||||||
# and time for each feature
|
# and time for each feature
|
||||||
def initializeCuraMessagePrintTimeProperties(self):
|
def initializeCuraMessagePrintTimeProperties(self):
|
||||||
self._current_print_time = Duration(None, self)
|
self._current_print_time = {} # Duration(None, self)
|
||||||
|
|
||||||
self._print_time_message_translations = {
|
self._print_time_message_translations = {
|
||||||
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
||||||
|
@ -101,10 +106,26 @@ class PrintInformation(QObject):
|
||||||
|
|
||||||
self._print_time_message_values = {}
|
self._print_time_message_values = {}
|
||||||
|
|
||||||
# Full fill message values using keys from _print_time_message_translations
|
|
||||||
for key in self._print_time_message_translations.keys():
|
|
||||||
self._print_time_message_values[key] = Duration(None, self)
|
|
||||||
|
|
||||||
|
def _initPrintTimeMessageValues(self, build_plate_number):
|
||||||
|
# Full fill message values using keys from _print_time_message_translations
|
||||||
|
self._print_time_message_values[build_plate_number] = {}
|
||||||
|
for key in self._print_time_message_translations.keys():
|
||||||
|
self._print_time_message_values[build_plate_number][key] = Duration(None, self)
|
||||||
|
|
||||||
|
def _initVariablesWithBuildPlate(self, build_plate_number):
|
||||||
|
if build_plate_number not in self._print_time_message_values:
|
||||||
|
self._initPrintTimeMessageValues(build_plate_number)
|
||||||
|
if self._active_build_plate not in self._material_lengths:
|
||||||
|
self._material_lengths[self._active_build_plate] = []
|
||||||
|
if self._active_build_plate not in self._material_weights:
|
||||||
|
self._material_weights[self._active_build_plate] = []
|
||||||
|
if self._active_build_plate not in self._material_costs:
|
||||||
|
self._material_costs[self._active_build_plate] = []
|
||||||
|
if self._active_build_plate not in self._material_names:
|
||||||
|
self._material_names[self._active_build_plate] = []
|
||||||
|
if self._active_build_plate not in self._current_print_time:
|
||||||
|
self._current_print_time[self._active_build_plate] = Duration(None, self)
|
||||||
|
|
||||||
currentPrintTimeChanged = pyqtSignal()
|
currentPrintTimeChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@ -120,64 +141,71 @@ class PrintInformation(QObject):
|
||||||
|
|
||||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||||
def currentPrintTime(self):
|
def currentPrintTime(self):
|
||||||
return self._current_print_time
|
return self._current_print_time[self._active_build_plate]
|
||||||
|
|
||||||
materialLengthsChanged = pyqtSignal()
|
materialLengthsChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty("QVariantList", notify = materialLengthsChanged)
|
@pyqtProperty("QVariantList", notify = materialLengthsChanged)
|
||||||
def materialLengths(self):
|
def materialLengths(self):
|
||||||
return self._material_lengths
|
return self._material_lengths[self._active_build_plate]
|
||||||
|
|
||||||
materialWeightsChanged = pyqtSignal()
|
materialWeightsChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty("QVariantList", notify = materialWeightsChanged)
|
@pyqtProperty("QVariantList", notify = materialWeightsChanged)
|
||||||
def materialWeights(self):
|
def materialWeights(self):
|
||||||
return self._material_weights
|
return self._material_weights[self._active_build_plate]
|
||||||
|
|
||||||
materialCostsChanged = pyqtSignal()
|
materialCostsChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty("QVariantList", notify = materialCostsChanged)
|
@pyqtProperty("QVariantList", notify = materialCostsChanged)
|
||||||
def materialCosts(self):
|
def materialCosts(self):
|
||||||
return self._material_costs
|
return self._material_costs[self._active_build_plate]
|
||||||
|
|
||||||
materialNamesChanged = pyqtSignal()
|
materialNamesChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty("QVariantList", notify = materialNamesChanged)
|
@pyqtProperty("QVariantList", notify = materialNamesChanged)
|
||||||
def materialNames(self):
|
def materialNames(self):
|
||||||
return self._material_names
|
return self._material_names[self._active_build_plate]
|
||||||
|
|
||||||
def _onPrintDurationMessage(self, print_time, material_amounts):
|
def printTimes(self):
|
||||||
|
return self._print_time_message_values[self._active_build_plate]
|
||||||
|
|
||||||
self._updateTotalPrintTimePerFeature(print_time)
|
def _onPrintDurationMessage(self, build_plate_number, print_time, material_amounts):
|
||||||
|
self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
|
||||||
self.currentPrintTimeChanged.emit()
|
self.currentPrintTimeChanged.emit()
|
||||||
|
|
||||||
self._material_amounts = material_amounts
|
self._material_amounts = material_amounts
|
||||||
self._calculateInformation()
|
self._calculateInformation(build_plate_number)
|
||||||
|
|
||||||
def _updateTotalPrintTimePerFeature(self, print_time):
|
def _updateTotalPrintTimePerFeature(self, build_plate_number, print_time):
|
||||||
total_estimated_time = 0
|
total_estimated_time = 0
|
||||||
|
|
||||||
|
if build_plate_number not in self._print_time_message_values:
|
||||||
|
self._initPrintTimeMessageValues(build_plate_number)
|
||||||
|
|
||||||
for feature, time in print_time.items():
|
for feature, time in print_time.items():
|
||||||
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
||||||
self._print_time_message_values.get(feature).setDuration(0)
|
self._print_time_message_values[build_plate_number].get(feature).setDuration(0)
|
||||||
Logger.log("w", "Received NaN for print duration message")
|
Logger.log("w", "Received NaN for print duration message")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
total_estimated_time += time
|
total_estimated_time += time
|
||||||
self._print_time_message_values.get(feature).setDuration(time)
|
self._print_time_message_values[build_plate_number].get(feature).setDuration(time)
|
||||||
|
|
||||||
self._current_print_time.setDuration(total_estimated_time)
|
if build_plate_number not in self._current_print_time:
|
||||||
|
self._current_print_time[build_plate_number] = Duration(None, self)
|
||||||
|
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
||||||
|
|
||||||
def _calculateInformation(self):
|
def _calculateInformation(self, build_plate_number):
|
||||||
if Application.getInstance().getGlobalContainerStack() is None:
|
if Application.getInstance().getGlobalContainerStack() is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||||
radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
||||||
self._material_lengths = []
|
self._material_lengths[build_plate_number] = []
|
||||||
self._material_weights = []
|
self._material_weights[build_plate_number] = []
|
||||||
self._material_costs = []
|
self._material_costs[build_plate_number] = []
|
||||||
self._material_names = []
|
self._material_names[build_plate_number] = []
|
||||||
|
|
||||||
material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings"))
|
material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings"))
|
||||||
|
|
||||||
|
@ -215,10 +243,10 @@ class PrintInformation(QObject):
|
||||||
length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
|
length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
|
||||||
else:
|
else:
|
||||||
length = 0
|
length = 0
|
||||||
self._material_weights.append(weight)
|
self._material_weights[build_plate_number].append(weight)
|
||||||
self._material_lengths.append(length)
|
self._material_lengths[build_plate_number].append(length)
|
||||||
self._material_costs.append(cost)
|
self._material_costs[build_plate_number].append(cost)
|
||||||
self._material_names.append(material_name)
|
self._material_names[build_plate_number].append(material_name)
|
||||||
|
|
||||||
self.materialLengthsChanged.emit()
|
self.materialLengthsChanged.emit()
|
||||||
self.materialWeightsChanged.emit()
|
self.materialWeightsChanged.emit()
|
||||||
|
@ -229,7 +257,8 @@ class PrintInformation(QObject):
|
||||||
if preference != "cura/material_settings":
|
if preference != "cura/material_settings":
|
||||||
return
|
return
|
||||||
|
|
||||||
self._calculateInformation()
|
for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
|
||||||
|
self._calculateInformation(build_plate_number)
|
||||||
|
|
||||||
def _onActiveMaterialChanged(self):
|
def _onActiveMaterialChanged(self):
|
||||||
if self._active_material_container:
|
if self._active_material_container:
|
||||||
|
@ -245,8 +274,22 @@ class PrintInformation(QObject):
|
||||||
self._active_material_container = active_material_containers[0]
|
self._active_material_container = active_material_containers[0]
|
||||||
self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged)
|
self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged)
|
||||||
|
|
||||||
|
def _onActiveBuildPlateChanged(self):
|
||||||
|
new_active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
if new_active_build_plate != self._active_build_plate:
|
||||||
|
self._active_build_plate = new_active_build_plate
|
||||||
|
|
||||||
|
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||||
|
|
||||||
|
self.materialLengthsChanged.emit()
|
||||||
|
self.materialWeightsChanged.emit()
|
||||||
|
self.materialCostsChanged.emit()
|
||||||
|
self.materialNamesChanged.emit()
|
||||||
|
self.currentPrintTimeChanged.emit()
|
||||||
|
|
||||||
def _onMaterialMetaDataChanged(self, *args, **kwargs):
|
def _onMaterialMetaDataChanged(self, *args, **kwargs):
|
||||||
self._calculateInformation()
|
for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
|
||||||
|
self._calculateInformation(build_plate_number)
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def setJobName(self, name):
|
def setJobName(self, name):
|
||||||
|
@ -340,7 +383,9 @@ class PrintInformation(QObject):
|
||||||
@pyqtSlot(result = "QVariantMap")
|
@pyqtSlot(result = "QVariantMap")
|
||||||
def getFeaturePrintTimes(self):
|
def getFeaturePrintTimes(self):
|
||||||
result = {}
|
result = {}
|
||||||
for feature, time in self._print_time_message_values.items():
|
if self._active_build_plate not in self._print_time_message_values:
|
||||||
|
self._initPrintTimeMessageValues(self._active_build_plate)
|
||||||
|
for feature, time in self._print_time_message_values[self._active_build_plate].items():
|
||||||
if feature in self._print_time_message_translations:
|
if feature in self._print_time_message_translations:
|
||||||
result[self._print_time_message_translations[feature]] = time
|
result[self._print_time_message_translations[feature]] = time
|
||||||
else:
|
else:
|
||||||
|
@ -348,10 +393,12 @@ class PrintInformation(QObject):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Simulate message with zero time duration
|
# Simulate message with zero time duration
|
||||||
def setToZeroPrintInformation(self):
|
def setToZeroPrintInformation(self, build_plate_number):
|
||||||
temp_message = {}
|
temp_message = {}
|
||||||
for key in self._print_time_message_values.keys():
|
if build_plate_number not in self._print_time_message_values:
|
||||||
|
self._print_time_message_values[build_plate_number] = {}
|
||||||
|
for key in self._print_time_message_values[build_plate_number].keys():
|
||||||
temp_message[key] = 0
|
temp_message[key] = 0
|
||||||
|
|
||||||
temp_material_amounts = [0]
|
temp_material_amounts = [0]
|
||||||
self._onPrintDurationMessage(temp_message, temp_material_amounts)
|
self._onPrintDurationMessage(build_plate_number, temp_message, temp_material_amounts)
|
||||||
|
|
26
cura/Scene/BuildPlateDecorator.py
Normal file
26
cura/Scene/BuildPlateDecorator.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
|
|
||||||
|
## Make a SceneNode build plate aware CuraSceneNode objects all have this decorator.
|
||||||
|
class BuildPlateDecorator(SceneNodeDecorator):
|
||||||
|
def __init__(self, build_plate_number = -1):
|
||||||
|
super().__init__()
|
||||||
|
self._build_plate_number = None
|
||||||
|
self.setBuildPlateNumber(build_plate_number)
|
||||||
|
|
||||||
|
def setBuildPlateNumber(self, nr):
|
||||||
|
# Make sure that groups are set correctly
|
||||||
|
# setBuildPlateForSelection in CuraActions makes sure that no single childs are set.
|
||||||
|
self._build_plate_number = nr
|
||||||
|
if issubclass(type(self._node), CuraSceneNode):
|
||||||
|
self._node.transformChanged() # trigger refresh node without introducing a new signal
|
||||||
|
if self._node and self._node.callDecoration("isGroup"):
|
||||||
|
for child in self._node.getChildren():
|
||||||
|
child.callDecoration("setBuildPlateNumber", nr)
|
||||||
|
|
||||||
|
def getBuildPlateNumber(self):
|
||||||
|
return self._build_plate_number
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
return BuildPlateDecorator()
|
|
@ -7,7 +7,7 @@ from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from . import ConvexHullNode
|
from cura.Scene import ConvexHullNode
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
|
@ -6,7 +6,6 @@ from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
from UM.Math.Color import Color
|
from UM.Math.Color import Color
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
|
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
|
||||||
|
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ class ConvexHullNode(SceneNode):
|
||||||
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
||||||
|
|
||||||
if self.getParent():
|
if self.getParent():
|
||||||
if self.getMeshData():
|
if self.getMeshData() and issubclass(type(self._node), SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate:
|
||||||
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
||||||
if self._convex_hull_head_mesh:
|
if self._convex_hull_head_mesh:
|
||||||
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
101
cura/Scene/CuraSceneController.py
Normal file
101
cura/Scene/CuraSceneController.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt, pyqtSlot, QObject
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
|
from cura.ObjectsModel import ObjectsModel
|
||||||
|
from cura.BuildPlateModel import BuildPlateModel
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Scene.Selection import Selection
|
||||||
|
|
||||||
|
|
||||||
|
class CuraSceneController(QObject):
|
||||||
|
def __init__(self, objects_model: ObjectsModel, build_plate_model: BuildPlateModel):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._objects_model = objects_model
|
||||||
|
self._build_plate_model = build_plate_model
|
||||||
|
self._active_build_plate = -1
|
||||||
|
|
||||||
|
self._last_selected_index = 0
|
||||||
|
self._max_build_plate = 1 # default
|
||||||
|
|
||||||
|
Application.getInstance().getController().getScene().sceneChanged.connect(self.updateMaxBuildPlate) # it may be a bit inefficient when changing a lot simultaneously
|
||||||
|
|
||||||
|
def updateMaxBuildPlate(self, *args):
|
||||||
|
if args:
|
||||||
|
source = args[0]
|
||||||
|
else:
|
||||||
|
source = None
|
||||||
|
if not issubclass(type(source), SceneNode):
|
||||||
|
return
|
||||||
|
max_build_plate = self._calcMaxBuildPlate()
|
||||||
|
changed = False
|
||||||
|
if max_build_plate != self._max_build_plate:
|
||||||
|
self._max_build_plate = max_build_plate
|
||||||
|
changed = True
|
||||||
|
if changed:
|
||||||
|
self._build_plate_model.setMaxBuildPlate(self._max_build_plate)
|
||||||
|
build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)]
|
||||||
|
self._build_plate_model.setItems(build_plates)
|
||||||
|
# self.buildPlateItemsChanged.emit() # TODO: necessary after setItems?
|
||||||
|
|
||||||
|
def _calcMaxBuildPlate(self):
|
||||||
|
max_build_plate = 0
|
||||||
|
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
|
||||||
|
if node.callDecoration("isSliceable"):
|
||||||
|
build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||||
|
max_build_plate = max(build_plate_number, max_build_plate)
|
||||||
|
return max_build_plate
|
||||||
|
|
||||||
|
## Either select or deselect an item
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def changeSelection(self, index):
|
||||||
|
modifiers = QApplication.keyboardModifiers()
|
||||||
|
ctrl_is_active = modifiers & Qt.ControlModifier
|
||||||
|
shift_is_active = modifiers & Qt.ShiftModifier
|
||||||
|
|
||||||
|
if ctrl_is_active:
|
||||||
|
item = self._objects_model.getItem(index)
|
||||||
|
node = item["node"]
|
||||||
|
if Selection.isSelected(node):
|
||||||
|
Selection.remove(node)
|
||||||
|
else:
|
||||||
|
Selection.add(node)
|
||||||
|
elif shift_is_active:
|
||||||
|
polarity = 1 if index + 1 > self._last_selected_index else -1
|
||||||
|
for i in range(self._last_selected_index, index + polarity, polarity):
|
||||||
|
item = self._objects_model.getItem(i)
|
||||||
|
node = item["node"]
|
||||||
|
Selection.add(node)
|
||||||
|
else:
|
||||||
|
# Single select
|
||||||
|
item = self._objects_model.getItem(index)
|
||||||
|
node = item["node"]
|
||||||
|
Selection.clear()
|
||||||
|
Selection.add(node)
|
||||||
|
build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||||
|
if build_plate_number is not None and build_plate_number != -1:
|
||||||
|
self._build_plate_model.setActiveBuildPlate(build_plate_number)
|
||||||
|
|
||||||
|
self._last_selected_index = index
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setActiveBuildPlate(self, nr):
|
||||||
|
if nr == self._active_build_plate:
|
||||||
|
return
|
||||||
|
Logger.log("d", "Select build plate: %s" % nr)
|
||||||
|
self._active_build_plate = nr
|
||||||
|
Selection.clear()
|
||||||
|
|
||||||
|
self._build_plate_model.setActiveBuildPlate(nr)
|
||||||
|
self._objects_model.setActiveBuildPlate(nr)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createCuraSceneController():
|
||||||
|
objects_model = Application.getInstance().getObjectsModel()
|
||||||
|
build_plate_model = Application.getInstance().getBuildPlateModel()
|
||||||
|
return CuraSceneController(objects_model = objects_model, build_plate_model = build_plate_model)
|
43
cura/Scene/CuraSceneNode.py
Normal file
43
cura/Scene/CuraSceneNode.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
|
## Scene nodes that are models are only seen when selecting the corresponding build plate
|
||||||
|
# Note that many other nodes can just be UM SceneNode objects.
|
||||||
|
class CuraSceneNode(SceneNode):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._outside_buildarea = True
|
||||||
|
|
||||||
|
def setOutsideBuildArea(self, new_value):
|
||||||
|
self._outside_buildarea = new_value
|
||||||
|
|
||||||
|
def isOutsideBuildArea(self):
|
||||||
|
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
|
||||||
|
|
||||||
|
def isVisible(self):
|
||||||
|
return super().isVisible() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
|
||||||
|
def isSelectable(self) -> bool:
|
||||||
|
return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
|
||||||
|
## Taken from SceneNode, but replaced SceneNode with CuraSceneNode
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
copy = CuraSceneNode()
|
||||||
|
copy.setTransformation(self.getLocalTransformation())
|
||||||
|
copy.setMeshData(self._mesh_data)
|
||||||
|
copy.setVisible(deepcopy(self._visible, memo))
|
||||||
|
copy._selectable = deepcopy(self._selectable, memo)
|
||||||
|
copy._name = deepcopy(self._name, memo)
|
||||||
|
for decorator in self._decorators:
|
||||||
|
copy.addDecorator(deepcopy(decorator, memo))
|
||||||
|
|
||||||
|
for child in self._children:
|
||||||
|
copy.addChild(deepcopy(child, memo))
|
||||||
|
self.calculateBoundingBoxMesh()
|
||||||
|
return copy
|
||||||
|
|
||||||
|
def transformChanged(self) -> None:
|
||||||
|
self._transformChanged()
|
0
cura/Scene/__init__.py
Normal file
0
cura/Scene/__init__.py
Normal file
|
@ -14,6 +14,7 @@ from UM.Decorators import override
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
from UM.Settings.SettingInstance import SettingInstance
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
|
@ -224,7 +225,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
# This is assumed to be the global profile
|
# This is assumed to be the global profile
|
||||||
profile_id = (global_container_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")
|
profile_id = (global_container_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")
|
||||||
|
|
||||||
elif len(machine_extruders) > profile_index:
|
elif profile_index < len(machine_extruders) + 1:
|
||||||
# This is assumed to be an extruder profile
|
# This is assumed to be an extruder profile
|
||||||
extruder_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_extruders[profile_index - 1].getBottom())
|
extruder_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_extruders[profile_index - 1].getBottom())
|
||||||
if not profile.getMetaDataEntry("extruder"):
|
if not profile.getMetaDataEntry("extruder"):
|
||||||
|
@ -430,11 +431,42 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
extruder_stack.setDefinition(extruder_definition)
|
extruder_stack.setDefinition(extruder_definition)
|
||||||
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
# create a new definition_changes container for the extruder stack
|
||||||
|
definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings")
|
||||||
|
definition_changes_name = definition_changes_id
|
||||||
|
definition_changes = InstanceContainer(definition_changes_id)
|
||||||
|
definition_changes.setName(definition_changes_name)
|
||||||
|
definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||||
|
definition_changes.addMetaDataEntry("type", "definition_changes")
|
||||||
|
definition_changes.addMetaDataEntry("definition", extruder_definition.getId())
|
||||||
|
|
||||||
|
# move definition_changes settings if exist
|
||||||
|
for setting_key in definition_changes.getAllKeys():
|
||||||
|
if machine.definition.getProperty(setting_key, "settable_per_extruder"):
|
||||||
|
setting_value = machine.definitionChanges.getProperty(setting_key, "value")
|
||||||
|
if setting_value is not None:
|
||||||
|
# move it to the extruder stack's definition_changes
|
||||||
|
setting_definition = machine.getSettingDefinition(setting_key)
|
||||||
|
new_instance = SettingInstance(setting_definition, definition_changes)
|
||||||
|
new_instance.setProperty("value", setting_value)
|
||||||
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
|
definition_changes.addInstance(new_instance)
|
||||||
|
definition_changes.setDirty(True)
|
||||||
|
|
||||||
|
machine.definitionChanges.removeInstance(setting_key, postpone_emit = True)
|
||||||
|
|
||||||
|
self.addContainer(definition_changes)
|
||||||
|
extruder_stack.setDefinitionChanges(definition_changes)
|
||||||
|
|
||||||
# create empty user changes container otherwise
|
# create empty user changes container otherwise
|
||||||
user_container = InstanceContainer(extruder_stack.id + "_user")
|
user_container_id = self.uniqueName(extruder_stack.getId() + "_user")
|
||||||
|
user_container_name = user_container_id
|
||||||
|
user_container = InstanceContainer(user_container_id)
|
||||||
|
user_container.setName(user_container_name)
|
||||||
user_container.addMetaDataEntry("type", "user")
|
user_container.addMetaDataEntry("type", "user")
|
||||||
user_container.addMetaDataEntry("machine", extruder_stack.getId())
|
user_container.addMetaDataEntry("machine", extruder_stack.getId())
|
||||||
from cura.CuraApplication import CuraApplication
|
|
||||||
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||||
user_container.setDefinition(machine.definition.getId())
|
user_container.setDefinition(machine.definition.getId())
|
||||||
|
|
||||||
|
@ -444,7 +476,15 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
for user_setting_key in machine.userChanges.getAllKeys():
|
for user_setting_key in machine.userChanges.getAllKeys():
|
||||||
settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder")
|
settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder")
|
||||||
if settable_per_extruder:
|
if settable_per_extruder:
|
||||||
user_container.addInstance(machine.userChanges.getInstance(user_setting_key))
|
setting_value = machine.getProperty(user_setting_key, "value")
|
||||||
|
|
||||||
|
setting_definition = machine.getSettingDefinition(user_setting_key)
|
||||||
|
new_instance = SettingInstance(setting_definition, definition_changes)
|
||||||
|
new_instance.setProperty("value", setting_value)
|
||||||
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
|
user_container.addInstance(new_instance)
|
||||||
|
user_container.setDirty(True)
|
||||||
|
|
||||||
machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)
|
machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)
|
||||||
|
|
||||||
self.addContainer(user_container)
|
self.addContainer(user_container)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
|
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
|
||||||
|
from UM.Settings.SettingInstance import SettingInstance
|
||||||
|
|
||||||
from . import Exceptions
|
from . import Exceptions
|
||||||
from .CuraContainerStack import CuraContainerStack
|
from .CuraContainerStack import CuraContainerStack
|
||||||
|
@ -16,6 +17,11 @@ from .ExtruderManager import ExtruderManager
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
|
|
||||||
|
|
||||||
|
_EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS = ["machine_nozzle_size",
|
||||||
|
"material_diameter"]
|
||||||
|
|
||||||
|
|
||||||
## Represents an Extruder and its related containers.
|
## Represents an Extruder and its related containers.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -39,6 +45,29 @@ class ExtruderStack(CuraContainerStack):
|
||||||
# For backward compatibility: Register the extruder with the Extruder Manager
|
# For backward compatibility: Register the extruder with the Extruder Manager
|
||||||
ExtruderManager.getInstance().registerExtruder(self, stack.id)
|
ExtruderManager.getInstance().registerExtruder(self, stack.id)
|
||||||
|
|
||||||
|
# Now each machine will have at least one extruder stack. If this is the first extruder, the extruder-specific
|
||||||
|
# settings such as nozzle size and material diameter should be moved from the machine's definition_changes to
|
||||||
|
# the this extruder's definition_changes.
|
||||||
|
#
|
||||||
|
# We do this here because it is tooooo expansive to do it in the version upgrade: During the version upgrade,
|
||||||
|
# when we are upgrading a definition_changes container file, there is NO guarantee that other files such as
|
||||||
|
# machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in
|
||||||
|
# the latest format.
|
||||||
|
if self.getMetaDataEntry("position") == "0":
|
||||||
|
for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS:
|
||||||
|
setting_value = stack.definitionChanges.getProperty(key, "value")
|
||||||
|
if setting_value is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
setting_definition = stack.getSettingDefinition(key)
|
||||||
|
new_instance = SettingInstance(setting_definition, self.definitionChanges)
|
||||||
|
new_instance.setProperty("value", setting_value)
|
||||||
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
|
self.definitionChanges.addInstance(new_instance)
|
||||||
|
self.definitionChanges.setDirty(True)
|
||||||
|
|
||||||
|
stack.definitionChanges.removeInstance(key, postpone_emit = True)
|
||||||
|
|
||||||
@override(ContainerStack)
|
@override(ContainerStack)
|
||||||
def getNextStack(self) -> Optional["GlobalStack"]:
|
def getNextStack(self) -> Optional["GlobalStack"]:
|
||||||
return super().getNextStack()
|
return super().getNextStack()
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import os.path
|
import os.path
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from UM.Job import Job
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Math.Matrix import Matrix
|
from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
@ -15,9 +14,10 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.QualityManager import QualityManager
|
from cura.QualityManager import QualityManager
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
|
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||||
|
|
||||||
MYPY = False
|
MYPY = False
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ class ThreeMFReader(MeshReader):
|
||||||
}
|
}
|
||||||
self._base_name = ""
|
self._base_name = ""
|
||||||
self._unit = None
|
self._unit = None
|
||||||
|
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||||
|
|
||||||
def _createMatrixFromTransformationString(self, transformation):
|
def _createMatrixFromTransformationString(self, transformation):
|
||||||
if transformation == "":
|
if transformation == "":
|
||||||
|
@ -77,7 +78,12 @@ class ThreeMFReader(MeshReader):
|
||||||
## Convenience function that converts a SceneNode object (as obtained from libSavitar) to a Uranium scene node.
|
## Convenience function that converts a SceneNode object (as obtained from libSavitar) to a Uranium scene node.
|
||||||
# \returns Uranium scene node.
|
# \returns Uranium scene node.
|
||||||
def _convertSavitarNodeToUMNode(self, savitar_node):
|
def _convertSavitarNodeToUMNode(self, savitar_node):
|
||||||
um_node = SceneNode()
|
self._object_count += 1
|
||||||
|
node_name = "Object %s" % self._object_count
|
||||||
|
|
||||||
|
um_node = CuraSceneNode()
|
||||||
|
um_node.addDecorator(BuildPlateDecorator(0))
|
||||||
|
um_node.setName(node_name)
|
||||||
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
||||||
um_node.setTransformation(transformation)
|
um_node.setTransformation(transformation)
|
||||||
mesh_builder = MeshBuilder()
|
mesh_builder = MeshBuilder()
|
||||||
|
@ -147,6 +153,7 @@ class ThreeMFReader(MeshReader):
|
||||||
|
|
||||||
def read(self, file_name):
|
def read(self, file_name):
|
||||||
result = []
|
result = []
|
||||||
|
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||||
# The base object of 3mf is a zipped archive.
|
# The base object of 3mf is a zipped archive.
|
||||||
try:
|
try:
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
archive = zipfile.ZipFile(file_name, "r")
|
||||||
|
|
|
@ -7,6 +7,7 @@ from UM.Logger import Logger
|
||||||
from UM.Math.Matrix import Matrix
|
from UM.Math.Matrix import Matrix
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
import UM.Scene.SceneNode
|
import UM.Scene.SceneNode
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
import Savitar
|
import Savitar
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ class ThreeMFWriter(MeshWriter):
|
||||||
## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
|
## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
|
||||||
# \returns Uranium Scenen node.
|
# \returns Uranium Scenen node.
|
||||||
def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()):
|
def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()):
|
||||||
if type(um_node) is not UM.Scene.SceneNode.SceneNode:
|
if type(um_node) not in [UM.Scene.SceneNode.SceneNode, CuraSceneNode]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
savitar_node = Savitar.SceneNode()
|
savitar_node = Savitar.SceneNode()
|
||||||
|
|
|
@ -16,6 +16,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
from UM.Qt.Duration import DurationFormat
|
from UM.Qt.Duration import DurationFormat
|
||||||
from PyQt5.QtCore import QObject, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtSlot
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from . import ProcessSlicedLayersJob
|
from . import ProcessSlicedLayersJob
|
||||||
from . import StartSliceJob
|
from . import StartSliceJob
|
||||||
|
@ -69,9 +70,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
# Workaround to disable layer view processing if layer view is not active.
|
# Workaround to disable layer view processing if layer view is not active.
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||||
self._onActiveViewChanged()
|
self._onActiveViewChanged()
|
||||||
self._stored_layer_data = []
|
self._stored_layer_data = []
|
||||||
self._stored_optimized_layer_data = []
|
self._stored_optimized_layer_data = {} # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
|
||||||
|
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||||
|
@ -105,17 +107,18 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||||
|
|
||||||
self._start_slice_job = None
|
self._start_slice_job = None
|
||||||
|
self._start_slice_job_build_plate = None
|
||||||
self._slicing = False # Are we currently slicing?
|
self._slicing = False # Are we currently slicing?
|
||||||
self._restart = False # Back-end is currently restarting?
|
self._restart = False # Back-end is currently restarting?
|
||||||
self._tool_active = False # If a tool is active, some tasks do not have to do anything
|
self._tool_active = False # If a tool is active, some tasks do not have to do anything
|
||||||
self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
|
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._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers.
|
||||||
self._need_slicing = False
|
self._build_plates_to_be_sliced = [] # what needs slicing?
|
||||||
self._engine_is_fresh = True # Is the newly started engine used before or not?
|
self._engine_is_fresh = True # Is the newly started engine used before or not?
|
||||||
|
|
||||||
self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
|
self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
|
||||||
self._error_message = None # Pop-up message that shows errors.
|
self._error_message = None # Pop-up message that shows errors.
|
||||||
self._last_num_objects = 0 # Count number of objects to see if there is something changed
|
self._last_num_objects = defaultdict(int) # Count number of objects to see if there is something changed
|
||||||
self._postponed_scene_change_sources = [] # scene change is postponed (by a tool)
|
self._postponed_scene_change_sources = [] # scene change is postponed (by a tool)
|
||||||
|
|
||||||
self.backendQuit.connect(self._onBackendQuit)
|
self.backendQuit.connect(self._onBackendQuit)
|
||||||
|
@ -174,6 +177,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._createSocket()
|
self._createSocket()
|
||||||
|
|
||||||
if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon.
|
if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon.
|
||||||
|
Logger.log("d", "Aborting process layers job...")
|
||||||
self._process_layers_job.abort()
|
self._process_layers_job.abort()
|
||||||
self._process_layers_job = None
|
self._process_layers_job = None
|
||||||
|
|
||||||
|
@ -190,17 +194,34 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
## Perform a slice of the scene.
|
## Perform a slice of the scene.
|
||||||
def slice(self):
|
def slice(self):
|
||||||
|
Logger.log("d", "starting to slice!")
|
||||||
self._slice_start_time = time()
|
self._slice_start_time = time()
|
||||||
if not self._need_slicing:
|
if not self._build_plates_to_be_sliced:
|
||||||
self.processingProgress.emit(1.0)
|
self.processingProgress.emit(1.0)
|
||||||
self.backendStateChange.emit(BackendState.Done)
|
self.backendStateChange.emit(BackendState.Done)
|
||||||
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
|
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
|
||||||
return
|
return
|
||||||
if Application.getInstance().getPrintInformation():
|
|
||||||
Application.getInstance().getPrintInformation().setToZeroPrintInformation()
|
if self._process_layers_job:
|
||||||
|
Logger.log("d", " ## Process layers job still busy, trying later")
|
||||||
|
self._invokeSlice()
|
||||||
|
return
|
||||||
|
|
||||||
|
# see if we really have to slice
|
||||||
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
|
||||||
|
Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
|
||||||
|
num_objects = self._numObjects()
|
||||||
|
if build_plate_to_be_sliced not in num_objects or num_objects[build_plate_to_be_sliced] == 0:
|
||||||
|
Logger.log("d", "Build plate %s has 0 objects to be sliced, skipping", build_plate_to_be_sliced)
|
||||||
|
self._invokeSlice()
|
||||||
|
return
|
||||||
|
|
||||||
self._stored_layer_data = []
|
self._stored_layer_data = []
|
||||||
self._stored_optimized_layer_data = []
|
self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
|
||||||
|
|
||||||
|
if Application.getInstance().getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
|
||||||
|
Application.getInstance().getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
|
||||||
|
|
||||||
if self._process is None:
|
if self._process is None:
|
||||||
self._createSocket()
|
self._createSocket()
|
||||||
|
@ -210,12 +231,16 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.processingProgress.emit(0.0)
|
self.processingProgress.emit(0.0)
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
|
|
||||||
self._scene.gcode_list = []
|
if not hasattr(self._scene, "gcode_list"):
|
||||||
|
self._scene.gcode_list = {}
|
||||||
|
self._scene.gcode_list[build_plate_to_be_sliced] = [] #[] indexed by build plate number
|
||||||
self._slicing = True
|
self._slicing = True
|
||||||
self.slicingStarted.emit()
|
self.slicingStarted.emit()
|
||||||
|
|
||||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||||
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
|
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
|
||||||
|
self._start_slice_job_build_plate = build_plate_to_be_sliced
|
||||||
|
self._start_slice_job.setBuildPlate(self._start_slice_job_build_plate)
|
||||||
self._start_slice_job.start()
|
self._start_slice_job.start()
|
||||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||||
|
|
||||||
|
@ -224,7 +249,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
def _terminate(self):
|
def _terminate(self):
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._stored_layer_data = []
|
self._stored_layer_data = []
|
||||||
self._stored_optimized_layer_data = []
|
if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
|
||||||
|
del self._stored_optimized_layer_data[self._start_slice_job_build_plate]
|
||||||
if self._start_slice_job is not None:
|
if self._start_slice_job is not None:
|
||||||
self._start_slice_job.cancel()
|
self._start_slice_job.cancel()
|
||||||
|
|
||||||
|
@ -331,15 +357,6 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
|
|
||||||
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
|
|
||||||
if Application.getInstance().platformActivity:
|
|
||||||
self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."),
|
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
|
||||||
self._error_message.show()
|
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
|
||||||
else:
|
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
|
||||||
return
|
|
||||||
# Preparation completed, send it to the backend.
|
# Preparation completed, send it to the backend.
|
||||||
self._socket.sendMessage(job.getSliceMessage())
|
self._socket.sendMessage(job.getSliceMessage())
|
||||||
|
|
||||||
|
@ -363,7 +380,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.backendStateChange.emit(BackendState.Disabled)
|
self.backendStateChange.emit(BackendState.Disabled)
|
||||||
gcode_list = node.callDecoration("getGCodeList")
|
gcode_list = node.callDecoration("getGCodeList")
|
||||||
if gcode_list is not None:
|
if gcode_list is not None:
|
||||||
self._scene.gcode_list = gcode_list
|
self._scene.gcode_list[node.callDecoration("getBuildPlateNumber")] = gcode_list
|
||||||
|
|
||||||
if self._use_timer == enable_timer:
|
if self._use_timer == enable_timer:
|
||||||
return self._use_timer
|
return self._use_timer
|
||||||
|
@ -375,43 +392,66 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.disableTimer()
|
self.disableTimer()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
## Return a dict with number of objects per build plate
|
||||||
|
def _numObjects(self):
|
||||||
|
num_objects = defaultdict(int)
|
||||||
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
|
# Only count sliceable objects
|
||||||
|
if node.callDecoration("isSliceable"):
|
||||||
|
build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||||
|
num_objects[build_plate_number] += 1
|
||||||
|
return num_objects
|
||||||
|
|
||||||
## Listener for when the scene has changed.
|
## Listener for when the scene has changed.
|
||||||
#
|
#
|
||||||
# This should start a slice if the scene is now ready to slice.
|
# This should start a slice if the scene is now ready to slice.
|
||||||
#
|
#
|
||||||
# \param source The scene node that was changed.
|
# \param source The scene node that was changed.
|
||||||
def _onSceneChanged(self, source):
|
def _onSceneChanged(self, source):
|
||||||
if type(source) is not SceneNode:
|
if not issubclass(type(source), SceneNode):
|
||||||
return
|
return
|
||||||
|
|
||||||
root_scene_nodes_changed = False
|
build_plate_changed = set()
|
||||||
|
source_build_plate_number = source.callDecoration("getBuildPlateNumber")
|
||||||
if source == self._scene.getRoot():
|
if source == self._scene.getRoot():
|
||||||
num_objects = 0
|
# we got the root node
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
num_objects = self._numObjects()
|
||||||
# Only count sliceable objects
|
for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()):
|
||||||
if node.callDecoration("isSliceable"):
|
if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]:
|
||||||
num_objects += 1
|
self._last_num_objects[build_plate_number] = num_objects[build_plate_number]
|
||||||
if num_objects != self._last_num_objects:
|
build_plate_changed.add(build_plate_number)
|
||||||
self._last_num_objects = num_objects
|
|
||||||
root_scene_nodes_changed = True
|
|
||||||
else:
|
else:
|
||||||
return
|
# we got a single scenenode
|
||||||
|
if not source.callDecoration("isGroup"):
|
||||||
if not source.callDecoration("isGroup") and not root_scene_nodes_changed:
|
|
||||||
if source.getMeshData() is None:
|
if source.getMeshData() is None:
|
||||||
return
|
return
|
||||||
if source.getMeshData().getVertices() is None:
|
if source.getMeshData().getVertices() is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
build_plate_changed.add(source_build_plate_number)
|
||||||
|
|
||||||
|
build_plate_changed.discard(None)
|
||||||
|
build_plate_changed.discard(-1) # object not on build plate
|
||||||
|
if not build_plate_changed:
|
||||||
|
return
|
||||||
|
|
||||||
if self._tool_active:
|
if self._tool_active:
|
||||||
# do it later, each source only has to be done once
|
# do it later, each source only has to be done once
|
||||||
if source not in self._postponed_scene_change_sources:
|
if source not in self._postponed_scene_change_sources:
|
||||||
self._postponed_scene_change_sources.append(source)
|
self._postponed_scene_change_sources.append(source)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.needsSlicing()
|
|
||||||
self.stopSlicing()
|
self.stopSlicing()
|
||||||
self._onChanged()
|
for build_plate_number in build_plate_changed:
|
||||||
|
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||||
|
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||||
|
self.processingProgress.emit(0.0)
|
||||||
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
|
# if not self._use_timer:
|
||||||
|
# With manually having to slice, we want to clear the old invalid layer data.
|
||||||
|
self._clearLayerData(build_plate_changed)
|
||||||
|
|
||||||
|
self._invokeSlice()
|
||||||
|
|
||||||
## Called when an error occurs in the socket connection towards the engine.
|
## Called when an error occurs in the socket connection towards the engine.
|
||||||
#
|
#
|
||||||
|
@ -431,16 +471,21 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
Logger.log("w", "A socket error caused the connection to be reset")
|
Logger.log("w", "A socket error caused the connection to be reset")
|
||||||
|
|
||||||
## Remove old layer data (if any)
|
## Remove old layer data (if any)
|
||||||
def _clearLayerData(self):
|
def _clearLayerData(self, build_plate_numbers = set()):
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if node.callDecoration("getLayerData"):
|
if node.callDecoration("getLayerData"):
|
||||||
|
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:
|
||||||
node.getParent().removeChild(node)
|
node.getParent().removeChild(node)
|
||||||
break
|
|
||||||
|
|
||||||
## Convenient function: set need_slicing, emit state and clear layer data
|
def markSliceAll(self):
|
||||||
|
for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
|
||||||
|
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||||
|
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||||
|
|
||||||
|
## Convenient function: mark everything to slice, emit state and clear layer data
|
||||||
def needsSlicing(self):
|
def needsSlicing(self):
|
||||||
self.stopSlicing()
|
self.stopSlicing()
|
||||||
self._need_slicing = True
|
self.markSliceAll()
|
||||||
self.processingProgress.emit(0.0)
|
self.processingProgress.emit(0.0)
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
if not self._use_timer:
|
if not self._use_timer:
|
||||||
|
@ -462,7 +507,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
def _onStackErrorCheckFinished(self):
|
def _onStackErrorCheckFinished(self):
|
||||||
self._is_error_check_scheduled = False
|
self._is_error_check_scheduled = False
|
||||||
if not self._slicing and self._need_slicing:
|
if not self._slicing and self._build_plates_to_be_sliced: #self._need_slicing:
|
||||||
self.needsSlicing()
|
self.needsSlicing()
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
|
@ -476,7 +521,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
#
|
#
|
||||||
# \param message The protobuf message containing sliced layer data.
|
# \param message The protobuf message containing sliced layer data.
|
||||||
def _onOptimizedLayerMessage(self, message):
|
def _onOptimizedLayerMessage(self, message):
|
||||||
self._stored_optimized_layer_data.append(message)
|
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
|
||||||
|
|
||||||
## Called when a progress message is received from the engine.
|
## Called when a progress message is received from the engine.
|
||||||
#
|
#
|
||||||
|
@ -485,6 +530,16 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.processingProgress.emit(message.amount)
|
self.processingProgress.emit(message.amount)
|
||||||
self.backendStateChange.emit(BackendState.Processing)
|
self.backendStateChange.emit(BackendState.Processing)
|
||||||
|
|
||||||
|
# testing
|
||||||
|
def _invokeSlice(self):
|
||||||
|
if self._use_timer:
|
||||||
|
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
|
||||||
|
# otherwise business as usual
|
||||||
|
if self._is_error_check_scheduled:
|
||||||
|
self._change_timer.stop()
|
||||||
|
else:
|
||||||
|
self._change_timer.start()
|
||||||
|
|
||||||
## Called when the engine sends a message that slicing is finished.
|
## Called when the engine sends a message that slicing is finished.
|
||||||
#
|
#
|
||||||
# \param message The protobuf message signalling that slicing is finished.
|
# \param message The protobuf message signalling that slicing is finished.
|
||||||
|
@ -492,36 +547,44 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.backendStateChange.emit(BackendState.Done)
|
self.backendStateChange.emit(BackendState.Done)
|
||||||
self.processingProgress.emit(1.0)
|
self.processingProgress.emit(1.0)
|
||||||
|
|
||||||
for line in self._scene.gcode_list:
|
gcode_list = self._scene.gcode_list[self._start_slice_job_build_plate]
|
||||||
|
for index, line in enumerate(gcode_list):
|
||||||
replaced = line.replace("{print_time}", str(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
|
replaced = line.replace("{print_time}", str(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
|
||||||
replaced = replaced.replace("{filament_amount}", str(Application.getInstance().getPrintInformation().materialLengths))
|
replaced = replaced.replace("{filament_amount}", str(Application.getInstance().getPrintInformation().materialLengths))
|
||||||
replaced = replaced.replace("{filament_weight}", str(Application.getInstance().getPrintInformation().materialWeights))
|
replaced = replaced.replace("{filament_weight}", str(Application.getInstance().getPrintInformation().materialWeights))
|
||||||
replaced = replaced.replace("{filament_cost}", str(Application.getInstance().getPrintInformation().materialCosts))
|
replaced = replaced.replace("{filament_cost}", str(Application.getInstance().getPrintInformation().materialCosts))
|
||||||
replaced = replaced.replace("{jobname}", str(Application.getInstance().getPrintInformation().jobName))
|
replaced = replaced.replace("{jobname}", str(Application.getInstance().getPrintInformation().jobName))
|
||||||
|
|
||||||
self._scene.gcode_list[self._scene.gcode_list.index(line)] = replaced
|
gcode_list[index] = replaced
|
||||||
|
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._need_slicing = False
|
|
||||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
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)
|
# See if we need to process the sliced layers job.
|
||||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
self._process_layers_job.start()
|
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()) and active_build_plate == self._start_slice_job_build_plate:
|
||||||
self._stored_optimized_layer_data = []
|
self._startProcessSlicedLayersJob(active_build_plate)
|
||||||
|
# self._onActiveViewChanged()
|
||||||
|
self._start_slice_job_build_plate = None
|
||||||
|
|
||||||
|
Logger.log("d", "See if there is more to slice...")
|
||||||
|
# Somehow this results in an Arcus Error
|
||||||
|
# self.slice()
|
||||||
|
# Testing call slice again, allow backend to restart by using the timer
|
||||||
|
self._invokeSlice()
|
||||||
|
|
||||||
## Called when a g-code message is received from the engine.
|
## Called when a g-code message is received from the engine.
|
||||||
#
|
#
|
||||||
# \param message The protobuf message containing g-code, encoded as UTF-8.
|
# \param message The protobuf message containing g-code, encoded as UTF-8.
|
||||||
def _onGCodeLayerMessage(self, message):
|
def _onGCodeLayerMessage(self, message):
|
||||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
self._scene.gcode_list[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace"))
|
||||||
|
|
||||||
## Called when a g-code prefix message is received from the engine.
|
## Called when a g-code prefix message is received from the engine.
|
||||||
#
|
#
|
||||||
# \param message The protobuf message containing the g-code prefix,
|
# \param message The protobuf message containing the g-code prefix,
|
||||||
# encoded as UTF-8.
|
# encoded as UTF-8.
|
||||||
def _onGCodePrefixMessage(self, message):
|
def _onGCodePrefixMessage(self, message):
|
||||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
self._scene.gcode_list[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace"))
|
||||||
|
|
||||||
## Creates a new socket connection.
|
## Creates a new socket connection.
|
||||||
def _createSocket(self):
|
def _createSocket(self):
|
||||||
|
@ -551,7 +614,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
||||||
|
|
||||||
times = self._parseMessagePrintTimes(message)
|
times = self._parseMessagePrintTimes(message)
|
||||||
self.printDurationMessage.emit(times, material_amounts)
|
self.printDurationMessage.emit(self._start_slice_job_build_plate, times, material_amounts)
|
||||||
|
|
||||||
## Called for parsing message to retrieve estimated time per feature
|
## Called for parsing message to retrieve estimated time per feature
|
||||||
#
|
#
|
||||||
|
@ -605,19 +668,25 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
source = self._postponed_scene_change_sources.pop(0)
|
source = self._postponed_scene_change_sources.pop(0)
|
||||||
self._onSceneChanged(source)
|
self._onSceneChanged(source)
|
||||||
|
|
||||||
|
def _startProcessSlicedLayersJob(self, build_plate_number):
|
||||||
|
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data[build_plate_number])
|
||||||
|
self._process_layers_job.setBuildPlate(build_plate_number)
|
||||||
|
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
||||||
|
self._process_layers_job.start()
|
||||||
|
|
||||||
## Called when the user changes the active view mode.
|
## Called when the user changes the active view mode.
|
||||||
def _onActiveViewChanged(self):
|
def _onActiveViewChanged(self):
|
||||||
if Application.getInstance().getController().getActiveView():
|
application = Application.getInstance()
|
||||||
view = Application.getInstance().getController().getActiveView()
|
view = application.getController().getActiveView()
|
||||||
|
if view:
|
||||||
|
active_build_plate = application.getBuildPlateModel().activeBuildPlate
|
||||||
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||||
self._layer_view_active = True
|
self._layer_view_active = True
|
||||||
# There is data and we're not slicing at the moment
|
# There is data and we're not slicing at the moment
|
||||||
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
||||||
if self._stored_optimized_layer_data and not self._slicing:
|
# TODO: what build plate I am slicing
|
||||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
|
if active_build_plate in self._stored_optimized_layer_data and not self._slicing and not self._process_layers_job:
|
||||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
self._startProcessSlicedLayersJob(active_build_plate)
|
||||||
self._process_layers_job.start()
|
|
||||||
self._stored_optimized_layer_data = []
|
|
||||||
else:
|
else:
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
|
|
||||||
|
@ -653,7 +722,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
def _onProcessLayersFinished(self, job):
|
def _onProcessLayersFinished(self, job):
|
||||||
|
del self._stored_optimized_layer_data[job.getBuildPlate()]
|
||||||
self._process_layers_job = None
|
self._process_layers_job = None
|
||||||
|
Logger.log("d", "See if there is more to slice(2)...")
|
||||||
|
self._invokeSlice()
|
||||||
|
|
||||||
## Connect slice function to timer.
|
## Connect slice function to timer.
|
||||||
def enableTimer(self):
|
def enableTimer(self):
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import gc
|
import gc
|
||||||
|
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Mesh.MeshData import MeshData
|
from UM.Mesh.MeshData import MeshData
|
||||||
|
@ -17,6 +16,7 @@ from UM.Logger import Logger
|
||||||
|
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
|
||||||
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura import LayerDataBuilder
|
from cura import LayerDataBuilder
|
||||||
from cura import LayerDataDecorator
|
from cura import LayerDataDecorator
|
||||||
|
@ -49,6 +49,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
||||||
self._abort_requested = False
|
self._abort_requested = False
|
||||||
|
self._build_plate_number = None
|
||||||
|
|
||||||
## Aborts the processing of layers.
|
## Aborts the processing of layers.
|
||||||
#
|
#
|
||||||
|
@ -59,7 +60,14 @@ class ProcessSlicedLayersJob(Job):
|
||||||
def abort(self):
|
def abort(self):
|
||||||
self._abort_requested = True
|
self._abort_requested = True
|
||||||
|
|
||||||
|
def setBuildPlate(self, new_value):
|
||||||
|
self._build_plate_number = new_value
|
||||||
|
|
||||||
|
def getBuildPlate(self):
|
||||||
|
return self._build_plate_number
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
Logger.log("d", "Processing new layer for build plate %s..." % self._build_plate_number)
|
||||||
start_time = time()
|
start_time = time()
|
||||||
view = Application.getInstance().getController().getActiveView()
|
view = Application.getInstance().getController().getActiveView()
|
||||||
if view.getPluginId() == "SimulationView":
|
if view.getPluginId() == "SimulationView":
|
||||||
|
@ -74,16 +82,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
|
||||||
new_node = SceneNode()
|
new_node = SceneNode()
|
||||||
|
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
|
||||||
## Remove old layer data (if any)
|
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
|
||||||
if node.callDecoration("getLayerData"):
|
|
||||||
node.getParent().removeChild(node)
|
|
||||||
break
|
|
||||||
if self._abort_requested:
|
|
||||||
if self._progress_message:
|
|
||||||
self._progress_message.hide()
|
|
||||||
return
|
|
||||||
|
|
||||||
# Force garbage collection.
|
# Force garbage collection.
|
||||||
# For some reason, Python has a tendency to keep the layer data
|
# For some reason, Python has a tendency to keep the layer data
|
||||||
|
|
|
@ -10,12 +10,12 @@ from UM.Job import Job
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
|
||||||
from UM.Settings.Validator import ValidatorState
|
from UM.Settings.Validator import ValidatorState
|
||||||
from UM.Settings.SettingRelation import RelationType
|
from UM.Settings.SettingRelation import RelationType
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
|
@ -36,9 +36,32 @@ class StartJobResult(IntEnum):
|
||||||
## Formatter class that handles token expansion in start/end gcod
|
## Formatter class that handles token expansion in start/end gcod
|
||||||
class GcodeStartEndFormatter(Formatter):
|
class GcodeStartEndFormatter(Formatter):
|
||||||
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||||
|
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
|
||||||
|
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||||
|
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
try:
|
try:
|
||||||
return kwargs[key]
|
extruder_nr = kwargs["default_extruder_nr"]
|
||||||
|
except ValueError:
|
||||||
|
extruder_nr = -1
|
||||||
|
|
||||||
|
key_fragments = [fragment.strip() for fragment in key.split(',')]
|
||||||
|
if len(key_fragments) == 2:
|
||||||
|
try:
|
||||||
|
extruder_nr = int(key_fragments[1])
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
extruder_nr = int(kwargs["-1"][key_fragments[1]]) # get extruder_nr values from the global stack
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
# either the key does not exist, or the value is not an int
|
||||||
|
Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end gcode, using global stack", key_fragments[1], key_fragments[0])
|
||||||
|
elif len(key_fragments) != 1:
|
||||||
|
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
|
||||||
|
return "{" + str(key) + "}"
|
||||||
|
|
||||||
|
key = key_fragments[0]
|
||||||
|
try:
|
||||||
|
return kwargs[str(extruder_nr)][key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
Logger.log("w", "Unable to replace '%s' placeholder in start/end gcode", key)
|
Logger.log("w", "Unable to replace '%s' placeholder in start/end gcode", key)
|
||||||
return "{" + key + "}"
|
return "{" + key + "}"
|
||||||
|
@ -55,10 +78,16 @@ class StartSliceJob(Job):
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._slice_message = slice_message
|
self._slice_message = slice_message
|
||||||
self._is_cancelled = False
|
self._is_cancelled = False
|
||||||
|
self._build_plate_number = None
|
||||||
|
|
||||||
|
self._all_extruders_settings = None # cache for all setting values from all stacks (global & extruder) for the current machine
|
||||||
|
|
||||||
def getSliceMessage(self):
|
def getSliceMessage(self):
|
||||||
return self._slice_message
|
return self._slice_message
|
||||||
|
|
||||||
|
def setBuildPlate(self, build_plate_number):
|
||||||
|
self._build_plate_number = build_plate_number
|
||||||
|
|
||||||
## Check if a stack has any errors.
|
## Check if a stack has any errors.
|
||||||
## returns true if it has errors, false otherwise.
|
## returns true if it has errors, false otherwise.
|
||||||
def _checkStackForErrors(self, stack):
|
def _checkStackForErrors(self, stack):
|
||||||
|
@ -75,6 +104,10 @@ class StartSliceJob(Job):
|
||||||
|
|
||||||
## Runs the job that initiates the slicing.
|
## Runs the job that initiates the slicing.
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if self._build_plate_number is None:
|
||||||
|
self.setResult(StartJobResult.Error)
|
||||||
|
return
|
||||||
|
|
||||||
stack = Application.getInstance().getGlobalContainerStack()
|
stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if not stack:
|
if not stack:
|
||||||
self.setResult(StartJobResult.Error)
|
self.setResult(StartJobResult.Error)
|
||||||
|
@ -108,7 +141,7 @@ class StartSliceJob(Job):
|
||||||
with self._scene.getSceneLock():
|
with self._scene.getSceneLock():
|
||||||
# Remove old layer data.
|
# Remove old layer data.
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if node.callDecoration("getLayerData"):
|
if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number:
|
||||||
node.getParent().removeChild(node)
|
node.getParent().removeChild(node)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -143,10 +176,12 @@ class StartSliceJob(Job):
|
||||||
if per_object_stack:
|
if per_object_stack:
|
||||||
is_non_printing_mesh = any(per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS)
|
is_non_printing_mesh = any(per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS)
|
||||||
|
|
||||||
if not getattr(node, "_outside_buildarea", False) or not is_non_printing_mesh:
|
if (node.callDecoration("getBuildPlateNumber") == self._build_plate_number):
|
||||||
|
if not getattr(node, "_outside_buildarea", False) or is_non_printing_mesh:
|
||||||
temp_list.append(node)
|
temp_list.append(node)
|
||||||
if not is_non_printing_mesh:
|
if not is_non_printing_mesh:
|
||||||
has_printing_mesh = True
|
has_printing_mesh = True
|
||||||
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
#If the list doesn't have any model with suitable settings then clean the list
|
#If the list doesn't have any model with suitable settings then clean the list
|
||||||
|
@ -232,16 +267,33 @@ class StartSliceJob(Job):
|
||||||
result["date"] = time.strftime("%d-%m-%Y")
|
result["date"] = time.strftime("%d-%m-%Y")
|
||||||
result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]
|
result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]
|
||||||
|
|
||||||
|
initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
|
||||||
|
initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
|
||||||
|
result["initial_extruder_nr"] = initial_extruder_nr
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
## Replace setting tokens in a piece of g-code.
|
## Replace setting tokens in a piece of g-code.
|
||||||
# \param value A piece of g-code to replace tokens in.
|
# \param value A piece of g-code to replace tokens in.
|
||||||
# \param settings A dictionary of tokens to replace and their respective
|
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
||||||
# replacement strings.
|
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1):
|
||||||
def _expandGcodeTokens(self, value: str, settings: dict):
|
if not self._all_extruders_settings:
|
||||||
|
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
|
||||||
|
# NB: keys must be strings for the string formatter
|
||||||
|
self._all_extruders_settings = {
|
||||||
|
"-1": self._buildReplacementTokens(global_stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
|
||||||
|
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
||||||
|
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# any setting can be used as a token
|
# any setting can be used as a token
|
||||||
fmt = GcodeStartEndFormatter()
|
fmt = GcodeStartEndFormatter()
|
||||||
|
settings = self._all_extruders_settings.copy()
|
||||||
|
settings["default_extruder_nr"] = default_extruder_nr
|
||||||
return str(fmt.format(value, **settings))
|
return str(fmt.format(value, **settings))
|
||||||
except:
|
except:
|
||||||
Logger.logException("w", "Unable to do token replacement on start/end gcode")
|
Logger.logException("w", "Unable to do token replacement on start/end gcode")
|
||||||
|
@ -258,8 +310,9 @@ class StartSliceJob(Job):
|
||||||
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
|
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
|
||||||
|
|
||||||
# Replace the setting tokens in start and end g-code.
|
# Replace the setting tokens in start and end g-code.
|
||||||
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], settings)
|
extruder_nr = stack.getProperty("extruder_nr", "value")
|
||||||
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], settings)
|
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr)
|
||||||
|
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr)
|
||||||
|
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
# Do not send settings that are not settable_per_extruder.
|
# Do not send settings that are not settable_per_extruder.
|
||||||
|
@ -284,13 +337,13 @@ class StartSliceJob(Job):
|
||||||
print_temperature_settings = {"material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"}
|
print_temperature_settings = {"material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"}
|
||||||
settings["material_print_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in print_temperature_settings))
|
settings["material_print_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in print_temperature_settings))
|
||||||
|
|
||||||
# Find the correct temperatures from the first used extruder
|
|
||||||
extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
|
|
||||||
extruder_0_settings = self._buildReplacementTokens(extruder_stack)
|
|
||||||
|
|
||||||
# Replace the setting tokens in start and end g-code.
|
# Replace the setting tokens in start and end g-code.
|
||||||
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], extruder_0_settings)
|
# Use values from the first used extruder by default so we get the expected temperatures
|
||||||
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], extruder_0_settings)
|
initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
|
||||||
|
initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
|
||||||
|
|
||||||
|
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr)
|
||||||
|
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr)
|
||||||
|
|
||||||
# Add all sub-messages for each individual setting.
|
# Add all sub-messages for each individual setting.
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
|
|
|
@ -17,7 +17,7 @@ catalog = i18nCatalog("cura")
|
||||||
from cura import LayerDataBuilder
|
from cura import LayerDataBuilder
|
||||||
from cura import LayerDataDecorator
|
from cura import LayerDataDecorator
|
||||||
from cura.LayerPolygon import LayerPolygon
|
from cura.LayerPolygon import LayerPolygon
|
||||||
from cura.GCodeListDecorator import GCodeListDecorator
|
from cura.Scene.GCodeListDecorator import GCodeListDecorator
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
|
@ -59,8 +59,9 @@ class GCodeWriter(MeshWriter):
|
||||||
Logger.log("e", "GCode Writer does not support non-text mode.")
|
Logger.log("e", "GCode Writer does not support non-text mode.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
scene = Application.getInstance().getController().getScene()
|
scene = Application.getInstance().getController().getScene()
|
||||||
gcode_list = getattr(scene, "gcode_list")
|
gcode_list = getattr(scene, "gcode_list")[active_build_plate]
|
||||||
if gcode_list:
|
if gcode_list:
|
||||||
for gcode in gcode_list:
|
for gcode in gcode_list:
|
||||||
stream.write(gcode)
|
stream.write(gcode)
|
||||||
|
|
|
@ -8,12 +8,13 @@ from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from .ImageReaderUI import ImageReaderUI
|
from .ImageReaderUI import ImageReaderUI
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||||
|
|
||||||
|
|
||||||
class ImageReader(MeshReader):
|
class ImageReader(MeshReader):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -7,14 +7,11 @@ from UM.FlameProfiler import pyqtSlot
|
||||||
from cura.MachineAction import MachineAction
|
from cura.MachineAction import MachineAction
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Preferences import Preferences
|
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||||
|
|
||||||
|
@ -30,13 +27,14 @@ class MachineSettingsAction(MachineAction):
|
||||||
self._qml_url = "MachineSettingsAction.qml"
|
self._qml_url = "MachineSettingsAction.qml"
|
||||||
|
|
||||||
self._global_container_stack = None
|
self._global_container_stack = None
|
||||||
self._container_index = 0
|
|
||||||
|
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||||
|
self._container_index = _ContainerIndexes.DefinitionChanges
|
||||||
|
|
||||||
self._container_registry = ContainerRegistry.getInstance()
|
self._container_registry = ContainerRegistry.getInstance()
|
||||||
self._container_registry.containerAdded.connect(self._onContainerAdded)
|
self._container_registry.containerAdded.connect(self._onContainerAdded)
|
||||||
self._container_registry.containerRemoved.connect(self._onContainerRemoved)
|
self._container_registry.containerRemoved.connect(self._onContainerRemoved)
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
|
||||||
|
|
||||||
self._empty_container = self._container_registry.getEmptyInstanceContainer()
|
self._empty_container = self._container_registry.getEmptyInstanceContainer()
|
||||||
|
|
||||||
|
@ -67,7 +65,9 @@ class MachineSettingsAction(MachineAction):
|
||||||
self._global_container_stack, self._global_container_stack.getName() + "_settings")
|
self._global_container_stack, self._global_container_stack.getName() + "_settings")
|
||||||
|
|
||||||
# Notify the UI in which container to store the machine settings data
|
# Notify the UI in which container to store the machine settings data
|
||||||
container_index = self._global_container_stack.getContainerIndex(definition_changes_container)
|
from cura.Settings.CuraContainerStack import CuraContainerStack, _ContainerIndexes
|
||||||
|
|
||||||
|
container_index = _ContainerIndexes.DefinitionChanges
|
||||||
if container_index != self._container_index:
|
if container_index != self._container_index:
|
||||||
self._container_index = container_index
|
self._container_index = container_index
|
||||||
self.containerIndexChanged.emit()
|
self.containerIndexChanged.emit()
|
||||||
|
@ -82,17 +82,6 @@ class MachineSettingsAction(MachineAction):
|
||||||
if self._backend and self._backend.determineAutoSlicing():
|
if self._backend and self._backend.determineAutoSlicing():
|
||||||
self._backend.tickle()
|
self._backend.tickle()
|
||||||
|
|
||||||
def _onActiveExtruderStackChanged(self):
|
|
||||||
extruder_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
|
||||||
if not self._global_container_stack or not extruder_container_stack:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Make sure there is a definition_changes container to store the machine settings
|
|
||||||
definition_changes_container = extruder_container_stack.definitionChanges
|
|
||||||
if definition_changes_container == self._empty_container:
|
|
||||||
definition_changes_container = CuraStackBuilder.createDefinitionChangesContainer(
|
|
||||||
extruder_container_stack, extruder_container_stack.getId() + "_settings")
|
|
||||||
|
|
||||||
containerIndexChanged = pyqtSignal()
|
containerIndexChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty(int, notify = containerIndexChanged)
|
@pyqtProperty(int, notify = containerIndexChanged)
|
||||||
|
@ -217,8 +206,8 @@ class MachineSettingsAction(MachineAction):
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.emit()
|
Application.getInstance().globalContainerStackChanged.emit()
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot(int)
|
||||||
def updateMaterialForDiameter(self):
|
def updateMaterialForDiameter(self, extruder_position: int):
|
||||||
# Updates the material container to a material that matches the material diameter set for the printer
|
# Updates the material container to a material that matches the material diameter set for the printer
|
||||||
if not self._global_container_stack:
|
if not self._global_container_stack:
|
||||||
return
|
return
|
||||||
|
@ -226,24 +215,22 @@ class MachineSettingsAction(MachineAction):
|
||||||
if not self._global_container_stack.getMetaDataEntry("has_materials", False):
|
if not self._global_container_stack.getMetaDataEntry("has_materials", False):
|
||||||
return
|
return
|
||||||
|
|
||||||
material = ExtruderManager.getInstance().getActiveExtruderStack().material
|
extruder_stack = self._global_container_stack.extruders[str(extruder_position)]
|
||||||
material_diameter = material.getProperty("material_diameter", "value")
|
|
||||||
|
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
|
||||||
if not material_diameter:
|
if not material_diameter:
|
||||||
# in case of "empty" material
|
# in case of "empty" material
|
||||||
material_diameter = 0
|
material_diameter = 0
|
||||||
|
|
||||||
material_approximate_diameter = str(round(material_diameter))
|
material_approximate_diameter = str(round(material_diameter))
|
||||||
definition_changes = self._global_container_stack.definitionChanges
|
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
|
||||||
machine_diameter = definition_changes.getProperty("material_diameter", "value")
|
|
||||||
if not machine_diameter:
|
if not machine_diameter:
|
||||||
machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value")
|
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
|
||||||
machine_approximate_diameter = str(round(machine_diameter))
|
machine_approximate_diameter = str(round(machine_diameter))
|
||||||
|
|
||||||
if material_approximate_diameter != machine_approximate_diameter:
|
if material_approximate_diameter != machine_approximate_diameter:
|
||||||
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
|
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
|
||||||
|
|
||||||
stacks = ExtruderManager.getInstance().getExtruderStacks()
|
|
||||||
|
|
||||||
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
|
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
|
||||||
materials_definition = self._global_container_stack.definition.getId()
|
materials_definition = self._global_container_stack.definition.getId()
|
||||||
has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False)
|
has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False)
|
||||||
|
@ -251,36 +238,38 @@ class MachineSettingsAction(MachineAction):
|
||||||
materials_definition = "fdmprinter"
|
materials_definition = "fdmprinter"
|
||||||
has_material_variants = False
|
has_material_variants = False
|
||||||
|
|
||||||
for stack in stacks:
|
old_material = extruder_stack.material
|
||||||
old_material = stack.material
|
|
||||||
search_criteria = {
|
search_criteria = {
|
||||||
"type": "material",
|
"type": "material",
|
||||||
"approximate_diameter": machine_approximate_diameter,
|
"approximate_diameter": machine_approximate_diameter,
|
||||||
"material": old_material.getMetaDataEntry("material", "value"),
|
"material": old_material.getMetaDataEntry("material", "value"),
|
||||||
|
"brand": old_material.getMetaDataEntry("brand", "value"),
|
||||||
"supplier": old_material.getMetaDataEntry("supplier", "value"),
|
"supplier": old_material.getMetaDataEntry("supplier", "value"),
|
||||||
"color_name": old_material.getMetaDataEntry("color_name", "value"),
|
"color_name": old_material.getMetaDataEntry("color_name", "value"),
|
||||||
"definition": materials_definition
|
"definition": materials_definition
|
||||||
}
|
}
|
||||||
if has_material_variants:
|
if has_material_variants:
|
||||||
search_criteria["variant"] = stack.variant.getId()
|
search_criteria["variant"] = extruder_stack.variant.getId()
|
||||||
|
|
||||||
if old_material == self._empty_container:
|
if old_material == self._empty_container:
|
||||||
search_criteria.pop("material", None)
|
search_criteria.pop("material", None)
|
||||||
search_criteria.pop("supplier", None)
|
search_criteria.pop("supplier", None)
|
||||||
|
search_criteria.pop("brand", None)
|
||||||
search_criteria.pop("definition", None)
|
search_criteria.pop("definition", None)
|
||||||
search_criteria["id"] = stack.getMetaDataEntry("preferred_material")
|
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
||||||
|
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
||||||
if not materials:
|
if not materials:
|
||||||
# Same material with new diameter is not found, search for generic version of the same material type
|
# Same material with new diameter is not found, search for generic version of the same material type
|
||||||
search_criteria.pop("supplier", None)
|
search_criteria.pop("supplier", None)
|
||||||
|
search_criteria.pop("brand", None)
|
||||||
search_criteria["color_name"] = "Generic"
|
search_criteria["color_name"] = "Generic"
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
||||||
if not materials:
|
if not materials:
|
||||||
# Generic material with new diameter is not found, search for preferred material
|
# Generic material with new diameter is not found, search for preferred material
|
||||||
search_criteria.pop("color_name", None)
|
search_criteria.pop("color_name", None)
|
||||||
search_criteria.pop("material", None)
|
search_criteria.pop("material", None)
|
||||||
search_criteria["id"] = stack.getMetaDataEntry("preferred_material")
|
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
||||||
if not materials:
|
if not materials:
|
||||||
# Preferred material with new diameter is not found, search for any material
|
# Preferred material with new diameter is not found, search for any material
|
||||||
|
@ -290,6 +279,6 @@ class MachineSettingsAction(MachineAction):
|
||||||
# Just use empty material as a final fallback
|
# Just use empty material as a final fallback
|
||||||
materials = [self._empty_container]
|
materials = [self._empty_container]
|
||||||
|
|
||||||
Logger.log("i", "Selecting new material: %s" % materials[0].getId())
|
Logger.log("i", "Selecting new material: %s", materials[0].getId())
|
||||||
|
|
||||||
stack.material = materials[0]
|
extruder_stack.material = materials[0]
|
||||||
|
|
|
@ -292,18 +292,6 @@ Cura.MachineAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader
|
|
||||||
{
|
|
||||||
id: materialDiameterField
|
|
||||||
visible: Cura.MachineManager.hasMaterials
|
|
||||||
sourceComponent: numericTextFieldWithUnit
|
|
||||||
property string settingKey: "material_diameter"
|
|
||||||
property string unit: catalog.i18nc("@label", "mm")
|
|
||||||
property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.")
|
|
||||||
property var afterOnEditingFinished: manager.updateMaterialForDiameter
|
|
||||||
property string label: catalog.i18nc("@label", "Material diameter")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +348,6 @@ Cura.MachineAction
|
||||||
if(currentIndex > 0)
|
if(currentIndex > 0)
|
||||||
{
|
{
|
||||||
contentItem.forceActiveFocus();
|
contentItem.forceActiveFocus();
|
||||||
Cura.ExtruderManager.setActiveExtruderIndex(currentIndex - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +384,25 @@ Cura.MachineAction
|
||||||
property bool isExtruderSetting: true
|
property bool isExtruderSetting: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader
|
||||||
|
{
|
||||||
|
id: materialDiameterField
|
||||||
|
visible: Cura.MachineManager.hasMaterials
|
||||||
|
sourceComponent: numericTextFieldWithUnit
|
||||||
|
property string settingKey: "material_diameter"
|
||||||
|
property string label: catalog.i18nc("@label", "Material diameter")
|
||||||
|
property string unit: catalog.i18nc("@label", "mm")
|
||||||
|
property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.")
|
||||||
|
function afterOnEditingFinished()
|
||||||
|
{
|
||||||
|
if (settingsTabs.currentIndex > 0)
|
||||||
|
{
|
||||||
|
manager.updateMaterialForDiameter(settingsTabs.currentIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property bool isExtruderSetting: true
|
||||||
|
}
|
||||||
|
|
||||||
Loader
|
Loader
|
||||||
{
|
{
|
||||||
id: extruderOffsetXField
|
id: extruderOffsetXField
|
||||||
|
@ -495,7 +501,7 @@ Cura.MachineAction
|
||||||
{
|
{
|
||||||
if(settingsTabs.currentIndex > 0)
|
if(settingsTabs.currentIndex > 0)
|
||||||
{
|
{
|
||||||
return Cura.MachineManager.activeStackId;
|
return Cura.ExtruderManager.extruderIds[String(settingsTabs.currentIndex - 1)];
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -548,7 +554,7 @@ Cura.MachineAction
|
||||||
{
|
{
|
||||||
if(settingsTabs.currentIndex > 0)
|
if(settingsTabs.currentIndex > 0)
|
||||||
{
|
{
|
||||||
return Cura.MachineManager.activeStackId;
|
return Cura.ExtruderManager.extruderIds[String(settingsTabs.currentIndex - 1)];
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -581,7 +587,10 @@ Cura.MachineAction
|
||||||
TextField
|
TextField
|
||||||
{
|
{
|
||||||
id: textField
|
id: textField
|
||||||
text: (propertyProvider.properties.value) ? propertyProvider.properties.value : ""
|
text: {
|
||||||
|
const value = propertyProvider.properties.value;
|
||||||
|
return value ? value : "";
|
||||||
|
}
|
||||||
validator: RegExpValidator { regExp: _allowNegative ? /-?[0-9\.]{0,6}/ : /[0-9\.]{0,6}/ }
|
validator: RegExpValidator { regExp: _allowNegative ? /-?[0-9\.]{0,6}/ : /[0-9\.]{0,6}/ }
|
||||||
onEditingFinished:
|
onEditingFinished:
|
||||||
{
|
{
|
||||||
|
@ -590,12 +599,7 @@ Cura.MachineAction
|
||||||
propertyProvider.setPropertyValue("value", text);
|
propertyProvider.setPropertyValue("value", text);
|
||||||
if(_forceUpdateOnChange)
|
if(_forceUpdateOnChange)
|
||||||
{
|
{
|
||||||
var extruderIndex = Cura.ExtruderManager.activeExtruderIndex;
|
|
||||||
manager.forceUpdate();
|
manager.forceUpdate();
|
||||||
if(Cura.ExtruderManager.activeExtruderIndex != extruderIndex)
|
|
||||||
{
|
|
||||||
Cura.ExtruderManager.setActiveExtruderIndex(extruderIndex)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(_afterOnEditingFinished)
|
if(_afterOnEditingFinished)
|
||||||
{
|
{
|
||||||
|
@ -641,7 +645,7 @@ Cura.MachineAction
|
||||||
{
|
{
|
||||||
if(settingsTabs.currentIndex > 0)
|
if(settingsTabs.currentIndex > 0)
|
||||||
{
|
{
|
||||||
return Cura.MachineManager.activeStackId;
|
return Cura.ExtruderManager.extruderIds[String(settingsTabs.currentIndex - 1)];
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ class SimulationPass(RenderPass):
|
||||||
self.bind()
|
self.bind()
|
||||||
|
|
||||||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay, backface_cull = True)
|
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay, backface_cull = True)
|
||||||
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
head_position = None # Indicates the current position of the print head
|
head_position = None # Indicates the current position of the print head
|
||||||
nozzle_node = None
|
nozzle_node = None
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ class SimulationPass(RenderPass):
|
||||||
nozzle_node = node
|
nozzle_node = node
|
||||||
nozzle_node.setVisible(False)
|
nozzle_node.setVisible(False)
|
||||||
|
|
||||||
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
elif issubclass(type(node), SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible() and node.callDecoration("getBuildPlateNumber") == active_build_plate:
|
||||||
layer_data = node.callDecoration("getLayerData")
|
layer_data = node.callDecoration("getLayerData")
|
||||||
if not layer_data:
|
if not layer_data:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -25,7 +25,7 @@ from UM.View.GL.OpenGL import OpenGL
|
||||||
from UM.View.GL.OpenGLContext import OpenGLContext
|
from UM.View.GL.OpenGLContext import OpenGLContext
|
||||||
from UM.View.View import View
|
from UM.View.View import View
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from cura.ConvexHullNode import ConvexHullNode
|
from cura.Scene.ConvexHullNode import ConvexHullNode
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
from .NozzleNode import NozzleNode
|
from .NozzleNode import NozzleNode
|
||||||
|
|
|
@ -212,17 +212,17 @@ geometry41core =
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// left side
|
// left side
|
||||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
|
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||||
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||||
|
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ class SliceInfo(Extension):
|
||||||
|
|
||||||
data["models"].append(model)
|
data["models"].append(model)
|
||||||
|
|
||||||
print_times = print_information._print_time_message_values
|
print_times = print_information.printTimes()
|
||||||
data["print_times"] = {"travel": int(print_times["travel"].getDisplayString(DurationFormat.Format.Seconds)),
|
data["print_times"] = {"travel": int(print_times["travel"].getDisplayString(DurationFormat.Format.Seconds)),
|
||||||
"support": int(print_times["support"].getDisplayString(DurationFormat.Format.Seconds)),
|
"support": int(print_times["support"].getDisplayString(DurationFormat.Format.Seconds)),
|
||||||
"infill": int(print_times["infill"].getDisplayString(DurationFormat.Format.Seconds)),
|
"infill": int(print_times["infill"].getDisplayString(DurationFormat.Format.Seconds)),
|
||||||
|
|
|
@ -243,6 +243,11 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
|
||||||
self._selected_printer = self._automatic_printer # reset to default option
|
self._selected_printer = self._automatic_printer # reset to default option
|
||||||
self._request_job = [nodes, file_name, filter_by_machine, file_handler, kwargs]
|
self._request_job = [nodes, file_name, filter_by_machine, file_handler, kwargs]
|
||||||
|
|
||||||
|
# the build plates to be sent
|
||||||
|
gcodes = getattr(Application.getInstance().getController().getScene(), "gcode_list")
|
||||||
|
self._job_list = list(gcodes.keys())
|
||||||
|
Logger.log("d", "build plates to be sent to printer: %s", (self._job_list))
|
||||||
|
|
||||||
if self._stage != OutputStage.ready:
|
if self._stage != OutputStage.ready:
|
||||||
if self._error_message:
|
if self._error_message:
|
||||||
self._error_message.hide()
|
self._error_message.hide()
|
||||||
|
@ -252,14 +257,15 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self._add_build_plate_number = len(self._job_list) > 1
|
||||||
self.writeStarted.emit(self) # Allow postprocessing before sending data to the printer
|
self.writeStarted.emit(self) # Allow postprocessing before sending data to the printer
|
||||||
|
|
||||||
if len(self._printers) > 1:
|
if len(self._printers) > 1:
|
||||||
self.spawnPrintView() # Ask user how to print it.
|
self.spawnPrintView() # Ask user how to print it.
|
||||||
elif len(self._printers) == 1:
|
elif len(self._printers) == 1:
|
||||||
# If there is only one printer, don't bother asking.
|
# If there is only one printer, don't bother asking.
|
||||||
self.selectAutomaticPrinter()
|
self.selectAutomaticPrinter()
|
||||||
self.sendPrintJob()
|
self.sendPrintJob()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Cluster has no printers, warn the user of this.
|
# Cluster has no printers, warn the user of this.
|
||||||
if self._error_message:
|
if self._error_message:
|
||||||
|
@ -274,28 +280,34 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def sendPrintJob(self):
|
def sendPrintJob(self):
|
||||||
nodes, file_name, filter_by_machine, file_handler, kwargs = self._request_job
|
nodes, file_name, filter_by_machine, file_handler, kwargs = self._request_job
|
||||||
require_printer_name = self._selected_printer["unique_name"]
|
output_build_plate_number = self._job_list.pop(0)
|
||||||
|
gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")[output_build_plate_number]
|
||||||
|
|
||||||
self._send_gcode_start = time.time()
|
self._send_gcode_start = time.time()
|
||||||
Logger.log("d", "Sending print job [%s] to host..." % file_name)
|
Logger.log("d", "Sending print job [%s] to host, build plate [%s]..." % (file_name, output_build_plate_number))
|
||||||
|
|
||||||
if self._stage != OutputStage.ready:
|
if self._stage != OutputStage.ready:
|
||||||
Logger.log("d", "Unable to send print job as the state is %s", self._stage)
|
Logger.log("d", "Unable to send print job as the state is %s", self._stage)
|
||||||
raise OutputDeviceError.DeviceBusyError()
|
raise OutputDeviceError.DeviceBusyError()
|
||||||
self._stage = OutputStage.uploading
|
self._stage = OutputStage.uploading
|
||||||
|
|
||||||
self._file_name = "%s.gcode.gz" % file_name
|
if self._add_build_plate_number:
|
||||||
|
self._file_name = "%s_%d.gcode.gz" % (file_name, output_build_plate_number)
|
||||||
|
else:
|
||||||
|
self._file_name = "%s.gcode.gz" % (file_name)
|
||||||
self._showProgressMessage()
|
self._showProgressMessage()
|
||||||
|
|
||||||
new_request = self._buildSendPrintJobHttpRequest(require_printer_name)
|
require_printer_name = self._selected_printer["unique_name"]
|
||||||
|
|
||||||
|
new_request = self._buildSendPrintJobHttpRequest(require_printer_name, gcode)
|
||||||
if new_request is None or self._stage != OutputStage.uploading:
|
if new_request is None or self._stage != OutputStage.uploading:
|
||||||
return
|
return
|
||||||
self._request = new_request
|
self._request = new_request
|
||||||
self._reply = self._manager.post(self._request, self._multipart)
|
self._reply = self._manager.post(self._request, self._multipart)
|
||||||
self._reply.uploadProgress.connect(self._onUploadProgress)
|
self._reply.uploadProgress.connect(self._onUploadProgress)
|
||||||
# See _finishedPostPrintJobRequest()
|
# See _finishedPrintJobPostRequest()
|
||||||
|
|
||||||
def _buildSendPrintJobHttpRequest(self, require_printer_name):
|
def _buildSendPrintJobHttpRequest(self, require_printer_name, gcode):
|
||||||
api_url = QUrl(self._api_base_uri + "print_jobs/")
|
api_url = QUrl(self._api_base_uri + "print_jobs/")
|
||||||
request = QNetworkRequest(api_url)
|
request = QNetworkRequest(api_url)
|
||||||
# Create multipart request and add the g-code.
|
# Create multipart request and add the g-code.
|
||||||
|
@ -304,9 +316,8 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
|
||||||
# Add gcode
|
# Add gcode
|
||||||
part = QHttpPart()
|
part = QHttpPart()
|
||||||
part.setHeader(QNetworkRequest.ContentDispositionHeader,
|
part.setHeader(QNetworkRequest.ContentDispositionHeader,
|
||||||
'form-data; name="file"; filename="%s"' % self._file_name)
|
'form-data; name="file"; filename="%s"' % (self._file_name))
|
||||||
|
|
||||||
gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")
|
|
||||||
compressed_gcode = self._compressGcode(gcode)
|
compressed_gcode = self._compressGcode(gcode)
|
||||||
if compressed_gcode is None:
|
if compressed_gcode is None:
|
||||||
return None # User aborted print, so stop trying.
|
return None # User aborted print, so stop trying.
|
||||||
|
@ -392,6 +403,9 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
|
||||||
|
|
||||||
self._cleanupRequest()
|
self._cleanupRequest()
|
||||||
|
|
||||||
|
if self._job_list: # start sending next job
|
||||||
|
self.sendPrintJob()
|
||||||
|
|
||||||
def _showRequestFailedMessage(self, reply):
|
def _showRequestFailedMessage(self, reply):
|
||||||
if reply is not None:
|
if reply is not None:
|
||||||
Logger.log("w", "Unable to send print job to group {cluster_name}: {error_string} ({error})".format(
|
Logger.log("w", "Unable to send print job to group {cluster_name}: {error_string} ({error})".format(
|
||||||
|
|
|
@ -675,7 +675,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
|
||||||
Application.getInstance().getController().setActiveStage("MonitorStage")
|
Application.getInstance().getController().setActiveStage("MonitorStage")
|
||||||
self._print_finished = True
|
self._print_finished = True
|
||||||
self.writeStarted.emit(self)
|
self.writeStarted.emit(self)
|
||||||
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")
|
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||||
|
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")[active_build_plate]
|
||||||
|
|
||||||
print_information = Application.getInstance().getPrintInformation()
|
print_information = Application.getInstance().getPrintInformation()
|
||||||
warnings = [] # There might be multiple things wrong. Keep a list of all the stuff we need to warn about.
|
warnings = [] # There might be multiple things wrong. Keep a list of all the stuff we need to warn about.
|
||||||
|
|
|
@ -122,26 +122,6 @@ class VersionUpgrade30to31(VersionUpgrade):
|
||||||
if len(all_quality_changes) <= 1 and not parser.has_option("metadata", "extruder"):
|
if len(all_quality_changes) <= 1 and not parser.has_option("metadata", "extruder"):
|
||||||
self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser)
|
self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser)
|
||||||
|
|
||||||
if parser["metadata"]["type"] == "definition_changes":
|
|
||||||
if parser["general"]["definition"] == "custom":
|
|
||||||
|
|
||||||
# We are only interested in machine_nozzle_size
|
|
||||||
if parser.has_option("values", "machine_nozzle_size"):
|
|
||||||
machine_nozzle_size = parser["values"]["machine_nozzle_size"]
|
|
||||||
|
|
||||||
machine_extruder_count = '1' # by default it is 1 and the value cannot be stored in the global stack
|
|
||||||
if parser.has_option("values", "machine_extruder_count"):
|
|
||||||
machine_extruder_count = parser["values"]["machine_extruder_count"]
|
|
||||||
|
|
||||||
if machine_extruder_count == '1':
|
|
||||||
definition_name = parser["general"]["name"]
|
|
||||||
machine_extruders = self._getSingleExtrusionMachineExtruders(definition_name)
|
|
||||||
|
|
||||||
# For single extruder machine we need only first extruder
|
|
||||||
if len(machine_extruders) !=0:
|
|
||||||
self._updateSingleExtruderDefinitionFile(machine_extruders, machine_nozzle_size)
|
|
||||||
parser.remove_option("values", "machine_nozzle_size")
|
|
||||||
|
|
||||||
# Update version numbers
|
# Update version numbers
|
||||||
parser["general"]["version"] = "2"
|
parser["general"]["version"] = "2"
|
||||||
parser["metadata"]["setting_version"] = "4"
|
parser["metadata"]["setting_version"] = "4"
|
||||||
|
@ -220,123 +200,6 @@ class VersionUpgrade30to31(VersionUpgrade):
|
||||||
|
|
||||||
return quality_changes_containers
|
return quality_changes_containers
|
||||||
|
|
||||||
def _getSingleExtrusionMachineExtruders(self, definition_name):
|
|
||||||
|
|
||||||
machine_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.MachineStack)
|
|
||||||
|
|
||||||
machine_instance_id = None
|
|
||||||
|
|
||||||
# Find machine instances
|
|
||||||
for item in os.listdir(machine_instances_dir):
|
|
||||||
file_path = os.path.join(machine_instances_dir, item)
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
parser = configparser.ConfigParser(interpolation=None)
|
|
||||||
try:
|
|
||||||
parser.read([file_path])
|
|
||||||
except:
|
|
||||||
# skip, it is not a valid stack file
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parser.has_option("metadata", "type"):
|
|
||||||
continue
|
|
||||||
if "machine" != parser["metadata"]["type"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parser.has_option("general", "id"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
id = parser["general"]["id"]
|
|
||||||
if id + "_settings" != definition_name:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
machine_instance_id = id
|
|
||||||
break
|
|
||||||
|
|
||||||
if machine_instance_id is not None:
|
|
||||||
|
|
||||||
extruders_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.ExtruderStack)
|
|
||||||
#"machine",[extruders]
|
|
||||||
extruder_instances = []
|
|
||||||
|
|
||||||
# Find all custom extruders for found machines
|
|
||||||
for item in os.listdir(extruders_instances_dir):
|
|
||||||
file_path = os.path.join(extruders_instances_dir, item)
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
parser = configparser.ConfigParser(interpolation=None)
|
|
||||||
try:
|
|
||||||
parser.read([file_path])
|
|
||||||
except:
|
|
||||||
# skip, it is not a valid stack file
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parser.has_option("metadata", "type"):
|
|
||||||
continue
|
|
||||||
if "extruder_train" != parser["metadata"]["type"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parser.has_option("metadata", "machine"):
|
|
||||||
continue
|
|
||||||
if not parser.has_option("metadata", "position"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if machine_instance_id != parser["metadata"]["machine"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
extruder_instances.append(parser)
|
|
||||||
|
|
||||||
return extruder_instances
|
|
||||||
|
|
||||||
# Find extruder definition at index 0 and update its values
|
|
||||||
def _updateSingleExtruderDefinitionFile(self, extruder_instances_per_machine, machine_nozzle_size):
|
|
||||||
|
|
||||||
defintion_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer)
|
|
||||||
|
|
||||||
for item in os.listdir(defintion_instances_dir):
|
|
||||||
file_path = os.path.join(defintion_instances_dir, item)
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
parser = configparser.ConfigParser(interpolation=None)
|
|
||||||
try:
|
|
||||||
parser.read([file_path])
|
|
||||||
except:
|
|
||||||
# skip, it is not a valid stack file
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not parser.has_option("general", "name"):
|
|
||||||
continue
|
|
||||||
name = parser["general"]["name"]
|
|
||||||
custom_extruder_at_0_position = None
|
|
||||||
for extruder_instance in extruder_instances_per_machine:
|
|
||||||
|
|
||||||
definition_position = extruder_instance["metadata"]["position"]
|
|
||||||
|
|
||||||
if definition_position == "0":
|
|
||||||
custom_extruder_at_0_position = extruder_instance
|
|
||||||
break
|
|
||||||
|
|
||||||
# If not null, then parsed file is for first extuder and then can be updated. I need to update only
|
|
||||||
# first, because this update for single extuder machine
|
|
||||||
if custom_extruder_at_0_position is not None:
|
|
||||||
|
|
||||||
#Add new value
|
|
||||||
parser["values"]["machine_nozzle_size"] = machine_nozzle_size
|
|
||||||
|
|
||||||
definition_output = io.StringIO()
|
|
||||||
parser.write(definition_output)
|
|
||||||
|
|
||||||
with open(file_path, "w") as f:
|
|
||||||
f.write(definition_output.getvalue())
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
|
def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
|
||||||
suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
|
suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
|
||||||
machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "")
|
machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "")
|
||||||
|
|
|
@ -11,7 +11,7 @@ from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||||
|
|
||||||
MYPY = False
|
MYPY = False
|
||||||
try:
|
try:
|
||||||
|
|
55
resources/definitions/anycubic_i3_mega.def.json
Normal file
55
resources/definitions/anycubic_i3_mega.def.json
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"version":2,
|
||||||
|
"name":"Anycubic i3 Mega",
|
||||||
|
"inherits":"fdmprinter",
|
||||||
|
"metadata":{
|
||||||
|
"visible":true,
|
||||||
|
"author":"TheTobby",
|
||||||
|
"manufacturer":"Anycubic",
|
||||||
|
"file_formats":"text/x-gcode",
|
||||||
|
"icon":"icon_ultimaker2",
|
||||||
|
"platform":"anycubic_i3_mega_platform.stl",
|
||||||
|
"has_materials": false,
|
||||||
|
"has_machine_quality": true,
|
||||||
|
"preferred_quality": "*normal*"
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides":{
|
||||||
|
"machine_name":{
|
||||||
|
"default_value":"Anycubic i3 Mega"
|
||||||
|
},
|
||||||
|
"machine_heated_bed":{
|
||||||
|
"default_value":true
|
||||||
|
},
|
||||||
|
"machine_width":{
|
||||||
|
"default_value":210
|
||||||
|
},
|
||||||
|
"machine_height":{
|
||||||
|
"default_value":205
|
||||||
|
},
|
||||||
|
"machine_depth":{
|
||||||
|
"default_value":210
|
||||||
|
},
|
||||||
|
"machine_center_is_zero":{
|
||||||
|
"default_value":false
|
||||||
|
},
|
||||||
|
"machine_nozzle_size":{
|
||||||
|
"default_value":0.4
|
||||||
|
},
|
||||||
|
"material_diameter":{
|
||||||
|
"default_value":1.75
|
||||||
|
},
|
||||||
|
"gantry_height":{
|
||||||
|
"default_value":0
|
||||||
|
},
|
||||||
|
"machine_gcode_flavor":{
|
||||||
|
"default_value":"RepRap (Marlin/Sprinter)"
|
||||||
|
},
|
||||||
|
"machine_start_gcode":{
|
||||||
|
"default_value":"G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\nM117 Printing...\nG5"
|
||||||
|
},
|
||||||
|
"machine_end_gcode":{
|
||||||
|
"default_value":"M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle\nto release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops\nso the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"id": "builder_premium_large",
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"name": "Builder Premium Large",
|
"name": "Builder Premium Large",
|
||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"id": "builder_premium_medium",
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"name": "Builder Premium Medium",
|
"name": "Builder Premium Medium",
|
||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"id": "builder_premium_small",
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"name": "Builder Premium Small",
|
"name": "Builder Premium Small",
|
||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"id": "deltacomb",
|
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"name": "Deltacomb 3D",
|
"name": "Deltacomb 3D",
|
||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
|
|
|
@ -181,6 +181,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"material": {
|
||||||
|
"label": "Material",
|
||||||
|
"icon": "category_material",
|
||||||
|
"description": "Material",
|
||||||
|
"type": "category",
|
||||||
|
"children": {
|
||||||
|
"material_diameter": {
|
||||||
|
"label": "Diameter",
|
||||||
|
"description": "Adjusts the diameter of the filament used. Match this value with the diameter of the used filament.",
|
||||||
|
"unit": "mm",
|
||||||
|
"type": "float",
|
||||||
|
"default_value": 2.85,
|
||||||
|
"minimum_value": "0.0001",
|
||||||
|
"minimum_value_warning": "0.4",
|
||||||
|
"maximum_value_warning": "3.5",
|
||||||
|
"enabled": "machine_gcode_flavor != \"UltiGCode\"",
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"platform_adhesion":
|
"platform_adhesion":
|
||||||
{
|
{
|
||||||
"label": "Build Plate Adhesion",
|
"label": "Build Plate Adhesion",
|
||||||
|
|
|
@ -1614,7 +1614,7 @@
|
||||||
"infill_overlap":
|
"infill_overlap":
|
||||||
{
|
{
|
||||||
"label": "Infill Overlap Percentage",
|
"label": "Infill Overlap Percentage",
|
||||||
"description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.",
|
"description": "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill.",
|
||||||
"unit": "%",
|
"unit": "%",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 10,
|
"default_value": 10,
|
||||||
|
@ -1644,7 +1644,7 @@
|
||||||
"skin_overlap":
|
"skin_overlap":
|
||||||
{
|
{
|
||||||
"label": "Skin Overlap Percentage",
|
"label": "Skin Overlap Percentage",
|
||||||
"description": "The amount of overlap between the skin and the walls as a percentage of the line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall.",
|
"description": "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall.",
|
||||||
"unit": "%",
|
"unit": "%",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 5,
|
"default_value": 5,
|
||||||
|
|
54
resources/definitions/tevo_blackwidow.def.json
Normal file
54
resources/definitions/tevo_blackwidow.def.json
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Tevo Black Widow",
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"visible": true,
|
||||||
|
"author": "TheTobby",
|
||||||
|
"manufacturer": "Tevo",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"icon": "icon_ultimaker2",
|
||||||
|
"has_materials": false,
|
||||||
|
"has_machine_quality": true,
|
||||||
|
"platform": "prusai3_platform.stl",
|
||||||
|
"preferred_quality": "*normal*"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"machine_name": {
|
||||||
|
"default_value": "Tevo Black Widow"
|
||||||
|
},
|
||||||
|
"machine_heated_bed": {
|
||||||
|
"default_value": true
|
||||||
|
},
|
||||||
|
"machine_width": {
|
||||||
|
"default_value": 350
|
||||||
|
},
|
||||||
|
"machine_height": {
|
||||||
|
"default_value": 250
|
||||||
|
},
|
||||||
|
"machine_depth": {
|
||||||
|
"default_value": 250
|
||||||
|
},
|
||||||
|
"machine_center_is_zero": {
|
||||||
|
"default_value": false
|
||||||
|
},
|
||||||
|
"machine_nozzle_size": {
|
||||||
|
"default_value": 0.4
|
||||||
|
},
|
||||||
|
"material_diameter": {
|
||||||
|
"default_value": 1.75
|
||||||
|
},
|
||||||
|
"gantry_height": {
|
||||||
|
"default_value": 0
|
||||||
|
},
|
||||||
|
"machine_gcode_flavor": {
|
||||||
|
"default_value": "RepRap (Marlin/Sprinter)"
|
||||||
|
},
|
||||||
|
"machine_start_gcode": {
|
||||||
|
"default_value": "M280 P0 S160 ; release BLTouch alarm (OK to send for Non BLTouch)\nM420 Z2 ; set fade leveling at 2mm for BLTouch (OK to send for Non BLTouch)\nG28 ; home all\nG29 ; probe bed\nG92 E0 ;zero the extruded length\nG1 X0.0 Y50.0 Z10.0 F3600\n; perform wipe and prime\nG1 Z0.0 F1000\nG1 Z0.2 Y70.0 E9.0 F1000.0 ; prime\nG1 Y100.0 E12.5 F1000.0 ; prime\nG92 E0 ; zero extruder again\nM117 Printing..."
|
||||||
|
},
|
||||||
|
"machine_end_gcode": {
|
||||||
|
"default_value": "G92 E0 ; zero the extruded length again\nG1 E-1.5 F500 ; retract the filament to release some of the pressure\nM104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nG28 X0 ; home X axis\nG1 Y245 ; move Y axis to end position\nM84 ; disable motors\nM107 ; turn off fan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,7 +66,7 @@
|
||||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
|
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
|
||||||
},
|
},
|
||||||
"machine_end_gcode": {
|
"machine_end_gcode": {
|
||||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nG1 Y200 F3600 ;move baseplate to front for easier access to printed object"
|
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG1 X0 Y200 F3600 ;move extruder out of the way by moving the baseplate to the front for easier access to printed object\nM84 ;steppers off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4595
resources/i18n/pt_PT/cura.po
Normal file
4595
resources/i18n/pt_PT/cura.po
Normal file
File diff suppressed because it is too large
Load diff
210
resources/i18n/pt_PT/fdmextruder.def.json.po
Normal file
210
resources/i18n/pt_PT/fdmextruder.def.json.po
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
# Cura JSON setting files
|
||||||
|
# Copyright (C) 2017 Ultimaker
|
||||||
|
# This file is distributed under the same license as the Cura package.
|
||||||
|
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Cura 3.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
|
||||||
|
"POT-Creation-Date: 2017-08-02 16:53+0000\n"
|
||||||
|
"PO-Revision-Date: 2017-12-07 13:41+0100\n"
|
||||||
|
"Last-Translator: Bothof <info@bothof.nl>\n"
|
||||||
|
"Language-Team: Bothof\n"
|
||||||
|
"Language: pt_PT\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_settings label"
|
||||||
|
msgid "Machine"
|
||||||
|
msgstr "Máquina"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_settings description"
|
||||||
|
msgid "Machine specific settings"
|
||||||
|
msgstr "Definições específicas da máquina"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_nr label"
|
||||||
|
msgid "Extruder"
|
||||||
|
msgstr "Extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_nr description"
|
||||||
|
msgid "The extruder train used for printing. This is used in multi-extrusion."
|
||||||
|
msgstr "A máquina extrusora utilizada para imprimir. Esta é utilizada em extrusões múltiplas."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_id label"
|
||||||
|
msgid "Nozzle ID"
|
||||||
|
msgstr "ID do bocal"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_id description"
|
||||||
|
msgid "The nozzle ID for an extruder train, such as \"AA 0.4\" and \"BB 0.8\"."
|
||||||
|
msgstr "A ID do bocal para uma máquina de extrusão, tal como \"AA 0.4\" e \"BB 0.8\"."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_size label"
|
||||||
|
msgid "Nozzle Diameter"
|
||||||
|
msgstr "Diâmetro do bocal"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_size description"
|
||||||
|
msgid ""
|
||||||
|
"The inner diameter of the nozzle. Change this setting when using a non-"
|
||||||
|
"standard nozzle size."
|
||||||
|
msgstr "O diâmetro interno do bocal. Altere esta definição ao utilizar um tamanho de bocal não convencional."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_offset_x label"
|
||||||
|
msgid "Nozzle X Offset"
|
||||||
|
msgstr "Desvio X do bocal"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_offset_x description"
|
||||||
|
msgid "The x-coordinate of the offset of the nozzle."
|
||||||
|
msgstr "A coordenada X do desvio do bocal."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_offset_y label"
|
||||||
|
msgid "Nozzle Y Offset"
|
||||||
|
msgstr "Desvio Y do bocal"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_nozzle_offset_y description"
|
||||||
|
msgid "The y-coordinate of the offset of the nozzle."
|
||||||
|
msgstr "A coordenada Y do desvio do bocal."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_code label"
|
||||||
|
msgid "Extruder Start G-Code"
|
||||||
|
msgstr "G-Code inicial da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_code description"
|
||||||
|
msgid "Start g-code to execute whenever turning the extruder on."
|
||||||
|
msgstr "G-Code inicial a ser executado sempre que a extrusora for ligada."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_abs label"
|
||||||
|
msgid "Extruder Start Position Absolute"
|
||||||
|
msgstr "Posição inicial absoluta da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_abs description"
|
||||||
|
msgid ""
|
||||||
|
"Make the extruder starting position absolute rather than relative to the "
|
||||||
|
"last-known location of the head."
|
||||||
|
msgstr "Torne a posição inicial da extrusora absoluta em vez de relativa à última posição conhecida da cabeça."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_x label"
|
||||||
|
msgid "Extruder Start Position X"
|
||||||
|
msgstr "X da posição inicial da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_x description"
|
||||||
|
msgid "The x-coordinate of the starting position when turning the extruder on."
|
||||||
|
msgstr "A coordenada X da posição inicial ao ligar a extrusora."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_y label"
|
||||||
|
msgid "Extruder Start Position Y"
|
||||||
|
msgstr "Y da posição inicial da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_start_pos_y description"
|
||||||
|
msgid "The y-coordinate of the starting position when turning the extruder on."
|
||||||
|
msgstr "A coordenada Y da posição inicial ao ligar a extrusora."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_code label"
|
||||||
|
msgid "Extruder End G-Code"
|
||||||
|
msgstr "G-Code final da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_code description"
|
||||||
|
msgid "End g-code to execute whenever turning the extruder off."
|
||||||
|
msgstr "G-Code final a ser executado sempre que a extrusora for desligada."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_abs label"
|
||||||
|
msgid "Extruder End Position Absolute"
|
||||||
|
msgstr "Posição final absoluta da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_abs description"
|
||||||
|
msgid ""
|
||||||
|
"Make the extruder ending position absolute rather than relative to the last-"
|
||||||
|
"known location of the head."
|
||||||
|
msgstr "Torne a posição final da extrusora absoluta em vez de relativa à última localização conhecida da cabeça."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_x label"
|
||||||
|
msgid "Extruder End Position X"
|
||||||
|
msgstr "X da posição final da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_x description"
|
||||||
|
msgid "The x-coordinate of the ending position when turning the extruder off."
|
||||||
|
msgstr "A coordenada X da posição final ao desligar a extrusora."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_y label"
|
||||||
|
msgid "Extruder End Position Y"
|
||||||
|
msgstr "Y da posição final da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "machine_extruder_end_pos_y description"
|
||||||
|
msgid "The y-coordinate of the ending position when turning the extruder off."
|
||||||
|
msgstr "A coordenada Y da posição final ao desligar a extrusora."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_z label"
|
||||||
|
msgid "Extruder Prime Z Position"
|
||||||
|
msgstr "Posição Z de preparação da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_z description"
|
||||||
|
msgid ""
|
||||||
|
"The Z coordinate of the position where the nozzle primes at the start of "
|
||||||
|
"printing."
|
||||||
|
msgstr "A coordenada Z da posição de preparação do bocal ao iniciar a impressão."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "platform_adhesion label"
|
||||||
|
msgid "Build Plate Adhesion"
|
||||||
|
msgstr "Aderência à placa de construção"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "platform_adhesion description"
|
||||||
|
msgid "Adhesion"
|
||||||
|
msgstr "Aderência"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_x label"
|
||||||
|
msgid "Extruder Prime X Position"
|
||||||
|
msgstr "Posição X de preparação da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_x description"
|
||||||
|
msgid ""
|
||||||
|
"The X coordinate of the position where the nozzle primes at the start of "
|
||||||
|
"printing."
|
||||||
|
msgstr "A coordenada X da posição de preparação do bocal ao iniciar a impressão."
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_y label"
|
||||||
|
msgid "Extruder Prime Y Position"
|
||||||
|
msgstr "Posição Y de preparação da extrusora"
|
||||||
|
|
||||||
|
#: fdmextruder.def.json
|
||||||
|
msgctxt "extruder_prime_pos_y description"
|
||||||
|
msgid ""
|
||||||
|
"The Y coordinate of the position where the nozzle primes at the start of "
|
||||||
|
"printing."
|
||||||
|
msgstr "A coordenada Y da posição de preparação do bocal ao iniciar a impressão."
|
5814
resources/i18n/pt_PT/fdmprinter.def.json.po
Normal file
5814
resources/i18n/pt_PT/fdmprinter.def.json.po
Normal file
File diff suppressed because it is too large
Load diff
BIN
resources/meshes/anycubic_i3_mega_platform.stl
Normal file
BIN
resources/meshes/anycubic_i3_mega_platform.stl
Normal file
Binary file not shown.
BIN
resources/meshes/tevo_blackwidow.stl
Normal file
BIN
resources/meshes/tevo_blackwidow.stl
Normal file
Binary file not shown.
|
@ -17,7 +17,12 @@ Item
|
||||||
property alias undo: undoAction;
|
property alias undo: undoAction;
|
||||||
property alias redo: redoAction;
|
property alias redo: redoAction;
|
||||||
|
|
||||||
property alias homeCamera: homeCameraAction;
|
property alias view3DCamera: view3DCameraAction;
|
||||||
|
property alias viewFrontCamera: viewFrontCameraAction;
|
||||||
|
property alias viewTopCamera: viewTopCameraAction;
|
||||||
|
property alias viewLeftSideCamera: viewLeftSideCameraAction;
|
||||||
|
property alias viewRightSideCamera: viewRightSideCameraAction;
|
||||||
|
|
||||||
property alias expandSidebar: expandSidebarAction;
|
property alias expandSidebar: expandSidebarAction;
|
||||||
|
|
||||||
property alias deleteSelection: deleteSelectionAction;
|
property alias deleteSelection: deleteSelectionAction;
|
||||||
|
@ -36,6 +41,7 @@ Item
|
||||||
property alias selectAll: selectAllAction;
|
property alias selectAll: selectAllAction;
|
||||||
property alias deleteAll: deleteAllAction;
|
property alias deleteAll: deleteAllAction;
|
||||||
property alias reloadAll: reloadAllAction;
|
property alias reloadAll: reloadAllAction;
|
||||||
|
property alias arrangeAllBuildPlates: arrangeAllBuildPlatesAction;
|
||||||
property alias arrangeAll: arrangeAllAction;
|
property alias arrangeAll: arrangeAllAction;
|
||||||
property alias arrangeSelection: arrangeSelectionAction;
|
property alias arrangeSelection: arrangeSelectionAction;
|
||||||
property alias resetAllTranslation: resetAllTranslationAction;
|
property alias resetAllTranslation: resetAllTranslationAction;
|
||||||
|
@ -104,9 +110,37 @@ Item
|
||||||
|
|
||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: homeCameraAction;
|
id: view3DCameraAction;
|
||||||
text: catalog.i18nc("@action:inmenu menubar:view","&Reset camera position");
|
text: catalog.i18nc("@action:inmenu menubar:view","&3D View");
|
||||||
onTriggered: CuraActions.homeCamera();
|
onTriggered: UM.Controller.rotateView("3d", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: viewFrontCameraAction;
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:view","&Front View");
|
||||||
|
onTriggered: UM.Controller.rotateView("home", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: viewTopCameraAction;
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:view","&Top View");
|
||||||
|
onTriggered: UM.Controller.rotateView("y", 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: viewLeftSideCameraAction;
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:view","&Left Side View");
|
||||||
|
onTriggered: UM.Controller.rotateView("x", 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: viewRightSideCameraAction;
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:view","&Right Side View");
|
||||||
|
onTriggered: UM.Controller.rotateView("x", -90);
|
||||||
}
|
}
|
||||||
|
|
||||||
Action
|
Action
|
||||||
|
@ -311,6 +345,13 @@ Item
|
||||||
onTriggered: CuraApplication.reloadAll();
|
onTriggered: CuraApplication.reloadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: arrangeAllBuildPlatesAction;
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models To All Build Plates");
|
||||||
|
onTriggered: Printer.arrangeObjectsToAllBuildPlates();
|
||||||
|
}
|
||||||
|
|
||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: arrangeAllAction;
|
id: arrangeAllAction;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2016 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
|
@ -25,6 +25,15 @@ UM.Dialog
|
||||||
width: minimumWidth
|
width: minimumWidth
|
||||||
height: minimumHeight
|
height: minimumHeight
|
||||||
|
|
||||||
|
flags: {
|
||||||
|
var window_flags = Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint;
|
||||||
|
if (Cura.MachineManager.activeDefinitionId !== "") //Disallow closing the window if we have no active printer yet. You MUST add a printer.
|
||||||
|
{
|
||||||
|
window_flags |= Qt.WindowCloseButtonHint;
|
||||||
|
}
|
||||||
|
return window_flags;
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityChanged:
|
onVisibilityChanged:
|
||||||
{
|
{
|
||||||
// Reset selection and machine name
|
// Reset selection and machine name
|
||||||
|
|
|
@ -375,6 +375,18 @@ UM.MainWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectsList
|
||||||
|
{
|
||||||
|
id: objectsList;
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate");
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
bottom: parent.bottom;
|
||||||
|
left: parent.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Topbar
|
Topbar
|
||||||
{
|
{
|
||||||
id: topbar
|
id: topbar
|
||||||
|
@ -401,7 +413,7 @@ UM.MainWindow
|
||||||
collapseSidebarAnimation.start();
|
collapseSidebarAnimation.start();
|
||||||
}
|
}
|
||||||
collapsed = !collapsed;
|
collapsed = !collapsed;
|
||||||
UM.Preferences.setValue("cura/sidebar_collapse", collapsed);
|
UM.Preferences.setValue("cura/sidebar_collapsed", collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors
|
anchors
|
||||||
|
@ -432,9 +444,10 @@ UM.MainWindow
|
||||||
|
|
||||||
Component.onCompleted:
|
Component.onCompleted:
|
||||||
{
|
{
|
||||||
var sidebarCollapsed = UM.Preferences.getValue("cura/sidebar_collapse");
|
var sidebar_collapsed = UM.Preferences.getValue("cura/sidebar_collapsed");
|
||||||
|
|
||||||
if (sidebarCollapsed) {
|
if (sidebar_collapsed)
|
||||||
|
{
|
||||||
sidebar.collapsed = true;
|
sidebar.collapsed = true;
|
||||||
viewportRect = Qt.rect(0, 0, 1, 1.0)
|
viewportRect = Qt.rect(0, 0, 1, 1.0)
|
||||||
collapseSidebarAnimation.start();
|
collapseSidebarAnimation.start();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import QtQuick.Dialogs 1.2
|
||||||
import QtQuick.Window 2.1
|
import QtQuick.Window 2.1
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.2 as Cura
|
||||||
|
|
||||||
Menu
|
Menu
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,35 @@ Menu
|
||||||
onObjectRemoved: base.removeItem(object)
|
onObjectRemoved: base.removeItem(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuSeparator {
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate")
|
||||||
|
}
|
||||||
|
|
||||||
|
Instantiator
|
||||||
|
{
|
||||||
|
model: Cura.BuildPlateModel
|
||||||
|
MenuItem {
|
||||||
|
text: Cura.BuildPlateModel.getItem(index).name;
|
||||||
|
onTriggered: CuraActions.setBuildPlateForSelection(Cura.BuildPlateModel.getItem(index).buildPlateNumber);
|
||||||
|
checkable: true
|
||||||
|
checked: Cura.BuildPlateModel.selectionBuildPlates.indexOf(Cura.BuildPlateModel.getItem(index).buildPlateNumber) != -1;
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate")
|
||||||
|
}
|
||||||
|
onObjectAdded: base.insertItem(index, object);
|
||||||
|
onObjectRemoved: base.removeItem(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "New build plate";
|
||||||
|
onTriggered: {
|
||||||
|
CuraActions.setBuildPlateForSelection(Cura.BuildPlateModel.maxBuildPlate + 1);
|
||||||
|
checked = false;
|
||||||
|
}
|
||||||
|
checkable: true
|
||||||
|
checked: false
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate")
|
||||||
|
}
|
||||||
|
|
||||||
// Global actions
|
// Global actions
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
MenuItem { action: Cura.Actions.selectAll; }
|
MenuItem { action: Cura.Actions.selectAll; }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
|
@ -33,9 +33,10 @@ Menu
|
||||||
{
|
{
|
||||||
id: materialDiameterProvider
|
id: materialDiameterProvider
|
||||||
|
|
||||||
containerStackId: Cura.MachineManager.activeMachineId
|
containerStackId: Cura.ExtruderManager.activeExtruderStackId
|
||||||
key: "material_diameter"
|
key: "material_diameter"
|
||||||
watchedProperties: [ "value" ]
|
watchedProperties: [ "value" ]
|
||||||
|
storeIndex: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem
|
MenuItem
|
||||||
|
|
|
@ -5,12 +5,12 @@ import QtQuick 2.2
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.1
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.2 as Cura
|
||||||
|
|
||||||
Menu
|
Menu
|
||||||
{
|
{
|
||||||
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
||||||
id: menu
|
id: base
|
||||||
enabled: !PrintInformation.preSliced
|
enabled: !PrintInformation.preSliced
|
||||||
|
|
||||||
// main views
|
// main views
|
||||||
|
@ -25,12 +25,49 @@ Menu
|
||||||
exclusiveGroup: group
|
exclusiveGroup: group
|
||||||
onTriggered: UM.Controller.setActiveView(model.id)
|
onTriggered: UM.Controller.setActiveView(model.id)
|
||||||
}
|
}
|
||||||
onObjectAdded: menu.insertItem(index, object)
|
onObjectAdded: base.insertItem(index, object)
|
||||||
onObjectRemoved: menu.removeItem(object)
|
onObjectRemoved: base.removeItem(object)
|
||||||
}
|
}
|
||||||
ExclusiveGroup { id: group }
|
ExclusiveGroup { id: group }
|
||||||
|
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
MenuItem { action: Cura.Actions.homeCamera; }
|
|
||||||
|
Menu
|
||||||
|
{
|
||||||
|
title: catalog.i18nc("@action:inmenu menubar:view","&Camera position");
|
||||||
|
MenuItem { action: Cura.Actions.view3DCamera; }
|
||||||
|
MenuItem { action: Cura.Actions.viewFrontCamera; }
|
||||||
|
MenuItem { action: Cura.Actions.viewTopCamera; }
|
||||||
|
MenuItem { action: Cura.Actions.viewLeftSideCamera; }
|
||||||
|
MenuItem { action: Cura.Actions.viewRightSideCamera; }
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuSeparator {
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate")
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu
|
||||||
|
{
|
||||||
|
id: buildPlateMenu;
|
||||||
|
title: catalog.i18nc("@action:inmenu menubar:view","&Build plate");
|
||||||
|
Instantiator
|
||||||
|
{
|
||||||
|
model: Cura.BuildPlateModel
|
||||||
|
MenuItem {
|
||||||
|
text: Cura.BuildPlateModel.getItem(index).name;
|
||||||
|
onTriggered: Cura.SceneController.setActiveBuildPlate(Cura.BuildPlateModel.getItem(index).buildPlateNumber);
|
||||||
|
checkable: true;
|
||||||
|
checked: Cura.BuildPlateModel.getItem(index).buildPlateNumber == Cura.BuildPlateModel.activeBuildPlate;
|
||||||
|
exclusiveGroup: buildPlateGroup;
|
||||||
|
visible: UM.Preferences.getValue("cura/use_multi_build_plate")
|
||||||
|
}
|
||||||
|
onObjectAdded: buildPlateMenu.insertItem(index, object);
|
||||||
|
onObjectRemoved: buildPlateMenu.removeItem(object)
|
||||||
|
}
|
||||||
|
ExclusiveGroup { id: buildPlateGroup; }
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuSeparator {}
|
||||||
|
|
||||||
MenuItem { action: Cura.Actions.expandSidebar; }
|
MenuItem { action: Cura.Actions.expandSidebar; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,12 +199,12 @@ Item
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateAdditionalComponents("monitorButtons")
|
buttonsRow.updateAdditionalComponents("monitorButtons")
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: CuraApplication
|
target: CuraApplication
|
||||||
onAdditionalComponentsChanged: updateAdditionalComponents
|
onAdditionalComponentsChanged: buttonsRow.updateAdditionalComponents("monitorButtons")
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAdditionalComponents (areaId) {
|
function updateAdditionalComponents (areaId) {
|
||||||
|
|
267
resources/qml/ObjectsList.qml
Normal file
267
resources/qml/ObjectsList.qml
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.1
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
|
||||||
|
import UM 1.3 as UM
|
||||||
|
import Cura 1.2 as Cura
|
||||||
|
|
||||||
|
import "Menus"
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: base;
|
||||||
|
|
||||||
|
color: UM.Theme.getColor("tool_panel_background")
|
||||||
|
|
||||||
|
width: UM.Theme.getSize("objects_menu_size").width
|
||||||
|
height: {
|
||||||
|
if (collapsed) {
|
||||||
|
return UM.Theme.getSize("objects_menu_size_collapsed").height;
|
||||||
|
} else {
|
||||||
|
return UM.Theme.getSize("objects_menu_size").height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on height { NumberAnimation { duration: 100 } }
|
||||||
|
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
|
||||||
|
property bool collapsed: false;
|
||||||
|
|
||||||
|
SystemPalette { id: palette }
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: collapseButton
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Math.floor(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2)
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
|
width: UM.Theme.getSize("standard_arrow").width
|
||||||
|
height: UM.Theme.getSize("standard_arrow").height
|
||||||
|
|
||||||
|
onClicked: collapsed = !collapsed
|
||||||
|
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: UM.RecolorImage
|
||||||
|
{
|
||||||
|
width: control.width
|
||||||
|
height: control.height
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: width
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
source: collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
|
||||||
|
}
|
||||||
|
label: Label{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: buildPlateDelegate
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
color: Cura.BuildPlateModel.getItem(index).buildPlateNumber == Cura.BuildPlateModel.activeBuildPlate ? palette.highlight : index % 2 ? palette.base : palette.alternateBase
|
||||||
|
width: parent.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: buildPlateNameLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30
|
||||||
|
text: Cura.BuildPlateModel.getItem(index) ? Cura.BuildPlateModel.getItem(index).name : "";
|
||||||
|
color: Cura.BuildPlateModel.activeBuildPlate == index ? palette.highlightedText : palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
anchors.fill: parent;
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
Cura.SceneController.setActiveBuildPlate(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView
|
||||||
|
{
|
||||||
|
id: buildPlateSelection
|
||||||
|
frameVisible: true
|
||||||
|
height: UM.Theme.getSize("build_plate_selection_size").height
|
||||||
|
width: parent.width - 2 * UM.Theme.getSize("default_margin").height
|
||||||
|
style: UM.Theme.styles.scrollview
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: collapseButton.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
//bottom: objectsList.top;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
parent: viewport
|
||||||
|
anchors.fill: parent
|
||||||
|
color: palette.light
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView
|
||||||
|
{
|
||||||
|
id: buildPlateListView
|
||||||
|
model: Cura.BuildPlateModel
|
||||||
|
width: parent.width
|
||||||
|
delegate: buildPlateDelegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: objectDelegate
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlight : index % 2 ? palette.base : palette.alternateBase
|
||||||
|
width: parent.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: nodeNameLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30
|
||||||
|
text: Cura.ObjectsModel.getItem(index) ? Cura.ObjectsModel.getItem(index).name : "";
|
||||||
|
color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlightedText : (Cura.ObjectsModel.getItem(index).isOutsideBuildArea ? palette.mid : palette.text)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: buildPlateNumberLabel
|
||||||
|
width: 20
|
||||||
|
anchors.left: nodeNameLabel.right
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: Cura.ObjectsModel.getItem(index).buildPlateNumber != -1 ? Cura.ObjectsModel.getItem(index).buildPlateNumber + 1 : "";
|
||||||
|
color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlightedText : palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
anchors.fill: parent;
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
Cura.SceneController.changeSelection(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all the scene nodes
|
||||||
|
ScrollView
|
||||||
|
{
|
||||||
|
id: objectsList
|
||||||
|
frameVisible: true
|
||||||
|
visible: !collapsed
|
||||||
|
width: parent.width - 2 * UM.Theme.getSize("default_margin").height
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: buildPlateSelection.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
bottom: filterBuildPlateCheckbox.top;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
parent: viewport
|
||||||
|
anchors.fill: parent
|
||||||
|
color: palette.light
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView
|
||||||
|
{
|
||||||
|
id: listview
|
||||||
|
model: Cura.ObjectsModel
|
||||||
|
width: parent.width
|
||||||
|
delegate: objectDelegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CheckBox
|
||||||
|
{
|
||||||
|
id: filterBuildPlateCheckbox
|
||||||
|
visible: !collapsed
|
||||||
|
checked: UM.Preferences.getValue("view/filter_current_build_plate")
|
||||||
|
onClicked: UM.Preferences.setValue("view/filter_current_build_plate", checked)
|
||||||
|
|
||||||
|
text: catalog.i18nc("@option:check","See only current build plate");
|
||||||
|
style: UM.Theme.styles.checkbox;
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
bottom: arrangeAllBuildPlatesButton.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: arrangeAllBuildPlatesButton;
|
||||||
|
text: catalog.i18nc("@action:button","Arrange to all build plates");
|
||||||
|
style: UM.Theme.styles.sidebar_action_button
|
||||||
|
height: UM.Theme.getSize("objects_menu_button").height;
|
||||||
|
tooltip: '';
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
//top: buildPlateSelection.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
bottom: arrangeBuildPlateButton.top;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
action: Cura.Actions.arrangeAllBuildPlates;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: arrangeBuildPlateButton;
|
||||||
|
text: catalog.i18nc("@action:button","Arrange current build plate");
|
||||||
|
style: UM.Theme.styles.sidebar_action_button
|
||||||
|
height: UM.Theme.getSize("objects_menu_button").height;
|
||||||
|
tooltip: '';
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
action: Cura.Actions.arrangeAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -159,6 +159,7 @@ UM.PreferencesPage
|
||||||
append({ text: "Nederlands", code: "nl_NL" })
|
append({ text: "Nederlands", code: "nl_NL" })
|
||||||
append({ text: "Polski", code: "pl_PL" })
|
append({ text: "Polski", code: "pl_PL" })
|
||||||
append({ text: "Português do Brasil", code: "pt_BR" })
|
append({ text: "Português do Brasil", code: "pt_BR" })
|
||||||
|
append({ text: "Português", code: "pt_PT" })
|
||||||
append({ text: "Русский", code: "ru_RU" })
|
append({ text: "Русский", code: "ru_RU" })
|
||||||
append({ text: "Türkçe", code: "tr_TR" })
|
append({ text: "Türkçe", code: "tr_TR" })
|
||||||
append({ text: "简体中文", code: "zh_CN" })
|
append({ text: "简体中文", code: "zh_CN" })
|
||||||
|
@ -452,6 +453,34 @@ UM.PreferencesPage
|
||||||
text: catalog.i18nc("@label","Opening and saving files")
|
text: catalog.i18nc("@label","Opening and saving files")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UM.TooltipArea {
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height
|
||||||
|
text: catalog.i18nc("@info:tooltip","Use multi build plate functionality (EXPERIMENTAL)")
|
||||||
|
|
||||||
|
CheckBox
|
||||||
|
{
|
||||||
|
id: useMultiBuildPlateCheckbox
|
||||||
|
text: catalog.i18nc("@option:check","Use multi build plate functionality (EXPERIMENTAL, restart)")
|
||||||
|
checked: boolCheck(UM.Preferences.getValue("cura/use_multi_build_plate"))
|
||||||
|
onCheckedChanged: UM.Preferences.setValue("cura/use_multi_build_plate", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UM.TooltipArea {
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height
|
||||||
|
text: catalog.i18nc("@info:tooltip","Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)")
|
||||||
|
|
||||||
|
CheckBox
|
||||||
|
{
|
||||||
|
id: arrangeOnLoadCheckbox
|
||||||
|
text: catalog.i18nc("@option:check","Arrange objects on load (EXPERIMENTAL)")
|
||||||
|
checked: boolCheck(UM.Preferences.getValue("cura/arrange_objects_on_load"))
|
||||||
|
onCheckedChanged: UM.Preferences.setValue("cura/arrange_objects_on_load", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UM.TooltipArea {
|
UM.TooltipArea {
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
|
@ -387,9 +387,10 @@ UM.ManagementPage
|
||||||
{
|
{
|
||||||
id: materialDiameterProvider
|
id: materialDiameterProvider
|
||||||
|
|
||||||
containerStackId: Cura.MachineManager.activeMachineId
|
containerStackId: Cura.ExtruderManager.activeExtruderStackId
|
||||||
key: "material_diameter"
|
key: "material_diameter"
|
||||||
watchedProperties: [ "value" ]
|
watchedProperties: [ "value" ]
|
||||||
|
storeIndex: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||||
|
|
|
@ -138,12 +138,12 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
addAdditionalComponents("saveButton")
|
saveRow.addAdditionalComponents("saveButton")
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: CuraApplication
|
target: CuraApplication
|
||||||
onAdditionalComponentsChanged: addAdditionalComponents
|
onAdditionalComponentsChanged: saveRow.addAdditionalComponents("saveButton")
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAdditionalComponents (areaId) {
|
function addAdditionalComponents (areaId) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2015 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.1
|
||||||
|
@ -31,13 +31,17 @@ Button {
|
||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
forceActiveFocus();
|
|
||||||
if(definition.expanded)
|
if(definition.expanded)
|
||||||
{
|
{
|
||||||
settingDefinitionsModel.collapse(definition.key);
|
settingDefinitionsModel.collapse(definition.key);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
settingDefinitionsModel.expandAll(definition.key);
|
settingDefinitionsModel.expandAll(definition.key);
|
||||||
}
|
}
|
||||||
|
//Set focus so that tab navigation continues from this point on.
|
||||||
|
//NB: This must be set AFTER collapsing/expanding the category so that the scroll position is correct.
|
||||||
|
forceActiveFocus();
|
||||||
}
|
}
|
||||||
onActiveFocusChanged:
|
onActiveFocusChanged:
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2015 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
|
@ -154,7 +154,7 @@ Item {
|
||||||
|
|
||||||
onEntered: {
|
onEntered: {
|
||||||
hoverTimer.stop();
|
hoverTimer.stop();
|
||||||
var tooltipText = catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders") + ".";
|
var tooltipText = catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders.");
|
||||||
if ((resolve != "None") && (stackLevel != 0)) {
|
if ((resolve != "None") && (stackLevel != 0)) {
|
||||||
// We come here if a setting has a resolve and the setting is not manually edited.
|
// We come here if a setting has a resolve and the setting is not manually edited.
|
||||||
tooltipText += " " + catalog.i18nc("@label", "The value is resolved from per-extruder values ") + "[" + Cura.ExtruderManager.getInstanceExtruderValues(definition.key) + "].";
|
tooltipText += " " + catalog.i18nc("@label", "The value is resolved from per-extruder values ") + "[" + Cura.ExtruderManager.getInstanceExtruderValues(definition.key) + "].";
|
||||||
|
|
|
@ -97,7 +97,7 @@ Rectangle
|
||||||
SidebarHeader {
|
SidebarHeader {
|
||||||
id: header
|
id: header
|
||||||
width: parent.width
|
width: parent.width
|
||||||
visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants
|
visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && !monitoringPrint
|
||||||
anchors.top: machineSelection.bottom
|
anchors.top: machineSelection.bottom
|
||||||
|
|
||||||
onShowTooltip: base.showTooltip(item, location, text)
|
onShowTooltip: base.showTooltip(item, location, text)
|
||||||
|
|
|
@ -340,6 +340,8 @@ Item
|
||||||
text: catalog.i18nc("@label", "Print Speed")
|
text: catalog.i18nc("@label", "Print Speed")
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
color: UM.Theme.getColor("text")
|
color: UM.Theme.getColor("text")
|
||||||
|
width: parseInt(UM.Theme.getSize("sidebar").width * 0.35)
|
||||||
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
|
|
@ -25,9 +25,12 @@ Rectangle
|
||||||
property int allItemsWidth: 0;
|
property int allItemsWidth: 0;
|
||||||
|
|
||||||
function updateMarginsAndSizes() {
|
function updateMarginsAndSizes() {
|
||||||
if (UM.Preferences.getValue("cura/sidebar_collapse")) {
|
if (UM.Preferences.getValue("cura/sidebar_collapsed"))
|
||||||
|
{
|
||||||
rightMargin = UM.Theme.getSize("default_margin").width;
|
rightMargin = UM.Theme.getSize("default_margin").width;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width;
|
rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width;
|
||||||
}
|
}
|
||||||
allItemsWidth = (
|
allItemsWidth = (
|
||||||
|
@ -112,10 +115,8 @@ Rectangle
|
||||||
iconSource: UM.Theme.getIcon("view_3d")
|
iconSource: UM.Theme.getIcon("view_3d")
|
||||||
style: UM.Theme.styles.small_tool_button
|
style: UM.Theme.styles.small_tool_button
|
||||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
onClicked:{
|
onClicked:UM.Controller.rotateView("3d", 0)
|
||||||
UM.Controller.rotateView("3d", 0);
|
visible: base.width - allItemsWidth - 4 * this.width > 0
|
||||||
}
|
|
||||||
visible: base.width - allItemsWidth - 4 * this.width > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #2 Front view
|
// #2 Front view
|
||||||
|
@ -124,10 +125,8 @@ Rectangle
|
||||||
iconSource: UM.Theme.getIcon("view_front")
|
iconSource: UM.Theme.getIcon("view_front")
|
||||||
style: UM.Theme.styles.small_tool_button
|
style: UM.Theme.styles.small_tool_button
|
||||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
onClicked:{
|
onClicked: UM.Controller.rotateView("home", 0);
|
||||||
UM.Controller.rotateView("home", 0);
|
visible: base.width - allItemsWidth - 3 * this.width > 0
|
||||||
}
|
|
||||||
visible: base.width - allItemsWidth - 3 * this.width > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #3 Top view
|
// #3 Top view
|
||||||
|
@ -136,10 +135,8 @@ Rectangle
|
||||||
iconSource: UM.Theme.getIcon("view_top")
|
iconSource: UM.Theme.getIcon("view_top")
|
||||||
style: UM.Theme.styles.small_tool_button
|
style: UM.Theme.styles.small_tool_button
|
||||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
onClicked:{
|
onClicked: UM.Controller.rotateView("y", 90)
|
||||||
UM.Controller.rotateView("y", 90);
|
visible: base.width - allItemsWidth - 2 * this.width > 0
|
||||||
}
|
|
||||||
visible: base.width - allItemsWidth - 2 * this.width > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #4 Left view
|
// #4 Left view
|
||||||
|
@ -148,10 +145,8 @@ Rectangle
|
||||||
iconSource: UM.Theme.getIcon("view_left")
|
iconSource: UM.Theme.getIcon("view_left")
|
||||||
style: UM.Theme.styles.small_tool_button
|
style: UM.Theme.styles.small_tool_button
|
||||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
onClicked:{
|
onClicked: UM.Controller.rotateView("x", 90)
|
||||||
UM.Controller.rotateView("x", 90);
|
visible: base.width - allItemsWidth - 1 * this.width > 0
|
||||||
}
|
|
||||||
visible: base.width - allItemsWidth - 1 * this.width > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #5 Left view
|
// #5 Left view
|
||||||
|
@ -160,10 +155,8 @@ Rectangle
|
||||||
iconSource: UM.Theme.getIcon("view_right")
|
iconSource: UM.Theme.getIcon("view_right")
|
||||||
style: UM.Theme.styles.small_tool_button
|
style: UM.Theme.styles.small_tool_button
|
||||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
onClicked:{
|
onClicked: UM.Controller.rotateView("x", -90)
|
||||||
UM.Controller.rotateView("x", -90);
|
visible: base.width - allItemsWidth > 0
|
||||||
}
|
|
||||||
visible: base.width - allItemsWidth > 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = Draft
|
||||||
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = 0
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
acceleration_enabled = True
|
||||||
|
acceleration_print = 2000
|
||||||
|
acceleration_travel = 3500
|
||||||
|
adhesion_type = skirt
|
||||||
|
brim_width = 4.0
|
||||||
|
cool_fan_full_at_height = 0.5
|
||||||
|
cool_fan_speed = 100
|
||||||
|
cool_fan_speed_0 = 100
|
||||||
|
infill_overlap = 15
|
||||||
|
infill_pattern = zigzag
|
||||||
|
infill_sparse_density = 25
|
||||||
|
initial_layer_line_width_factor = 140
|
||||||
|
jerk_enabled = True
|
||||||
|
jerk_print = 13
|
||||||
|
jerk_travel = 13
|
||||||
|
layer_height = 0.4
|
||||||
|
layer_height_0 = 0.4
|
||||||
|
material_bed_temperature = 60
|
||||||
|
material_diameter = 1.75
|
||||||
|
material_print_temperature = 200
|
||||||
|
material_print_temperature_layer_0 = 0
|
||||||
|
retract_at_layer_change = False
|
||||||
|
retraction_amount = 7
|
||||||
|
retraction_hop = 0.075
|
||||||
|
retraction_hop_enabled = True
|
||||||
|
retraction_hop_only_when_collides = True
|
||||||
|
retraction_min_travel = 1.5
|
||||||
|
retraction_speed = 40
|
||||||
|
skirt_brim_speed = 40
|
||||||
|
skirt_gap = 5
|
||||||
|
skirt_line_count = 3
|
||||||
|
speed_infill = 60
|
||||||
|
speed_print = 60
|
||||||
|
speed_support = 60
|
||||||
|
speed_topbottom = 30
|
||||||
|
speed_travel = 100
|
||||||
|
speed_wall = 60
|
||||||
|
speed_wall_x = 60
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -0,0 +1,60 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = High
|
||||||
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 2
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
acceleration_enabled = True
|
||||||
|
acceleration_print = 2000
|
||||||
|
acceleration_travel = 3500
|
||||||
|
adhesion_type = skirt
|
||||||
|
brim_width = 4.0
|
||||||
|
cool_fan_full_at_height = 0.5
|
||||||
|
cool_fan_speed = 100
|
||||||
|
cool_fan_speed_0 = 100
|
||||||
|
infill_overlap = 15
|
||||||
|
infill_pattern = zigzag
|
||||||
|
infill_sparse_density = 25
|
||||||
|
initial_layer_line_width_factor = 140
|
||||||
|
jerk_enabled = True
|
||||||
|
jerk_print = 13
|
||||||
|
jerk_travel = 13
|
||||||
|
layer_height = 0.1
|
||||||
|
layer_height_0 = 0.1
|
||||||
|
material_bed_temperature = 60
|
||||||
|
material_diameter = 1.75
|
||||||
|
material_print_temperature = 200
|
||||||
|
material_print_temperature_layer_0 = 0
|
||||||
|
retract_at_layer_change = False
|
||||||
|
retraction_amount = 7
|
||||||
|
retraction_hop = 0.075
|
||||||
|
retraction_hop_enabled = True
|
||||||
|
retraction_hop_only_when_collides = True
|
||||||
|
retraction_min_travel = 1.5
|
||||||
|
retraction_speed = 40
|
||||||
|
skirt_brim_speed = 40
|
||||||
|
skirt_gap = 5
|
||||||
|
skirt_line_count = 3
|
||||||
|
speed_infill = 50
|
||||||
|
speed_print = 50
|
||||||
|
speed_support = 30
|
||||||
|
speed_topbottom = 20
|
||||||
|
speed_travel = 50
|
||||||
|
speed_wall = 50
|
||||||
|
speed_wall_x = 50
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -0,0 +1,60 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = Normal
|
||||||
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = normal
|
||||||
|
weight = 1
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
acceleration_enabled = True
|
||||||
|
acceleration_print = 2000
|
||||||
|
acceleration_travel = 3500
|
||||||
|
adhesion_type = skirt
|
||||||
|
brim_width = 4.0
|
||||||
|
cool_fan_full_at_height = 0.5
|
||||||
|
cool_fan_speed = 100
|
||||||
|
cool_fan_speed_0 = 100
|
||||||
|
infill_overlap = 15
|
||||||
|
infill_pattern = zigzag
|
||||||
|
infill_sparse_density = 25
|
||||||
|
initial_layer_line_width_factor = 140
|
||||||
|
jerk_enabled = True
|
||||||
|
jerk_print = 13
|
||||||
|
jerk_travel = 13
|
||||||
|
layer_height = 0.2
|
||||||
|
layer_height_0 = 0.2
|
||||||
|
material_bed_temperature = 60
|
||||||
|
material_diameter = 1.75
|
||||||
|
material_print_temperature = 200
|
||||||
|
material_print_temperature_layer_0 = 0
|
||||||
|
retract_at_layer_change = False
|
||||||
|
retraction_amount = 7
|
||||||
|
retraction_hop = 0.075
|
||||||
|
retraction_hop_enabled = True
|
||||||
|
retraction_hop_only_when_collides = True
|
||||||
|
retraction_min_travel = 1.5
|
||||||
|
retraction_speed = 40
|
||||||
|
skirt_brim_speed = 40
|
||||||
|
skirt_gap = 5
|
||||||
|
skirt_line_count = 3
|
||||||
|
speed_infill = 50
|
||||||
|
speed_print = 50
|
||||||
|
speed_support = 30
|
||||||
|
speed_topbottom = 20
|
||||||
|
speed_travel = 100
|
||||||
|
speed_wall = 50
|
||||||
|
speed_wall_x = 50
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -0,0 +1,33 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = Draft
|
||||||
|
definition = tevo_blackwidow
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = -2
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 4.0
|
||||||
|
infill_pattern = zigzag
|
||||||
|
layer_height = 0.4
|
||||||
|
material_diameter = 1.75
|
||||||
|
speed_infill = 50
|
||||||
|
speed_print = 50
|
||||||
|
speed_support = 30
|
||||||
|
speed_topbottom = 20
|
||||||
|
speed_travel = 100
|
||||||
|
speed_wall = 50
|
||||||
|
speed_wall_x = 50
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -0,0 +1,33 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = High
|
||||||
|
definition = tevo_blackwidow
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 1
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 4.0
|
||||||
|
infill_pattern = zigzag
|
||||||
|
layer_height = 0.1
|
||||||
|
material_diameter = 1.75
|
||||||
|
speed_infill = 50
|
||||||
|
speed_print = 50
|
||||||
|
speed_support = 30
|
||||||
|
speed_topbottom = 15
|
||||||
|
speed_travel = 100
|
||||||
|
speed_wall = 50
|
||||||
|
speed_wall_x = 50
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -0,0 +1,33 @@
|
||||||
|
[general]
|
||||||
|
version = 2
|
||||||
|
name = Normal
|
||||||
|
definition = tevo_blackwidow
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
type = quality
|
||||||
|
quality_type = normal
|
||||||
|
weight = 0
|
||||||
|
setting_version = 4
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 4.0
|
||||||
|
infill_pattern = zigzag
|
||||||
|
layer_height = 0.2
|
||||||
|
material_diameter = 1.75
|
||||||
|
speed_infill = 60
|
||||||
|
speed_print = 50
|
||||||
|
speed_support = 30
|
||||||
|
speed_topbottom = 20
|
||||||
|
speed_travel = 100
|
||||||
|
speed_wall = 50
|
||||||
|
speed_wall_x = 50
|
||||||
|
support_angle = 60
|
||||||
|
support_enable = True
|
||||||
|
support_interface_enable = True
|
||||||
|
support_pattern = triangles
|
||||||
|
support_roof_enable = True
|
||||||
|
support_type = everywhere
|
||||||
|
support_use_towers = False
|
||||||
|
support_xy_distance = 0.7
|
||||||
|
top_bottom_thickness = 1.2
|
||||||
|
wall_thickness = 1.2
|
|
@ -202,9 +202,8 @@ QtObject {
|
||||||
height: Theme.getSize("topbar_button_icon").height
|
height: Theme.getSize("topbar_button_icon").height
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
|
id: button_label
|
||||||
text: control.text;
|
text: control.text;
|
||||||
anchors.right: (icon.visible || overlayIcon.visible) ? icon.left : parent.right
|
|
||||||
anchors.rightMargin: (icon.visible || overlayIcon.visible) ? Theme.getSize("default_margin").width : 0
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
anchors.verticalCenter: parent.verticalCenter;
|
||||||
font: control.checked ? UM.Theme.getFont("large") : UM.Theme.getFont("large_nonbold")
|
font: control.checked ? UM.Theme.getFont("large") : UM.Theme.getFont("large_nonbold")
|
||||||
color:
|
color:
|
||||||
|
@ -227,6 +226,8 @@ QtObject {
|
||||||
{
|
{
|
||||||
visible: control.iconSource != ""
|
visible: control.iconSource != ""
|
||||||
id: icon
|
id: icon
|
||||||
|
anchors.left: button_label.right
|
||||||
|
anchors.leftMargin: (icon.visible || overlayIcon.visible) ? Theme.getSize("default_margin").width : 0
|
||||||
color: UM.Theme.getColor("text_emphasis")
|
color: UM.Theme.getColor("text_emphasis")
|
||||||
opacity: !control.enabled ? 0.2 : 1.0
|
opacity: !control.enabled ? 0.2 : 1.0
|
||||||
source: control.iconSource
|
source: control.iconSource
|
||||||
|
@ -238,6 +239,8 @@ QtObject {
|
||||||
UM.RecolorImage
|
UM.RecolorImage
|
||||||
{
|
{
|
||||||
id: overlayIcon
|
id: overlayIcon
|
||||||
|
anchors.left: button_label.right
|
||||||
|
anchors.leftMargin: (icon.visible || overlayIcon.visible) ? Theme.getSize("default_margin").width : 0
|
||||||
visible: control.overlayIconSource != "" && control.iconSource != ""
|
visible: control.overlayIconSource != "" && control.iconSource != ""
|
||||||
color: control.overlayColor
|
color: control.overlayColor
|
||||||
opacity: !control.enabled ? 0.2 : 1.0
|
opacity: !control.enabled ? 0.2 : 1.0
|
||||||
|
|
|
@ -391,6 +391,11 @@
|
||||||
|
|
||||||
"infill_button_margin": [0.5, 0.5],
|
"infill_button_margin": [0.5, 0.5],
|
||||||
|
|
||||||
"jobspecs_line": [2.0, 2.0]
|
"jobspecs_line": [2.0, 2.0],
|
||||||
|
|
||||||
|
"objects_menu_size": [20, 40],
|
||||||
|
"objects_menu_size_collapsed": [20, 17],
|
||||||
|
"build_plate_selection_size": [15, 5],
|
||||||
|
"objects_menu_button": [0.3, 2.7]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import pytest
|
|
||||||
import numpy
|
import numpy
|
||||||
import time
|
|
||||||
|
|
||||||
from cura.Arrange import Arrange
|
from cura.Arranging.Arrange import Arrange
|
||||||
from cura.ShapeArray import ShapeArray
|
from cura.Arranging.ShapeArray import ShapeArray
|
||||||
|
|
||||||
|
|
||||||
def gimmeShapeArray():
|
def gimmeShapeArray():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue