mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-13 17:57:55 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura into network_rewrite
This commit is contained in:
commit
ed9634ebe0
77 changed files with 2675 additions and 791 deletions
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.Logger import Logger
|
||||
from UM.Math.Vector import Vector
|
||||
from cura.ShapeArray import ShapeArray
|
||||
from cura import ZOffsetDecorator
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
from cura.Scene import ZOffsetDecorator
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
|
@ -30,6 +30,7 @@ class Arrange:
|
|||
self._offset_x = offset_x
|
||||
self._offset_y = offset_y
|
||||
self._last_priority = 0
|
||||
self._is_empty = True
|
||||
|
||||
## Helper to create an Arranger instance
|
||||
#
|
||||
|
@ -38,8 +39,8 @@ class Arrange:
|
|||
# \param scene_root Root for finding all scene nodes
|
||||
# \param fixed_nodes Scene nodes to be placed
|
||||
@classmethod
|
||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5):
|
||||
arranger = Arrange(220, 220, 110, 110, scale = scale)
|
||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 220, y = 220):
|
||||
arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
|
||||
arranger.centerFirst()
|
||||
|
||||
if fixed_nodes is None:
|
||||
|
@ -64,7 +65,7 @@ class Arrange:
|
|||
for area in disallowed_areas:
|
||||
points = copy.deepcopy(area._points)
|
||||
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
|
||||
arranger.place(0, 0, shape_arr)
|
||||
arranger.place(0, 0, shape_arr, update_empty = False)
|
||||
return arranger
|
||||
|
||||
## 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 y y-coordinate
|
||||
# \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)
|
||||
y = int(self._scale * y)
|
||||
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)
|
||||
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
|
||||
occupied_slice[numpy.where(shape_arr.arr[
|
||||
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 1
|
||||
new_occupied = numpy.where(shape_arr.arr[
|
||||
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.
|
||||
prio_slice = self._priority[min_y:max_y, min_x:max_x]
|
||||
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
|
||||
|
||||
@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.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.Logger import Logger
|
||||
|
@ -12,9 +11,9 @@ from UM.Message import Message
|
|||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
||||
from cura.Arrange import Arrange
|
||||
from cura.ShapeArray import ShapeArray
|
||||
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||
from cura.Arranging.Arrange import Arrange
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
|
||||
from typing import List
|
||||
|
6
cura/ShapeArray.py → cura/Arranging/ShapeArray.py
Executable file → Normal file
6
cura/ShapeArray.py → cura/Arranging/ShapeArray.py
Executable file → Normal file
|
@ -29,8 +29,12 @@ class ShapeArray:
|
|||
offset_x = int(numpy.amin(flip_vertices[:, 1]))
|
||||
flip_vertices[:, 0] = numpy.add(flip_vertices[:, 0], -offset_y)
|
||||
flip_vertices[:, 1] = numpy.add(flip_vertices[:, 1], -offset_x)
|
||||
shape = [int(numpy.amax(flip_vertices[:, 0])), int(numpy.amax(flip_vertices[:, 1]))]
|
||||
shape = numpy.array([int(numpy.amax(flip_vertices[:, 0])), int(numpy.amax(flip_vertices[:, 1]))])
|
||||
shape[numpy.where(shape == 0)] = 1
|
||||
arr = cls.arrayFromPolygon(shape, flip_vertices)
|
||||
if not numpy.ndarray.any(arr):
|
||||
# set at least 1 pixel
|
||||
arr[0][0] = 1
|
||||
return cls(arr, offset_x, offset_y)
|
||||
|
||||
## Instantiate an offset and hull ShapeArray from a scene node.
|
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.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
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.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOperation
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
|
||||
class CuraActions(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
@ -54,7 +60,11 @@ class CuraActions(QObject):
|
|||
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
|
||||
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.push()
|
||||
|
||||
|
@ -124,5 +134,31 @@ class CuraActions(QObject):
|
|||
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
|
||||
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):
|
||||
QDesktopServices.openUrl(url)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from PyQt5.QtNetwork import QLocalServer
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
|
@ -16,7 +17,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Mesh.ReadMeshJob import ReadMeshJob
|
||||
from UM.Logger import Logger
|
||||
from UM.Preferences import Preferences
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
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.SetTransformOperation import SetTransformOperation
|
||||
|
||||
from cura.Arrange import Arrange
|
||||
from cura.ShapeArray import ShapeArray
|
||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
||||
from cura.SetParentOperation import SetParentOperation
|
||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
||||
|
||||
from cura.ArrangeObjectsJob import ArrangeObjectsJob
|
||||
from cura.Arranging.Arrange import Arrange
|
||||
from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
|
||||
from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
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.ContainerRegistry import ContainerRegistry
|
||||
|
@ -53,12 +57,13 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
|||
from cura.Settings.UserProfilesModel import UserProfilesModel
|
||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||
|
||||
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
from . import PrintInformation
|
||||
from . import CuraActions
|
||||
from . import ZOffsetDecorator
|
||||
from cura.Scene import ZOffsetDecorator
|
||||
from . import CuraSplashScreen
|
||||
from . import CameraImageProvider
|
||||
from . import MachineActionManager
|
||||
|
@ -72,8 +77,9 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
|
|||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Settings.QualitySettingsModel import QualitySettingsModel
|
||||
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 UM.FlameProfiler import pyqtSlot
|
||||
|
@ -85,7 +91,6 @@ import sys
|
|||
import os.path
|
||||
import numpy
|
||||
import copy
|
||||
import urllib.parse
|
||||
import os
|
||||
import argparse
|
||||
import json
|
||||
|
@ -203,8 +208,11 @@ class CuraApplication(QtApplication):
|
|||
self._machine_manager = None # This is initialized on demand.
|
||||
self._extruder_manager = None
|
||||
self._material_manager = None
|
||||
self._object_manager = None
|
||||
self._build_plate_model = None
|
||||
self._setting_inheritance_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
|
||||
|
||||
|
@ -311,11 +319,14 @@ class CuraApplication(QtApplication):
|
|||
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
||||
preferences.addPreference("cura/choice_on_profile_override", "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/material_settings", "{}")
|
||||
|
||||
preferences.addPreference("view/invert_zoom", 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")
|
||||
|
@ -389,6 +400,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
|
||||
|
||||
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
||||
|
||||
def _onEngineCreated(self):
|
||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
|
||||
|
@ -684,8 +697,16 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
|
||||
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
|
||||
qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
|
||||
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager)
|
||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
||||
|
||||
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager",
|
||||
self.getSettingInheritanceManager)
|
||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager",
|
||||
self.getSimpleModeSettingsManager)
|
||||
|
||||
qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
|
||||
qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
|
||||
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)
|
||||
|
||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||
|
||||
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
||||
|
@ -723,6 +744,22 @@ class CuraApplication(QtApplication):
|
|||
self._material_manager = MaterialManager.createMaterialManager()
|
||||
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):
|
||||
if self._setting_inheritance_manager is None:
|
||||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||
|
@ -858,7 +895,7 @@ class CuraApplication(QtApplication):
|
|||
scene_bounding_box = None
|
||||
is_block_slicing_node = False
|
||||
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
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
is_block_slicing_node = True
|
||||
|
@ -975,7 +1012,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
Selection.clear()
|
||||
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.
|
||||
|
@ -983,6 +1020,9 @@ class CuraApplication(QtApplication):
|
|||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
if not node.isSelectable():
|
||||
continue # i.e. node with layer data
|
||||
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
|
||||
continue # i.e. node with layer data
|
||||
|
||||
Selection.add(node)
|
||||
|
||||
## Delete all nodes containing mesh data in the scene.
|
||||
|
@ -994,7 +1034,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
if type(node) not in {SceneNode, CuraSceneNode}:
|
||||
continue
|
||||
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.
|
||||
|
@ -1010,13 +1050,15 @@ class CuraApplication(QtApplication):
|
|||
op.push()
|
||||
Selection.clear()
|
||||
|
||||
self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
|
||||
|
||||
## Reset all translation on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
def resetAllTranslation(self):
|
||||
Logger.log("i", "Resetting all scene translations")
|
||||
nodes = []
|
||||
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.
|
||||
|
@ -1044,13 +1086,13 @@ class CuraApplication(QtApplication):
|
|||
Logger.log("i", "Resetting all scene transformations")
|
||||
nodes = []
|
||||
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.isSelectable():
|
||||
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
|
||||
continue # i.e. node with layer data
|
||||
nodes.append(node)
|
||||
|
||||
|
@ -1068,10 +1110,31 @@ class CuraApplication(QtApplication):
|
|||
|
||||
## Arrange all objects.
|
||||
@pyqtSlot()
|
||||
def arrangeAll(self):
|
||||
def arrangeObjectsToAllBuildPlates(self):
|
||||
nodes = []
|
||||
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") and not node.callDecoration("isGroup"):
|
||||
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
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
|
@ -1079,6 +1142,9 @@ class CuraApplication(QtApplication):
|
|||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
if not node.isSelectable():
|
||||
continue # i.e. node with layer data
|
||||
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
|
||||
continue # i.e. node with layer data
|
||||
if node.callDecoration("getBuildPlateNumber") == active_build_plate:
|
||||
# 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)
|
||||
|
@ -1092,7 +1158,7 @@ class CuraApplication(QtApplication):
|
|||
# What nodes are on the build plate and are not being moved
|
||||
fixed_nodes = []
|
||||
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.
|
||||
|
@ -1100,6 +1166,8 @@ class CuraApplication(QtApplication):
|
|||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
if not node.isSelectable():
|
||||
continue # i.e. node with layer data
|
||||
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
|
||||
continue # i.e. node with layer data
|
||||
if node in nodes: # exclude selected node from fixed_nodes
|
||||
continue
|
||||
fixed_nodes.append(node)
|
||||
|
@ -1118,7 +1186,7 @@ class CuraApplication(QtApplication):
|
|||
Logger.log("i", "Reloading all loaded mesh data.")
|
||||
nodes = []
|
||||
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
|
||||
|
||||
nodes.append(node)
|
||||
|
@ -1209,10 +1277,11 @@ class CuraApplication(QtApplication):
|
|||
@pyqtSlot()
|
||||
def groupSelected(self):
|
||||
# Create a group-node
|
||||
group_node = SceneNode()
|
||||
group_node = CuraSceneNode()
|
||||
group_decorator = GroupDecorator()
|
||||
group_node.addDecorator(group_decorator)
|
||||
group_node.addDecorator(ConvexHullDecorator())
|
||||
group_node.addDecorator(BuildPlateDecorator(self.getBuildPlateModel().activeBuildPlate))
|
||||
group_node.setParent(self.getController().getScene().getRoot())
|
||||
group_node.setSelectable(True)
|
||||
center = Selection.getSelectionCenter()
|
||||
|
@ -1357,8 +1426,15 @@ class CuraApplication(QtApplication):
|
|||
min_offset = 8
|
||||
|
||||
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.setName(os.path.basename(filename))
|
||||
|
||||
|
@ -1385,6 +1461,7 @@ class CuraApplication(QtApplication):
|
|||
if not child.getDecorator(ConvexHullDecorator):
|
||||
child.addDecorator(ConvexHullDecorator())
|
||||
|
||||
if arrange_objects_on_load:
|
||||
if node.callDecoration("isSliceable"):
|
||||
# Only check position if it's not already blatantly obvious that it won't fit.
|
||||
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:
|
||||
|
@ -1394,13 +1471,14 @@ class CuraApplication(QtApplication):
|
|||
# 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:
|
||||
Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
|
||||
title=self._i18n_catalog.i18nc("@info:title", "Warning")
|
||||
).show()
|
||||
title=self._i18n_catalog.i18nc("@info:title", "Warning")).show()
|
||||
return
|
||||
|
||||
# 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.addDecorator(BuildPlateDecorator(target_build_plate))
|
||||
|
||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||
op.push()
|
||||
scene.sceneChanged.emit(node)
|
||||
|
|
|
@ -2,24 +2,15 @@
|
|||
# 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.SetTransformOperation import SetTransformOperation
|
||||
from UM.Operations.TranslateOperation import TranslateOperation
|
||||
from UM.Operations.GroupedOperation import GroupedOperation
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
||||
from cura.Arrange import Arrange
|
||||
from cura.ShapeArray import ShapeArray
|
||||
|
||||
from typing import List
|
||||
from cura.Arranging.Arrange import Arrange
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
|
||||
|
||||
|
@ -65,6 +56,10 @@ class MultiplyObjectsJob(Job):
|
|||
new_location = new_location.set(z = 100 - i * 20)
|
||||
node.setPosition(new_location)
|
||||
|
||||
# Same build plate
|
||||
build_plate_number = current_node.callDecoration("getBuildPlateNumber")
|
||||
node.callDecoration("setBuildPlateNumber", build_plate_number)
|
||||
|
||||
nodes.append(node)
|
||||
current_progress += 1
|
||||
status_message.setProgress((current_progress / total_progress) * 100)
|
||||
|
|
63
cura/ObjectsModel.py
Normal file
63
cura/ObjectsModel.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
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
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## 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
|
||||
group_nr = 1
|
||||
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
|
||||
if not issubclass(type(node), SceneNode):
|
||||
continue
|
||||
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
||||
continue
|
||||
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") and not node.callDecoration("isGroup"):
|
||||
continue
|
||||
node_build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||
if filter_current_build_plate and node_build_plate_number != active_build_plate_number:
|
||||
continue
|
||||
|
||||
if not node.callDecoration("isGroup"):
|
||||
name = node.getName()
|
||||
else:
|
||||
name = catalog.i18nc("@label", "Group #{group_nr}").format(group_nr = str(group_nr))
|
||||
group_nr += 1
|
||||
|
||||
nodes.append({
|
||||
"name": name,
|
||||
"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()
|
29
cura/Operations/SetBuildPlateNumberOperation.py
Normal file
29
cura/Operations/SetBuildPlateNumberOperation.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# 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:
|
||||
super().__init__()
|
||||
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.Preferences import Preferences
|
||||
|
||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
||||
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||
|
||||
from . import PlatformPhysicsOperation
|
||||
from . import ZOffsetDecorator
|
||||
from cura.Operations import PlatformPhysicsOperation
|
||||
from cura.Scene import ZOffsetDecorator
|
||||
|
||||
import random # used for list shuffling
|
||||
|
||||
|
@ -34,7 +34,7 @@ class PlatformPhysics:
|
|||
self._change_timer.timeout.connect(self._onChangeTimerFinished)
|
||||
self._move_factor = 1.1 # By how much should we multiply overlap to calculate a new spot?
|
||||
self._max_overlap_checks = 10 # How many times should we try to find a new spot per tick?
|
||||
self._minimum_gap = 2 # It is a minimum distance between two models, applicable for small models
|
||||
self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models
|
||||
|
||||
Preferences.getInstance().addPreference("physics/automatic_push_free", True)
|
||||
Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
|
||||
|
@ -42,7 +42,7 @@ class PlatformPhysics:
|
|||
def _onSceneChanged(self, source):
|
||||
self._change_timer.start()
|
||||
|
||||
def _onChangeTimerFinished(self, was_triggered_by_tool=False):
|
||||
def _onChangeTimerFinished(self):
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
|
@ -61,7 +61,7 @@ class PlatformPhysics:
|
|||
|
||||
random.shuffle(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
|
||||
|
||||
bbox = node.getBoundingBox()
|
||||
|
@ -71,7 +71,7 @@ class PlatformPhysics:
|
|||
|
||||
if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||
move_vector = move_vector.set(y=-bbox.bottom + z_offset)
|
||||
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
||||
|
||||
# If there is no convex hull for the node, start calculating it and continue.
|
||||
if not node.getDecorator(ConvexHullDecorator):
|
||||
|
@ -82,7 +82,7 @@ class PlatformPhysics:
|
|||
# Check for collisions between convex hulls
|
||||
for other_node in BreadthFirstIterator(root):
|
||||
# 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
|
||||
|
||||
# Ignore collisions of a group with it's own children
|
||||
|
@ -134,13 +134,10 @@ class PlatformPhysics:
|
|||
|
||||
# if the distance between two models less than 2mm then try to find a new factor
|
||||
if abs(temp_move_vector.x - overlap[0]) < self._minimum_gap and abs(temp_move_vector.y - overlap[1]) < self._minimum_gap:
|
||||
temp_scale_factor = self._move_factor
|
||||
temp_x_factor = (abs(overlap[0]) + self._minimum_gap) / overlap[0] if overlap[0] != 0 else 0 # find x move_factor, like (3.4 + 2) / 3.4 = 1.58
|
||||
temp_y_factor = (abs(overlap[1]) + self._minimum_gap) / overlap[1] if overlap[1] != 0 else 0 # find y move_factor
|
||||
if abs(temp_x_factor) > abs(temp_y_factor):
|
||||
temp_scale_factor = temp_x_factor
|
||||
else:
|
||||
temp_scale_factor = temp_y_factor
|
||||
|
||||
temp_scale_factor = temp_x_factor if abs(temp_x_factor) > abs(temp_y_factor) else temp_y_factor
|
||||
|
||||
move_vector = move_vector.set(x = move_vector.x + overlap[0] * temp_scale_factor,
|
||||
z = move_vector.z + overlap[1] * temp_scale_factor)
|
||||
|
@ -180,4 +177,4 @@ class PlatformPhysics:
|
|||
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
||||
|
||||
self._enabled = True
|
||||
self._onChangeTimerFinished(True)
|
||||
self._onChangeTimerFinished()
|
||||
|
|
|
@ -54,10 +54,10 @@ class PrintInformation(QObject):
|
|||
|
||||
self.initializeCuraMessagePrintTimeProperties()
|
||||
|
||||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
self._material_costs = []
|
||||
self._material_names = []
|
||||
self._material_lengths = {} # indexed by build plate number
|
||||
self._material_weights = {}
|
||||
self._material_costs = {}
|
||||
self._material_names = {}
|
||||
|
||||
self._pre_sliced = False
|
||||
|
||||
|
@ -68,10 +68,15 @@ class PrintInformation(QObject):
|
|||
self._base_name = ""
|
||||
self._abbr_machine = ""
|
||||
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().fileLoaded.connect(self.setBaseName)
|
||||
Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
||||
Application.getInstance().workspaceLoaded.connect(self.setProjectName)
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
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
|
||||
# and time for each feature
|
||||
def initializeCuraMessagePrintTimeProperties(self):
|
||||
self._current_print_time = Duration(None, self)
|
||||
self._current_print_time = {} # Duration(None, self)
|
||||
|
||||
self._print_time_message_translations = {
|
||||
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
||||
|
@ -101,10 +106,26 @@ class PrintInformation(QObject):
|
|||
|
||||
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()
|
||||
|
||||
|
@ -120,64 +141,71 @@ class PrintInformation(QObject):
|
|||
|
||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
return self._current_print_time[self._active_build_plate]
|
||||
|
||||
materialLengthsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialLengthsChanged)
|
||||
def materialLengths(self):
|
||||
return self._material_lengths
|
||||
return self._material_lengths[self._active_build_plate]
|
||||
|
||||
materialWeightsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialWeightsChanged)
|
||||
def materialWeights(self):
|
||||
return self._material_weights
|
||||
return self._material_weights[self._active_build_plate]
|
||||
|
||||
materialCostsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialCostsChanged)
|
||||
def materialCosts(self):
|
||||
return self._material_costs
|
||||
return self._material_costs[self._active_build_plate]
|
||||
|
||||
materialNamesChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialNamesChanged)
|
||||
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._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
|
||||
|
||||
if build_plate_number not in self._print_time_message_values:
|
||||
self._initPrintTimeMessageValues(build_plate_number)
|
||||
|
||||
for feature, time in print_time.items():
|
||||
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")
|
||||
continue
|
||||
|
||||
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:
|
||||
return
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
||||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
self._material_costs = []
|
||||
self._material_names = []
|
||||
self._material_lengths[build_plate_number] = []
|
||||
self._material_weights[build_plate_number] = []
|
||||
self._material_costs[build_plate_number] = []
|
||||
self._material_names[build_plate_number] = []
|
||||
|
||||
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)
|
||||
else:
|
||||
length = 0
|
||||
self._material_weights.append(weight)
|
||||
self._material_lengths.append(length)
|
||||
self._material_costs.append(cost)
|
||||
self._material_names.append(material_name)
|
||||
self._material_weights[build_plate_number].append(weight)
|
||||
self._material_lengths[build_plate_number].append(length)
|
||||
self._material_costs[build_plate_number].append(cost)
|
||||
self._material_names[build_plate_number].append(material_name)
|
||||
|
||||
self.materialLengthsChanged.emit()
|
||||
self.materialWeightsChanged.emit()
|
||||
|
@ -229,7 +257,8 @@ class PrintInformation(QObject):
|
|||
if preference != "cura/material_settings":
|
||||
return
|
||||
|
||||
self._calculateInformation()
|
||||
for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
|
||||
self._calculateInformation(build_plate_number)
|
||||
|
||||
def _onActiveMaterialChanged(self):
|
||||
if self._active_material_container:
|
||||
|
@ -245,8 +274,22 @@ class PrintInformation(QObject):
|
|||
self._active_material_container = active_material_containers[0]
|
||||
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):
|
||||
self._calculateInformation()
|
||||
for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
|
||||
self._calculateInformation(build_plate_number)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
|
@ -340,7 +383,9 @@ class PrintInformation(QObject):
|
|||
@pyqtSlot(result = "QVariantMap")
|
||||
def getFeaturePrintTimes(self):
|
||||
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:
|
||||
result[self._print_time_message_translations[feature]] = time
|
||||
else:
|
||||
|
@ -348,10 +393,12 @@ class PrintInformation(QObject):
|
|||
return result
|
||||
|
||||
# Simulate message with zero time duration
|
||||
def setToZeroPrintInformation(self):
|
||||
def setToZeroPrintInformation(self, build_plate_number):
|
||||
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_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 cura.Settings.ExtruderManager import ExtruderManager
|
||||
from . import ConvexHullNode
|
||||
from cura.Scene import ConvexHullNode
|
||||
|
||||
import numpy
|
||||
|
|
@ -6,7 +6,6 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Resources import Resources
|
||||
from UM.Math.Color import Color
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
|
||||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
|
||||
|
@ -66,7 +65,7 @@ class ConvexHullNode(SceneNode):
|
|||
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
||||
|
||||
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)
|
||||
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)
|
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
|
@ -270,7 +270,7 @@ class ExtruderManager(QObject):
|
|||
return []
|
||||
|
||||
# Get the extruders of all printable meshes in the scene
|
||||
meshes = [node for node in DepthFirstIterator(scene_root) if type(node) is SceneNode and node.isSelectable()]
|
||||
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()]
|
||||
for mesh in meshes:
|
||||
extruder_stack_id = mesh.callDecoration("getActiveExtruder")
|
||||
if not extruder_stack_id:
|
||||
|
|
|
@ -40,7 +40,7 @@ import faulthandler
|
|||
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||
linux_distro_name = platform.linux_distribution()[0].lower()
|
||||
if linux_distro_name in ("debian", "ubuntu", "linuxmint", "fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
# TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
import ctypes
|
||||
from ctypes.util import find_library
|
||||
libGL = find_library("GL")
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import os.path
|
||||
import zipfile
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
|
@ -15,9 +14,10 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
|||
from UM.Application import Application
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.QualityManager import QualityManager
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||
|
||||
MYPY = False
|
||||
|
||||
|
@ -43,6 +43,7 @@ class ThreeMFReader(MeshReader):
|
|||
}
|
||||
self._base_name = ""
|
||||
self._unit = None
|
||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||
|
||||
def _createMatrixFromTransformationString(self, 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.
|
||||
# \returns Uranium scene 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())
|
||||
um_node.setTransformation(transformation)
|
||||
mesh_builder = MeshBuilder()
|
||||
|
@ -147,6 +153,7 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
def read(self, file_name):
|
||||
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.
|
||||
try:
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
|
|
|
@ -7,6 +7,7 @@ from UM.Logger import Logger
|
|||
from UM.Math.Matrix import Matrix
|
||||
from UM.Application import Application
|
||||
import UM.Scene.SceneNode
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
import Savitar
|
||||
|
||||
|
@ -63,7 +64,7 @@ class ThreeMFWriter(MeshWriter):
|
|||
## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
|
||||
# \returns Uranium Scenen node.
|
||||
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
|
||||
|
||||
savitar_node = Savitar.SceneNode()
|
||||
|
|
|
@ -16,6 +16,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Qt.Duration import DurationFormat
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
from collections import defaultdict
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from . import ProcessSlicedLayersJob
|
||||
from . import StartSliceJob
|
||||
|
@ -69,9 +70,10 @@ class CuraEngineBackend(QObject, Backend):
|
|||
# Workaround to disable layer view processing if layer view is not active.
|
||||
self._layer_view_active = False
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
self._onActiveViewChanged()
|
||||
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.sceneChanged.connect(self._onSceneChanged)
|
||||
|
@ -105,17 +107,18 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._start_slice_job = None
|
||||
self._start_slice_job_build_plate = None
|
||||
self._slicing = False # Are we currently slicing?
|
||||
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._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._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._backend_log_max_lines = 20000 # Maximum number of lines to buffer
|
||||
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.backendQuit.connect(self._onBackendQuit)
|
||||
|
@ -174,6 +177,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._createSocket()
|
||||
|
||||
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 = None
|
||||
|
||||
|
@ -190,17 +194,35 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
Logger.log("d", "starting to slice!")
|
||||
self._slice_start_time = time()
|
||||
if not self._need_slicing:
|
||||
if not self._build_plates_to_be_sliced:
|
||||
self.processingProgress.emit(1.0)
|
||||
self.backendStateChange.emit(BackendState.Done)
|
||||
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
|
||||
return
|
||||
if Application.getInstance().getPrintInformation():
|
||||
Application.getInstance().getPrintInformation().setToZeroPrintInformation()
|
||||
|
||||
if self._process_layers_job:
|
||||
Logger.log("d", " ## Process layers job still busy, trying later")
|
||||
return
|
||||
|
||||
if not hasattr(self._scene, "gcode_list"):
|
||||
self._scene.gcode_list = {}
|
||||
|
||||
# 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:
|
||||
self._scene.gcode_list[build_plate_to_be_sliced] = []
|
||||
Logger.log("d", "Build plate %s has 0 objects to be sliced, skipping", build_plate_to_be_sliced)
|
||||
return
|
||||
|
||||
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:
|
||||
self._createSocket()
|
||||
|
@ -210,12 +232,14 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.processingProgress.emit(0.0)
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
|
||||
self._scene.gcode_list = []
|
||||
self._scene.gcode_list[build_plate_to_be_sliced] = [] #[] indexed by build plate number
|
||||
self._slicing = True
|
||||
self.slicingStarted.emit()
|
||||
|
||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||
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.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
|
@ -224,7 +248,8 @@ class CuraEngineBackend(QObject, Backend):
|
|||
def _terminate(self):
|
||||
self._slicing = False
|
||||
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:
|
||||
self._start_slice_job.cancel()
|
||||
|
||||
|
@ -339,7 +364,10 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.backendStateChange.emit(BackendState.Error)
|
||||
else:
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
pass
|
||||
self._invokeSlice()
|
||||
return
|
||||
|
||||
# Preparation completed, send it to the backend.
|
||||
self._socket.sendMessage(job.getSliceMessage())
|
||||
|
||||
|
@ -363,7 +391,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.backendStateChange.emit(BackendState.Disabled)
|
||||
gcode_list = node.callDecoration("getGCodeList")
|
||||
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:
|
||||
return self._use_timer
|
||||
|
@ -375,43 +403,66 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.disableTimer()
|
||||
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.
|
||||
#
|
||||
# This should start a slice if the scene is now ready to slice.
|
||||
#
|
||||
# \param source The scene node that was changed.
|
||||
def _onSceneChanged(self, source):
|
||||
if type(source) is not SceneNode:
|
||||
if not issubclass(type(source), SceneNode):
|
||||
return
|
||||
|
||||
root_scene_nodes_changed = False
|
||||
build_plate_changed = set()
|
||||
source_build_plate_number = source.callDecoration("getBuildPlateNumber")
|
||||
if source == self._scene.getRoot():
|
||||
num_objects = 0
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
# Only count sliceable objects
|
||||
if node.callDecoration("isSliceable"):
|
||||
num_objects += 1
|
||||
if num_objects != self._last_num_objects:
|
||||
self._last_num_objects = num_objects
|
||||
root_scene_nodes_changed = True
|
||||
# we got the root node
|
||||
num_objects = self._numObjects()
|
||||
for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()):
|
||||
if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]:
|
||||
self._last_num_objects[build_plate_number] = num_objects[build_plate_number]
|
||||
build_plate_changed.add(build_plate_number)
|
||||
else:
|
||||
return
|
||||
|
||||
if not source.callDecoration("isGroup") and not root_scene_nodes_changed:
|
||||
# we got a single scenenode
|
||||
if not source.callDecoration("isGroup"):
|
||||
if source.getMeshData() is None:
|
||||
return
|
||||
if source.getMeshData().getVertices() is None:
|
||||
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:
|
||||
# do it later, each source only has to be done once
|
||||
if source not in self._postponed_scene_change_sources:
|
||||
self._postponed_scene_change_sources.append(source)
|
||||
return
|
||||
|
||||
self.needsSlicing()
|
||||
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.
|
||||
#
|
||||
|
@ -431,16 +482,21 @@ class CuraEngineBackend(QObject, Backend):
|
|||
Logger.log("w", "A socket error caused the connection to be reset")
|
||||
|
||||
## Remove old layer data (if any)
|
||||
def _clearLayerData(self):
|
||||
def _clearLayerData(self, build_plate_numbers = set()):
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("getLayerData"):
|
||||
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:
|
||||
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):
|
||||
self.stopSlicing()
|
||||
self._need_slicing = True
|
||||
self.markSliceAll()
|
||||
self.processingProgress.emit(0.0)
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
if not self._use_timer:
|
||||
|
@ -462,7 +518,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
def _onStackErrorCheckFinished(self):
|
||||
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._onChanged()
|
||||
|
||||
|
@ -476,7 +532,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
#
|
||||
# \param message The protobuf message containing sliced layer data.
|
||||
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.
|
||||
#
|
||||
|
@ -485,6 +541,16 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.processingProgress.emit(message.amount)
|
||||
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.
|
||||
#
|
||||
# \param message The protobuf message signalling that slicing is finished.
|
||||
|
@ -492,36 +558,44 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.backendStateChange.emit(BackendState.Done)
|
||||
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 = replaced.replace("{filament_amount}", str(Application.getInstance().getPrintInformation().materialLengths))
|
||||
replaced = replaced.replace("{filament_weight}", str(Application.getInstance().getPrintInformation().materialWeights))
|
||||
replaced = replaced.replace("{filament_cost}", str(Application.getInstance().getPrintInformation().materialCosts))
|
||||
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._need_slicing = False
|
||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
||||
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
|
||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
|
||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
||||
self._process_layers_job.start()
|
||||
self._stored_optimized_layer_data = []
|
||||
|
||||
# See if we need to process the sliced layers job.
|
||||
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||
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._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.
|
||||
#
|
||||
# \param message The protobuf message containing g-code, encoded as UTF-8.
|
||||
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.
|
||||
#
|
||||
# \param message The protobuf message containing the g-code prefix,
|
||||
# encoded as UTF-8.
|
||||
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.
|
||||
def _createSocket(self):
|
||||
|
@ -551,7 +625,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
||||
|
||||
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
|
||||
#
|
||||
|
@ -605,19 +679,25 @@ class CuraEngineBackend(QObject, Backend):
|
|||
source = self._postponed_scene_change_sources.pop(0)
|
||||
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.
|
||||
def _onActiveViewChanged(self):
|
||||
if Application.getInstance().getController().getActiveView():
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
application = Application.getInstance()
|
||||
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.
|
||||
self._layer_view_active = True
|
||||
# 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 self._stored_optimized_layer_data and not self._slicing:
|
||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
|
||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
||||
self._process_layers_job.start()
|
||||
self._stored_optimized_layer_data = []
|
||||
# TODO: what build plate I am slicing
|
||||
if active_build_plate in self._stored_optimized_layer_data and not self._slicing and not self._process_layers_job:
|
||||
self._startProcessSlicedLayersJob(active_build_plate)
|
||||
else:
|
||||
self._layer_view_active = False
|
||||
|
||||
|
@ -653,7 +733,10 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._onChanged()
|
||||
|
||||
def _onProcessLayersFinished(self, job):
|
||||
del self._stored_optimized_layer_data[job.getBuildPlate()]
|
||||
self._process_layers_job = None
|
||||
Logger.log("d", "See if there is more to slice(2)...")
|
||||
self._invokeSlice()
|
||||
|
||||
## Connect slice function to timer.
|
||||
def enableTimer(self):
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import gc
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Application import Application
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
|
@ -17,6 +16,7 @@ from UM.Logger import Logger
|
|||
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura import LayerDataBuilder
|
||||
from cura import LayerDataDecorator
|
||||
|
@ -49,6 +49,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
||||
self._abort_requested = False
|
||||
self._build_plate_number = None
|
||||
|
||||
## Aborts the processing of layers.
|
||||
#
|
||||
|
@ -59,7 +60,14 @@ class ProcessSlicedLayersJob(Job):
|
|||
def abort(self):
|
||||
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):
|
||||
Logger.log("d", "Processing new layer for build plate %s..." % self._build_plate_number)
|
||||
start_time = time()
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "SimulationView":
|
||||
|
@ -74,16 +82,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
new_node = SceneNode()
|
||||
|
||||
## 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
|
||||
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
|
||||
|
||||
# Force garbage collection.
|
||||
# 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.Logger import Logger
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Settings.SettingRelation import RelationType
|
||||
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
|
@ -36,9 +36,32 @@ class StartJobResult(IntEnum):
|
|||
## Formatter class that handles token expansion in start/end gcod
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
# 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):
|
||||
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:
|
||||
Logger.log("w", "Unable to replace '%s' placeholder in start/end gcode", key)
|
||||
return "{" + key + "}"
|
||||
|
@ -55,10 +78,16 @@ class StartSliceJob(Job):
|
|||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._slice_message = slice_message
|
||||
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):
|
||||
return self._slice_message
|
||||
|
||||
def setBuildPlate(self, build_plate_number):
|
||||
self._build_plate_number = build_plate_number
|
||||
|
||||
## Check if a stack has any errors.
|
||||
## returns true if it has errors, false otherwise.
|
||||
def _checkStackForErrors(self, stack):
|
||||
|
@ -75,6 +104,10 @@ class StartSliceJob(Job):
|
|||
|
||||
## Runs the job that initiates the slicing.
|
||||
def run(self):
|
||||
if self._build_plate_number is None:
|
||||
self.setResult(StartJobResult.Error)
|
||||
return
|
||||
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not stack:
|
||||
self.setResult(StartJobResult.Error)
|
||||
|
@ -108,7 +141,7 @@ class StartSliceJob(Job):
|
|||
with self._scene.getSceneLock():
|
||||
# Remove old layer data.
|
||||
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)
|
||||
break
|
||||
|
||||
|
@ -143,6 +176,7 @@ class StartSliceJob(Job):
|
|||
if per_object_stack:
|
||||
is_non_printing_mesh = any(per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS)
|
||||
|
||||
if (node.callDecoration("getBuildPlateNumber") == self._build_plate_number):
|
||||
if not getattr(node, "_outside_buildarea", False) or is_non_printing_mesh:
|
||||
temp_list.append(node)
|
||||
if not is_non_printing_mesh:
|
||||
|
@ -233,16 +267,33 @@ class StartSliceJob(Job):
|
|||
result["date"] = time.strftime("%d-%m-%Y")
|
||||
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
|
||||
|
||||
## Replace setting tokens in a piece of g-code.
|
||||
# \param value A piece of g-code to replace tokens in.
|
||||
# \param settings A dictionary of tokens to replace and their respective
|
||||
# replacement strings.
|
||||
def _expandGcodeTokens(self, value: str, settings: dict):
|
||||
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
||||
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1):
|
||||
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:
|
||||
# any setting can be used as a token
|
||||
fmt = GcodeStartEndFormatter()
|
||||
settings = self._all_extruders_settings.copy()
|
||||
settings["default_extruder_nr"] = default_extruder_nr
|
||||
return str(fmt.format(value, **settings))
|
||||
except:
|
||||
Logger.logException("w", "Unable to do token replacement on start/end gcode")
|
||||
|
@ -259,8 +310,9 @@ class StartSliceJob(Job):
|
|||
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
|
||||
|
||||
# Replace the setting tokens in start and end g-code.
|
||||
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], settings)
|
||||
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], settings)
|
||||
extruder_nr = stack.getProperty("extruder_nr", "value")
|
||||
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():
|
||||
# Do not send settings that are not settable_per_extruder.
|
||||
|
@ -285,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"}
|
||||
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.
|
||||
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], extruder_0_settings)
|
||||
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], extruder_0_settings)
|
||||
# Use values from the first used extruder by default so we get the expected temperatures
|
||||
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.
|
||||
for key, value in settings.items():
|
||||
|
|
|
@ -17,7 +17,7 @@ catalog = i18nCatalog("cura")
|
|||
from cura import LayerDataBuilder
|
||||
from cura import LayerDataDecorator
|
||||
from cura.LayerPolygon import LayerPolygon
|
||||
from cura.GCodeListDecorator import GCodeListDecorator
|
||||
from cura.Scene.GCodeListDecorator import GCodeListDecorator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
import numpy
|
||||
|
|
|
@ -59,8 +59,9 @@ class GCodeWriter(MeshWriter):
|
|||
Logger.log("e", "GCode Writer does not support non-text mode.")
|
||||
return False
|
||||
|
||||
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
|
||||
scene = Application.getInstance().getController().getScene()
|
||||
gcode_list = getattr(scene, "gcode_list")
|
||||
gcode_list = getattr(scene, "gcode_list")[active_build_plate]
|
||||
if gcode_list:
|
||||
for gcode in gcode_list:
|
||||
stream.write(gcode)
|
||||
|
|
|
@ -8,12 +8,13 @@ from PyQt5.QtCore import Qt
|
|||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from .ImageReaderUI import ImageReaderUI
|
||||
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||
|
||||
|
||||
class ImageReader(MeshReader):
|
||||
def __init__(self):
|
||||
|
|
|
@ -27,7 +27,9 @@ class MachineSettingsAction(MachineAction):
|
|||
self._qml_url = "MachineSettingsAction.qml"
|
||||
|
||||
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.containerAdded.connect(self._onContainerAdded)
|
||||
|
@ -241,6 +243,7 @@ class MachineSettingsAction(MachineAction):
|
|||
"type": "material",
|
||||
"approximate_diameter": machine_approximate_diameter,
|
||||
"material": old_material.getMetaDataEntry("material", "value"),
|
||||
"brand": old_material.getMetaDataEntry("brand", "value"),
|
||||
"supplier": old_material.getMetaDataEntry("supplier", "value"),
|
||||
"color_name": old_material.getMetaDataEntry("color_name", "value"),
|
||||
"definition": materials_definition
|
||||
|
@ -251,6 +254,7 @@ class MachineSettingsAction(MachineAction):
|
|||
if old_material == self._empty_container:
|
||||
search_criteria.pop("material", None)
|
||||
search_criteria.pop("supplier", None)
|
||||
search_criteria.pop("brand", None)
|
||||
search_criteria.pop("definition", None)
|
||||
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
||||
|
||||
|
@ -258,6 +262,7 @@ class MachineSettingsAction(MachineAction):
|
|||
if not materials:
|
||||
# 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("brand", None)
|
||||
search_criteria["color_name"] = "Generic"
|
||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
||||
if not materials:
|
||||
|
@ -274,6 +279,6 @@ class MachineSettingsAction(MachineAction):
|
|||
# Just use empty material as a final fallback
|
||||
materials = [self._empty_container]
|
||||
|
||||
Logger.log("i", "Selecting new material: %s" % materials[0].getId())
|
||||
Logger.log("i", "Selecting new material: %s", materials[0].getId())
|
||||
|
||||
extruder_stack.material = materials[0]
|
||||
|
|
|
@ -393,7 +393,7 @@ Cura.MachineAction
|
|||
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.")
|
||||
property var afterOnEditingFinished:
|
||||
function afterOnEditingFinished()
|
||||
{
|
||||
if (settingsTabs.currentIndex > 0)
|
||||
{
|
||||
|
|
|
@ -111,7 +111,7 @@ Item {
|
|||
ScrollView
|
||||
{
|
||||
height: parent.height
|
||||
width: UM.Theme.getSize("setting").width
|
||||
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("default_margin").width
|
||||
style: UM.Theme.styles.scrollview
|
||||
|
||||
ListView
|
||||
|
|
|
@ -93,6 +93,7 @@ class SimulationPass(RenderPass):
|
|||
self.bind()
|
||||
|
||||
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
|
||||
nozzle_node = None
|
||||
|
||||
|
@ -105,7 +106,7 @@ class SimulationPass(RenderPass):
|
|||
nozzle_node = node
|
||||
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")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
|
|
@ -25,7 +25,7 @@ from UM.View.GL.OpenGL import OpenGL
|
|||
from UM.View.GL.OpenGLContext import OpenGLContext
|
||||
from UM.View.View import View
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura.ConvexHullNode import ConvexHullNode
|
||||
from cura.Scene.ConvexHullNode import ConvexHullNode
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
from .NozzleNode import NozzleNode
|
||||
|
|
|
@ -145,35 +145,42 @@ geometry41core =
|
|||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||
//And reverse so that the line is also visible from the back side.
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + 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_horz + 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_horz + 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_horz_head + 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_horz + g_vertex_offset_vert));
|
||||
|
||||
EndPrimitive();
|
||||
} else {
|
||||
// All normal lines are rendered as 3d tubes.
|
||||
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[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].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[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
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[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].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[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].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[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||
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[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].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[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||
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[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||
|
||||
EndPrimitive();
|
||||
|
||||
// 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_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();
|
||||
|
||||
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_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();
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ class SliceInfo(Extension):
|
|||
|
||||
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)),
|
||||
"support": int(print_times["support"].getDisplayString(DurationFormat.Format.Seconds)),
|
||||
"infill": int(print_times["infill"].getDisplayString(DurationFormat.Format.Seconds)),
|
||||
|
|
|
@ -11,7 +11,7 @@ from UM.Math.Matrix import Matrix
|
|||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||
|
||||
MYPY = False
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1853,6 +1853,14 @@
|
|||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"infill_enable_travel_optimization":
|
||||
{
|
||||
"label": "Enable Travel Optimization",
|
||||
"description": "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3475,13 +3483,22 @@
|
|||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"support_tree_enable":
|
||||
{
|
||||
"label": "Tree Support",
|
||||
"description": "Generate a tree-like support with branches that support your print. This may reduce material usage and print time.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"support_extruder_nr":
|
||||
{
|
||||
"label": "Support Extruder",
|
||||
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children": {
|
||||
|
@ -3492,7 +3509,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3503,7 +3520,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3514,7 +3531,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children":
|
||||
|
@ -3526,7 +3543,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_interface_extruder_nr",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3537,7 +3554,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_interface_extruder_nr",
|
||||
"enabled": "support_enable and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
|
@ -3557,7 +3574,7 @@
|
|||
},
|
||||
"default_value": "everywhere",
|
||||
"resolve": "'everywhere' if 'everywhere' in extruderValues('support_type') else 'buildplate'",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3572,7 +3589,7 @@
|
|||
"maximum_value_warning": "80",
|
||||
"default_value": 50,
|
||||
"limit_to_extruder": "support_roof_extruder_nr if support_roof_enable else support_infill_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_pattern":
|
||||
|
@ -3591,7 +3608,7 @@
|
|||
"cross": "Cross"
|
||||
},
|
||||
"default_value": "zigzag",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
|
@ -3602,7 +3619,7 @@
|
|||
"description": "Connect the ZigZags. This will increase the strength of the zig zag support structure.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "support_enable and (support_pattern == 'zigzag')",
|
||||
"enabled": "(support_enable or support_tree_enable) and support_pattern == 'zigzag'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
|
@ -3616,7 +3633,8 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value_warning": "100",
|
||||
"default_value": 15,
|
||||
"enabled": "support_enable",
|
||||
"value": "15 if support_enable else 0",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
|
@ -3631,7 +3649,7 @@
|
|||
"minimum_value": "0",
|
||||
"minimum_value_warning": "support_line_width",
|
||||
"default_value": 2.66,
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"value": "0 if support_infill_rate == 0 else (support_line_width * 100) / support_infill_rate * (2 if support_pattern == 'grid' else (3 if support_pattern == 'triangles' else 1))",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -3649,7 +3667,7 @@
|
|||
"maximum_value_warning": "machine_nozzle_size",
|
||||
"default_value": 0.1,
|
||||
"limit_to_extruder": "support_interface_extruder_nr if support_interface_enable else support_infill_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
{
|
||||
|
@ -3662,7 +3680,7 @@
|
|||
"maximum_value_warning": "machine_nozzle_size",
|
||||
"default_value": 0.1,
|
||||
"type": "float",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"value": "extruderValue(support_roof_extruder_nr if support_roof_enable else support_infill_extruder_nr, 'support_z_distance')",
|
||||
"limit_to_extruder": "support_roof_extruder_nr if support_roof_enable else support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
|
@ -3678,7 +3696,7 @@
|
|||
"value": "extruderValue(support_bottom_extruder_nr if support_bottom_enable else support_infill_extruder_nr, 'support_z_distance') if support_type == 'everywhere' else 0",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr if support_bottom_enable else support_infill_extruder_nr",
|
||||
"type": "float",
|
||||
"enabled": "support_enable and resolveOrValue('support_type') == 'everywhere'",
|
||||
"enabled": "(support_enable or support_tree_enable) and resolveOrValue('support_type') == 'everywhere'",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
|
@ -3693,7 +3711,7 @@
|
|||
"maximum_value_warning": "1.5 * machine_nozzle_tip_outer_diameter",
|
||||
"default_value": 0.7,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_xy_overrides_z":
|
||||
|
@ -3788,10 +3806,114 @@
|
|||
"maximum_value_warning": "0.75 * machine_nozzle_size",
|
||||
"maximum_value": "resolveOrValue('layer_height') * 8",
|
||||
"value": "resolveOrValue('layer_height')",
|
||||
"enabled": "support_enable and support_infill_rate > 0",
|
||||
"enabled": "(support_enable or support_tree_enable) and support_infill_rate > 0",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
"support_tree_angle":
|
||||
{
|
||||
"label": "Tree Support Branch Angle",
|
||||
"description": "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "90",
|
||||
"maximum_value_warning": "60",
|
||||
"default_value": 40,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_branch_distance":
|
||||
{
|
||||
"label": "Tree Support Branch Distance",
|
||||
"description": "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0.001",
|
||||
"default_value": 4,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_tree_branch_diameter":
|
||||
{
|
||||
"label": "Tree Support Branch Diameter",
|
||||
"description": "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "support_line_width * 2",
|
||||
"default_value": 2,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_branch_diameter_angle":
|
||||
{
|
||||
"label": "Tree Support Branch Diameter Angle",
|
||||
"description": "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "89.9999",
|
||||
"maximum_value_warning": "15",
|
||||
"default_value": 5,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_collision_resolution":
|
||||
{
|
||||
"label": "Tree Support Collision Resolution",
|
||||
"description": "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "support_line_width / 4",
|
||||
"maximum_value_warning": "support_line_width * 2",
|
||||
"default_value": 0.4,
|
||||
"value": "support_line_width / 2",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable and support_tree_branch_diameter_angle > 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_wall_thickness":
|
||||
{
|
||||
"label": "Tree Support Wall Thickness",
|
||||
"description": "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "wall_line_width",
|
||||
"default_value": 0.8,
|
||||
"value": "support_line_width",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
{
|
||||
"support_tree_wall_count":
|
||||
{
|
||||
"label": "Tree Support Wall Line Count",
|
||||
"description": "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
|
||||
"type": "int",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "1",
|
||||
"default_value": 1,
|
||||
"value": "round(support_tree_wall_thickness / support_line_width)",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_tree_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"gradual_support_infill_steps":
|
||||
{
|
||||
"label": "Gradual Support Infill Steps",
|
||||
|
@ -3801,7 +3923,7 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value_warning": "1 if (support_pattern == 'cross' or support_pattern == 'lines' or support_pattern == 'zigzag' or support_pattern == 'concentric' or support_pattern == 'concentric_3d') else 5",
|
||||
"maximum_value": "999999 if support_line_distance == 0 else (20 - math.log(support_line_distance) / math.log(2))",
|
||||
"enabled": "support_enable and support_infill_rate > 0",
|
||||
"enabled": "(support_enable or support_tree_enable) and support_infill_rate > 0",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
|
@ -3814,7 +3936,7 @@
|
|||
"default_value": 1,
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
|
||||
"enabled": "support_enable and support_infill_rate > 0 and gradual_support_infill_steps > 0",
|
||||
"enabled": "(support_enable or support_tree_enable) and support_infill_rate > 0 and gradual_support_infill_steps > 0",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
|
@ -3825,7 +3947,7 @@
|
|||
"type": "bool",
|
||||
"default_value": false,
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
{
|
||||
|
@ -3837,7 +3959,7 @@
|
|||
"default_value": false,
|
||||
"value": "extruderValue(support_roof_extruder_nr, 'support_interface_enable')",
|
||||
"limit_to_extruder": "support_roof_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_bottom_enable":
|
||||
|
@ -3848,7 +3970,7 @@
|
|||
"default_value": false,
|
||||
"value": "extruderValue(support_bottom_extruder_nr, 'support_interface_enable')",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable or support_tree_enable",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
|
@ -3864,7 +3986,7 @@
|
|||
"minimum_value_warning": "0.2 + layer_height",
|
||||
"maximum_value_warning": "10",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"enabled": "support_interface_enable and support_enable",
|
||||
"enabled": "support_interface_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
{
|
||||
|
@ -3880,7 +4002,7 @@
|
|||
"maximum_value_warning": "10",
|
||||
"value": "extruderValue(support_roof_extruder_nr, 'support_interface_height')",
|
||||
"limit_to_extruder": "support_roof_extruder_nr",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"enabled": "support_roof_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_bottom_height":
|
||||
|
@ -3895,7 +4017,7 @@
|
|||
"minimum_value_warning": "min(0.2 + layer_height, support_bottom_stair_step_height)",
|
||||
"maximum_value_warning": "10",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr",
|
||||
"enabled": "support_bottom_enable and support_enable",
|
||||
"enabled": "support_bottom_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
|
@ -3922,7 +4044,7 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value_warning": "100",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"enabled": "support_interface_enable and support_enable",
|
||||
"enabled": "support_interface_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
|
@ -3937,7 +4059,7 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"limit_to_extruder": "support_roof_extruder_nr",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"enabled": "support_roof_enable and (support_enable or support_tree_enable)",
|
||||
"value": "extruderValue(support_roof_extruder_nr, 'support_interface_density')",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
|
@ -3954,7 +4076,7 @@
|
|||
"minimum_value_warning": "support_roof_line_width - 0.0001",
|
||||
"value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == 'grid' else (3 if support_roof_pattern == 'triangles' else 1))",
|
||||
"limit_to_extruder": "support_roof_extruder_nr",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"enabled": "support_roof_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
|
@ -3970,7 +4092,7 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr",
|
||||
"enabled": "support_bottom_enable and support_enable",
|
||||
"enabled": "support_bottom_enable and (support_enable or support_tree_enable)",
|
||||
"value": "extruderValue(support_bottom_extruder_nr, 'support_interface_density')",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
|
@ -3987,7 +4109,7 @@
|
|||
"minimum_value_warning": "support_bottom_line_width - 0.0001",
|
||||
"value": "0 if support_bottom_density == 0 else (support_bottom_line_width * 100) / support_bottom_density * (2 if support_bottom_pattern == 'grid' else (3 if support_bottom_pattern == 'triangles' else 1))",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr",
|
||||
"enabled": "support_bottom_enable and support_enable",
|
||||
"enabled": "support_bottom_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
|
@ -4011,7 +4133,7 @@
|
|||
},
|
||||
"default_value": "concentric",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"enabled": "support_interface_enable and support_enable",
|
||||
"enabled": "support_interface_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
|
@ -4033,7 +4155,7 @@
|
|||
"default_value": "concentric",
|
||||
"value": "extruderValue(support_roof_extruder_nr, 'support_interface_pattern')",
|
||||
"limit_to_extruder": "support_roof_extruder_nr",
|
||||
"enabled": "support_roof_enable and support_enable",
|
||||
"enabled": "support_roof_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
|
@ -4054,7 +4176,7 @@
|
|||
"default_value": "concentric",
|
||||
"value": "extruderValue(support_bottom_extruder_nr, 'support_interface_pattern')",
|
||||
"limit_to_extruder": "support_bottom_extruder_nr",
|
||||
"enabled": "support_bottom_enable and support_enable",
|
||||
"enabled": "support_bottom_enable and (support_enable or support_tree_enable)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
|
|
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..."
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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 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 deleteSelection: deleteSelectionAction;
|
||||
|
@ -36,6 +41,7 @@ Item
|
|||
property alias selectAll: selectAllAction;
|
||||
property alias deleteAll: deleteAllAction;
|
||||
property alias reloadAll: reloadAllAction;
|
||||
property alias arrangeAllBuildPlates: arrangeAllBuildPlatesAction;
|
||||
property alias arrangeAll: arrangeAllAction;
|
||||
property alias arrangeSelection: arrangeSelectionAction;
|
||||
property alias resetAllTranslation: resetAllTranslationAction;
|
||||
|
@ -104,9 +110,37 @@ Item
|
|||
|
||||
Action
|
||||
{
|
||||
id: homeCameraAction;
|
||||
text: catalog.i18nc("@action:inmenu menubar:view","&Reset camera position");
|
||||
onTriggered: CuraActions.homeCamera();
|
||||
id: view3DCameraAction;
|
||||
text: catalog.i18nc("@action:inmenu menubar:view","&3D View");
|
||||
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
|
||||
|
@ -311,6 +345,13 @@ Item
|
|||
onTriggered: CuraApplication.reloadAll();
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: arrangeAllBuildPlatesAction;
|
||||
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models To All Build Plates");
|
||||
onTriggered: Printer.arrangeObjectsToAllBuildPlates();
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: arrangeAllAction;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
id: topbar
|
||||
|
|
|
@ -7,7 +7,7 @@ import QtQuick.Dialogs 1.2
|
|||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
import Cura 1.2 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
|
@ -39,6 +39,35 @@ Menu
|
|||
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
|
||||
MenuSeparator {}
|
||||
MenuItem { action: Cura.Actions.selectAll; }
|
||||
|
|
|
@ -5,12 +5,12 @@ import QtQuick 2.2
|
|||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
import Cura 1.2 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
||||
id: menu
|
||||
id: base
|
||||
enabled: !PrintInformation.preSliced
|
||||
|
||||
// main views
|
||||
|
@ -25,12 +25,50 @@ Menu
|
|||
exclusiveGroup: group
|
||||
onTriggered: UM.Controller.setActiveView(model.id)
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
onObjectAdded: base.insertItem(index, object)
|
||||
onObjectRemoved: base.removeItem(object)
|
||||
}
|
||||
ExclusiveGroup { id: group }
|
||||
|
||||
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");
|
||||
visible: UM.Preferences.getValue("cura/use_multi_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; }
|
||||
}
|
||||
|
|
266
resources/qml/ObjectsList.qml
Normal file
266
resources/qml/ObjectsList.qml
Normal file
|
@ -0,0 +1,266 @@
|
|||
// 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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -453,6 +453,34 @@ UM.PreferencesPage
|
|||
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 {
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
|
|
@ -1,18 +1,58 @@
|
|||
// 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 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Button {
|
||||
id: base;
|
||||
|
||||
style: UM.Theme.styles.sidebar_category;
|
||||
Button
|
||||
{
|
||||
id: base
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
background: Rectangle
|
||||
{
|
||||
implicitHeight: UM.Theme.getSize("section").height
|
||||
color: {
|
||||
if (base.color) {
|
||||
return base.color;
|
||||
} else if (!base.enabled) {
|
||||
return UM.Theme.getColor("setting_category_disabled");
|
||||
} else if (base.hovered && base.checkable && base.checked) {
|
||||
return UM.Theme.getColor("setting_category_active_hover");
|
||||
} else if (base.pressed || (base.checkable && base.checked)) {
|
||||
return UM.Theme.getColor("setting_category_active");
|
||||
} else if (base.hovered) {
|
||||
return UM.Theme.getColor("setting_category_hover");
|
||||
} else {
|
||||
return UM.Theme.getColor("setting_category");
|
||||
}
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
Rectangle
|
||||
{
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
color: {
|
||||
if (!base.enabled) {
|
||||
return UM.Theme.getColor("setting_category_disabled_border");
|
||||
} else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
|
||||
return UM.Theme.getColor("setting_category_active_hover_border");
|
||||
} else if (base.pressed || (base.checkable && base.checked)) {
|
||||
return UM.Theme.getColor("setting_category_active_border");
|
||||
} else if (base.hovered || base.activeFocus) {
|
||||
return UM.Theme.getColor("setting_category_hover_border");
|
||||
} else {
|
||||
return UM.Theme.getColor("setting_category_border");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal showTooltip(string text)
|
||||
signal hideTooltip()
|
||||
|
@ -23,20 +63,102 @@ Button {
|
|||
|
||||
property var focusItem: base
|
||||
|
||||
//text: definition.label
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
anchors.left: parent.left
|
||||
|
||||
Label {
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
leftMargin: 2 * UM.Theme.getSize("default_margin").width + UM.Theme.getSize("section_icon").width
|
||||
right: parent.right;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
text: definition.label
|
||||
iconSource: UM.Theme.getIcon(definition.icon)
|
||||
font: UM.Theme.getFont("setting_category")
|
||||
color:
|
||||
{
|
||||
if (!base.enabled) {
|
||||
return UM.Theme.getColor("setting_category_disabled_text");
|
||||
} else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
|
||||
return UM.Theme.getColor("setting_category_active_hover_text");
|
||||
} else if (base.pressed || (base.checkable && base.checked)) {
|
||||
return UM.Theme.getColor("setting_category_active_text");
|
||||
} else if (base.hovered || base.activeFocus) {
|
||||
return UM.Theme.getColor("setting_category_hover_text");
|
||||
} else {
|
||||
return UM.Theme.getColor("setting_category_text");
|
||||
}
|
||||
}
|
||||
fontSizeMode: Text.HorizontalFit
|
||||
minimumPointSize: 8
|
||||
}
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: category_arrow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
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
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color:
|
||||
{
|
||||
if (!base.enabled) {
|
||||
return UM.Theme.getColor("setting_category_disabled_text");
|
||||
} else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
|
||||
return UM.Theme.getColor("setting_category_active_hover_text");
|
||||
} else if (base.pressed || (base.checkable && base.checked)) {
|
||||
return UM.Theme.getColor("setting_category_active_text");
|
||||
} else if (base.hovered || base.activeFocus) {
|
||||
return UM.Theme.getColor("setting_category_hover_text");
|
||||
} else {
|
||||
return UM.Theme.getColor("setting_category_text");
|
||||
}
|
||||
}
|
||||
source: base.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
|
||||
}
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: icon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
color:
|
||||
{
|
||||
if (!base.enabled) {
|
||||
return UM.Theme.getColor("setting_category_disabled_text");
|
||||
} else if((base.hovered || base.activeFocus) && base.checkable && base.checked) {
|
||||
return UM.Theme.getColor("setting_category_active_hover_text");
|
||||
} else if(base.pressed || (base.checkable && base.checked)) {
|
||||
return UM.Theme.getColor("setting_category_active_text");
|
||||
} else if(base.hovered || base.activeFocus) {
|
||||
return UM.Theme.getColor("setting_category_hover_text");
|
||||
} else {
|
||||
return UM.Theme.getColor("setting_category_text");
|
||||
}
|
||||
}
|
||||
source: UM.Theme.getIcon(definition.icon)
|
||||
width: UM.Theme.getSize("section_icon").width;
|
||||
height: UM.Theme.getSize("section_icon").height;
|
||||
sourceSize.width: width + 15 * screenScaleFactor
|
||||
sourceSize.height: width + 15 * screenScaleFactor
|
||||
}
|
||||
|
||||
checkable: true
|
||||
checked: definition.expanded
|
||||
|
||||
onClicked:
|
||||
{
|
||||
if(definition.expanded)
|
||||
{
|
||||
if (definition.expanded) {
|
||||
settingDefinitionsModel.collapse(definition.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
settingDefinitionsModel.expandAll(definition.key);
|
||||
}
|
||||
//Set focus so that tab navigation continues from this point on.
|
||||
|
@ -70,13 +192,14 @@ Button {
|
|||
|
||||
anchors {
|
||||
right: inheritButton.visible ? inheritButton.left : parent.right
|
||||
rightMargin: inheritButton.visible? UM.Theme.getSize("default_margin").width / 2 : UM.Theme.getSize("setting_preferences_button_margin").width
|
||||
verticalCenter: parent.verticalCenter;
|
||||
// use 1.9 as the factor because there is a 0.1 difference between the settings and inheritance warning icons
|
||||
rightMargin: inheritButton.visible ? UM.Theme.getSize("default_margin").width / 2 : category_arrow.width + UM.Theme.getSize("default_margin").width * 1.9
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
color: UM.Theme.getColor("setting_control_button");
|
||||
color: UM.Theme.getColor("setting_control_button")
|
||||
hoverColor: UM.Theme.getColor("setting_control_button_hover")
|
||||
iconSource: UM.Theme.getIcon("settings");
|
||||
iconSource: UM.Theme.getIcon("settings")
|
||||
|
||||
onClicked: {
|
||||
Cura.Actions.configureSettingVisibility.trigger(definition)
|
||||
|
@ -85,20 +208,20 @@ Button {
|
|||
|
||||
UM.SimpleButton
|
||||
{
|
||||
id: inheritButton;
|
||||
id: inheritButton
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("setting_preferences_button_margin").width
|
||||
anchors.rightMargin: category_arrow.width + UM.Theme.getSize("default_margin").width * 2
|
||||
|
||||
visible:
|
||||
{
|
||||
if(Cura.SettingInheritanceManager.settingsWithInheritanceWarning.indexOf(definition.key) >= 0)
|
||||
if (Cura.SettingInheritanceManager.settingsWithInheritanceWarning.indexOf(definition.key) >= 0)
|
||||
{
|
||||
var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(definition.key)
|
||||
for(var i = 0; i < children_with_override.length; i++)
|
||||
for (var i = 0; i < children_with_override.length; i++)
|
||||
{
|
||||
if(!settingDefinitionsModel.getVisible(children_with_override[i]))
|
||||
if (!settingDefinitionsModel.getVisible(children_with_override[i]))
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
|
@ -17,71 +16,52 @@ SettingItem
|
|||
id: control
|
||||
|
||||
model: definition.options
|
||||
textRole: "value";
|
||||
textRole: "value"
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent;
|
||||
acceptedButtons: Qt.NoButton;
|
||||
onWheel: wheel.accepted = true;
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: wheel.accepted = true
|
||||
}
|
||||
|
||||
style: ComboBoxStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(!enabled)
|
||||
{
|
||||
if (!enabled) {
|
||||
return UM.Theme.getColor("setting_control_disabled")
|
||||
}
|
||||
if(control.hovered || control.activeFocus)
|
||||
{
|
||||
|
||||
if (control.hovered || control.activeFocus) {
|
||||
return UM.Theme.getColor("setting_control_highlight")
|
||||
}
|
||||
|
||||
return UM.Theme.getColor("setting_control")
|
||||
}
|
||||
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color:
|
||||
{
|
||||
if(!enabled)
|
||||
{
|
||||
if (!enabled) {
|
||||
return UM.Theme.getColor("setting_control_disabled_border")
|
||||
}
|
||||
if(control.hovered || control.activeFocus)
|
||||
{
|
||||
|
||||
if (control.hovered || control.activeFocus) {
|
||||
return UM.Theme.getColor("setting_control_border_highlight")
|
||||
}
|
||||
|
||||
return UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
}
|
||||
label: Item
|
||||
{
|
||||
Label
|
||||
{
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.right: downArrow.left;
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
text: control.currentText;
|
||||
font: UM.Theme.getFont("default");
|
||||
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text");
|
||||
|
||||
elide: Text.ElideRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
indicator: UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
x: control.width - width - control.rightPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
|
@ -89,23 +69,50 @@ SettingItem
|
|||
sourceSize.width: width + 5 * screenScaleFactor
|
||||
sourceSize.height: width + 5 * screenScaleFactor
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text");
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
}
|
||||
|
||||
contentItem: Label
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: downArrow.left
|
||||
|
||||
text: control.currentText
|
||||
font: UM.Theme.getFont("default")
|
||||
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
width: control.width
|
||||
height: control.height
|
||||
highlighted: control.highlightedIndex == index
|
||||
|
||||
contentItem: Text
|
||||
{
|
||||
text: modelData.value
|
||||
color: control.contentItem.color
|
||||
font: UM.Theme.getFont("default")
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
onActivated:
|
||||
{
|
||||
forceActiveFocus();
|
||||
propertyProvider.setPropertyValue("value", definition.options[index].key);
|
||||
forceActiveFocus()
|
||||
propertyProvider.setPropertyValue("value", definition.options[index].key)
|
||||
}
|
||||
|
||||
onActiveFocusChanged:
|
||||
{
|
||||
if(activeFocus)
|
||||
{
|
||||
base.focusReceived();
|
||||
base.focusReceived()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +120,7 @@ SettingItem
|
|||
{
|
||||
base.setActiveFocusToNextSetting(true)
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed:
|
||||
{
|
||||
base.setActiveFocusToNextSetting(false)
|
||||
|
@ -126,16 +134,14 @@ SettingItem
|
|||
{
|
||||
// FIXME this needs to go away once 'resolve' is combined with 'value' in our data model.
|
||||
var value = undefined;
|
||||
if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1))
|
||||
{
|
||||
if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1)) {
|
||||
// We have a resolve function. Indicates that the setting is not settable per extruder and that
|
||||
// we have to choose between the resolved value (default) and the global value
|
||||
// (if user has explicitly set this).
|
||||
value = base.resolve;
|
||||
}
|
||||
|
||||
if (value == undefined)
|
||||
{
|
||||
if (value == undefined) {
|
||||
value = propertyProvider.properties.value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
@ -65,17 +64,30 @@ SettingItem
|
|||
value: control.currentText != "" ? control.model.getItem(control.currentIndex).color : ""
|
||||
}
|
||||
|
||||
style: ComboBoxStyle
|
||||
indicator: UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
x: control.width - width - control.rightPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width + 5 * screenScaleFactor
|
||||
sourceSize.height: width + 5 * screenScaleFactor
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text");
|
||||
}
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(!enabled)
|
||||
if (!enabled)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_disabled");
|
||||
}
|
||||
if(control.hovered || base.activeFocus)
|
||||
if (control.hovered || base.activeFocus)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_highlight");
|
||||
}
|
||||
|
@ -84,23 +96,28 @@ SettingItem
|
|||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color:
|
||||
{
|
||||
if(!enabled)
|
||||
if (!enabled)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_disabled_border")
|
||||
}
|
||||
if(control.hovered || control.activeFocus)
|
||||
if (control.hovered || control.activeFocus)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_border_highlight")
|
||||
}
|
||||
return UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
}
|
||||
label: Item
|
||||
|
||||
contentItem: Item
|
||||
{
|
||||
Label
|
||||
{
|
||||
id: extruderText
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width
|
||||
anchors.right: swatch.left
|
||||
|
||||
text: control.currentText
|
||||
font: UM.Theme.getFont("default")
|
||||
|
@ -109,39 +126,39 @@ SettingItem
|
|||
elide: Text.ElideLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
height: UM.Theme.getSize("setting_control").height / 2
|
||||
width: height
|
||||
|
||||
anchors
|
||||
{
|
||||
right: arrow.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
margins: UM.Theme.getSize("default_margin").width / 4
|
||||
}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: downArrow.width + UM.Theme.getSize("setting_unit_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width / 4
|
||||
|
||||
border.width: UM.Theme.getSize("default_lining").width * 2
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border")
|
||||
radius: width / 2
|
||||
|
||||
color: control.color
|
||||
}
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: arrow
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width + 5 * screenScaleFactor
|
||||
sourceSize.height: width + 5 * screenScaleFactor
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
}
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
width: control.width
|
||||
height: control.height
|
||||
highlighted: control.highlightedIndex == index
|
||||
|
||||
contentItem: Text
|
||||
{
|
||||
text: model.name
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
@ -84,17 +83,30 @@ SettingItem
|
|||
value: control.currentText != "" ? control.model.getItem(control.currentIndex).color : ""
|
||||
}
|
||||
|
||||
style: ComboBoxStyle
|
||||
indicator: UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
x: control.width - width - control.rightPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width + 5 * screenScaleFactor
|
||||
sourceSize.height: width + 5 * screenScaleFactor
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text");
|
||||
}
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(!enabled)
|
||||
if (!enabled)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_disabled");
|
||||
}
|
||||
if(control.hovered || control.activeFocus)
|
||||
if (control.hovered || control.activeFocus)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_highlight");
|
||||
}
|
||||
|
@ -103,23 +115,28 @@ SettingItem
|
|||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color:
|
||||
{
|
||||
if(!enabled)
|
||||
if (!enabled)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_disabled_border")
|
||||
}
|
||||
if(control.hovered || control.activeFocus)
|
||||
if (control.hovered || control.activeFocus)
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_border_highlight")
|
||||
}
|
||||
return UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
}
|
||||
label: Item
|
||||
|
||||
contentItem: Item
|
||||
{
|
||||
Label
|
||||
{
|
||||
id: extruderText
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - swatch.width - arrow.width;
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width
|
||||
anchors.right: swatch.left
|
||||
|
||||
text: control.currentText
|
||||
font: UM.Theme.getFont("default")
|
||||
|
@ -128,39 +145,39 @@ SettingItem
|
|||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
height: UM.Theme.getSize("setting_control").height / 2
|
||||
width: height
|
||||
|
||||
anchors
|
||||
{
|
||||
right: arrow.left;
|
||||
verticalCenter: parent.verticalCenter
|
||||
margins: UM.Theme.getSize("default_margin").width / 4
|
||||
}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: downArrow.width + UM.Theme.getSize("setting_unit_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width / 4
|
||||
|
||||
border.width: UM.Theme.getSize("default_lining").width * 2
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border")
|
||||
radius: width / 2
|
||||
|
||||
color: control.color
|
||||
}
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: arrow
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width + 5 * screenScaleFactor
|
||||
sourceSize.height: width + 5 * screenScaleFactor
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
}
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
width: control.width
|
||||
height: control.height
|
||||
highlighted: control.highlightedIndex == index
|
||||
|
||||
contentItem: Text
|
||||
{
|
||||
text: model.name
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// 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.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
|
@ -17,6 +17,7 @@ SettingItem
|
|||
{
|
||||
textHasChanged = false;
|
||||
textBeforeEdit = focusItem.text;
|
||||
focusItem.selectAll();
|
||||
}
|
||||
|
||||
contents: Rectangle
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
@ -372,7 +372,7 @@ Item
|
|||
{
|
||||
id: provider
|
||||
|
||||
containerStackId: Cura.ExtruderManager.activeExtruderStackId
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
key: model.key ? model.key : ""
|
||||
watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
|
||||
storeIndex: 0
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// 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 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
@ -12,9 +11,9 @@ import "Menus"
|
|||
|
||||
Rectangle
|
||||
{
|
||||
id: base;
|
||||
id: base
|
||||
|
||||
property int currentModeIndex;
|
||||
property int currentModeIndex
|
||||
property bool hideSettings: PrintInformation.preSliced
|
||||
property bool hideView: Cura.MachineManager.activeMachineName == ""
|
||||
|
||||
|
@ -78,7 +77,7 @@ Rectangle
|
|||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons;
|
||||
acceptedButtons: Qt.AllButtons
|
||||
|
||||
onWheel:
|
||||
{
|
||||
|
@ -119,13 +118,14 @@ Rectangle
|
|||
UM.Preferences.setValue("cura/active_mode", currentModeIndex);
|
||||
if(modesListModel.count > base.currentModeIndex)
|
||||
{
|
||||
sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true });
|
||||
sidebarContents.replace(modesListModel.get(base.currentModeIndex).item, { "replace": true })
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
id: settingsModeLabel
|
||||
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified");
|
||||
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.top: headerSeparator.bottom
|
||||
|
@ -136,13 +136,18 @@ Rectangle
|
|||
visible: !monitoringPrint && !hideView
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// Settings mode selection toggle
|
||||
Rectangle
|
||||
{
|
||||
id: settingsModeSelection
|
||||
color: "transparent"
|
||||
|
||||
width: Math.floor(parent.width * 0.55)
|
||||
height: UM.Theme.getSize("sidebar_header_mode_toggle").height
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
anchors.top:
|
||||
{
|
||||
if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2)
|
||||
|
@ -154,66 +159,69 @@ Rectangle
|
|||
return headerSeparator.bottom;
|
||||
}
|
||||
}
|
||||
anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
|
||||
visible: !monitoringPrint && !hideSettings && !hideView
|
||||
Component{
|
||||
|
||||
Component
|
||||
{
|
||||
id: wizardDelegate
|
||||
Button {
|
||||
|
||||
Button
|
||||
{
|
||||
id: control
|
||||
|
||||
height: settingsModeSelection.height
|
||||
width: Math.floor(0.5 * parent.width)
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Math.floor(0.5 * parent.width)
|
||||
text: model.text
|
||||
exclusiveGroup: modeMenuGroup;
|
||||
checkable: true;
|
||||
|
||||
ButtonGroup.group: modeMenuGroup
|
||||
|
||||
checkable: true
|
||||
checked: base.currentModeIndex == index
|
||||
onClicked: base.currentModeIndex = index
|
||||
|
||||
onHoveredChanged: {
|
||||
onHoveredChanged:
|
||||
{
|
||||
if (hovered)
|
||||
{
|
||||
tooltipDelayTimer.item = settingsModeSelection
|
||||
tooltipDelayTimer.text = model.tooltipText
|
||||
tooltipDelayTimer.start();
|
||||
tooltipDelayTimer.start()
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltipDelayTimer.stop();
|
||||
base.hideTooltip();
|
||||
tooltipDelayTimer.stop()
|
||||
base.hideTooltip()
|
||||
}
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width
|
||||
border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_border") :
|
||||
UM.Theme.getColor("action_button_border")
|
||||
color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered") :
|
||||
UM.Theme.getColor("action_button")
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
Label {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2
|
||||
color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_text") :
|
||||
UM.Theme.getColor("action_button_text")
|
||||
border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border"): UM.Theme.getColor("action_button_border")
|
||||
|
||||
// for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is
|
||||
color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
|
||||
}
|
||||
|
||||
contentItem: Text
|
||||
{
|
||||
text: model.text
|
||||
font: UM.Theme.getFont("default")
|
||||
text: control.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
}
|
||||
label: Item { }
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
ExclusiveGroup { id: modeMenuGroup; }
|
||||
|
||||
ButtonGroup
|
||||
{
|
||||
id: modeMenuGroup
|
||||
}
|
||||
|
||||
ListView
|
||||
{
|
||||
|
@ -238,34 +246,24 @@ Rectangle
|
|||
anchors.right: base.right
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
|
||||
delegate: StackViewDelegate
|
||||
{
|
||||
function transitionFinished(properties)
|
||||
{
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
|
||||
pushTransition: StackViewTransition
|
||||
{
|
||||
PropertyAnimation
|
||||
{
|
||||
target: enterItem
|
||||
replaceEnter: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
to:1
|
||||
duration: 100
|
||||
}
|
||||
PropertyAnimation
|
||||
{
|
||||
target: exitItem
|
||||
}
|
||||
|
||||
replaceExit: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
to:0
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
|
@ -559,19 +557,19 @@ Rectangle
|
|||
|
||||
SidebarTooltip
|
||||
{
|
||||
id: tooltip;
|
||||
id: tooltip
|
||||
}
|
||||
|
||||
// Setting mode: Recommended or Custom
|
||||
ListModel
|
||||
{
|
||||
id: modesListModel;
|
||||
id: modesListModel
|
||||
}
|
||||
|
||||
SidebarSimple
|
||||
{
|
||||
id: sidebarSimple;
|
||||
visible: false;
|
||||
id: sidebarSimple
|
||||
visible: false
|
||||
|
||||
onShowTooltip: base.showTooltip(item, location, text)
|
||||
onHideTooltip: base.hideTooltip()
|
||||
|
@ -579,8 +577,8 @@ Rectangle
|
|||
|
||||
SidebarAdvanced
|
||||
{
|
||||
id: sidebarAdvanced;
|
||||
visible: false;
|
||||
id: sidebarAdvanced
|
||||
visible: false
|
||||
|
||||
onShowTooltip: base.showTooltip(item, location, text)
|
||||
onHideTooltip: base.hideTooltip()
|
||||
|
@ -598,7 +596,7 @@ Rectangle
|
|||
tooltipText: catalog.i18nc("@tooltip", "<b>Custom Print Setup</b><br/><br/>Print with finegrained control over every last bit of the slicing process."),
|
||||
item: sidebarAdvanced
|
||||
})
|
||||
sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true });
|
||||
sidebarContents.replace(modesListModel.get(base.currentModeIndex).item, { "immediate": true })
|
||||
|
||||
var index = Math.floor(UM.Preferences.getValue("cura/active_mode"))
|
||||
if(index)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import "Settings"
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// 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 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.2 as Cura
|
||||
|
||||
Item
|
||||
{
|
||||
id: base;
|
||||
id: base
|
||||
|
||||
signal showTooltip(Item item, point location, string text);
|
||||
signal hideTooltip();
|
||||
|
@ -70,6 +70,18 @@ Item
|
|||
onActiveVariantChanged: qualityModel.update()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: base
|
||||
onVisibleChanged:
|
||||
{
|
||||
// update needs to be called when the widgets are visible, otherwise the step width calculation
|
||||
// will fail because the width of an invisible item is 0.
|
||||
if (visible) {
|
||||
qualityModel.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListModel
|
||||
{
|
||||
id: qualityModel
|
||||
|
@ -183,11 +195,10 @@ Item
|
|||
text:
|
||||
{
|
||||
var result = ""
|
||||
if(Cura.MachineManager.activeMachine != null){
|
||||
if (Cura.MachineManager.activeMachine != null) {
|
||||
result = Cura.ProfilesModel.getItem(index).layer_height_without_unit
|
||||
|
||||
var result = Cura.ProfilesModel.getItem(index).layer_height_without_unit
|
||||
|
||||
if(result == undefined)
|
||||
if (result == undefined)
|
||||
result = ""
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -116,10 +116,8 @@ Rectangle
|
|||
iconSource: UM.Theme.getIcon("view_3d")
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||
onClicked:{
|
||||
UM.Controller.rotateView("3d", 0);
|
||||
}
|
||||
visible: base.width - allItemsWidth - 4 * this.width > 0;
|
||||
onClicked:UM.Controller.rotateView("3d", 0)
|
||||
visible: base.width - allItemsWidth - 4 * this.width > 0
|
||||
}
|
||||
|
||||
// #2 Front view
|
||||
|
@ -128,10 +126,8 @@ Rectangle
|
|||
iconSource: UM.Theme.getIcon("view_front")
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||
onClicked:{
|
||||
UM.Controller.rotateView("home", 0);
|
||||
}
|
||||
visible: base.width - allItemsWidth - 3 * this.width > 0;
|
||||
onClicked: UM.Controller.rotateView("home", 0);
|
||||
visible: base.width - allItemsWidth - 3 * this.width > 0
|
||||
}
|
||||
|
||||
// #3 Top view
|
||||
|
@ -140,10 +136,8 @@ Rectangle
|
|||
iconSource: UM.Theme.getIcon("view_top")
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||
onClicked:{
|
||||
UM.Controller.rotateView("y", 90);
|
||||
}
|
||||
visible: base.width - allItemsWidth - 2 * this.width > 0;
|
||||
onClicked: UM.Controller.rotateView("y", 90)
|
||||
visible: base.width - allItemsWidth - 2 * this.width > 0
|
||||
}
|
||||
|
||||
// #4 Left view
|
||||
|
@ -152,10 +146,8 @@ Rectangle
|
|||
iconSource: UM.Theme.getIcon("view_left")
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||
onClicked:{
|
||||
UM.Controller.rotateView("x", 90);
|
||||
}
|
||||
visible: base.width - allItemsWidth - 1 * this.width > 0;
|
||||
onClicked: UM.Controller.rotateView("x", 90)
|
||||
visible: base.width - allItemsWidth - 1 * this.width > 0
|
||||
}
|
||||
|
||||
// #5 Left view
|
||||
|
@ -164,10 +156,8 @@ Rectangle
|
|||
iconSource: UM.Theme.getIcon("view_right")
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||
onClicked:{
|
||||
UM.Controller.rotateView("x", -90);
|
||||
}
|
||||
visible: base.width - allItemsWidth > 0;
|
||||
onClicked: UM.Controller.rotateView("x", -90)
|
||||
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
|
|
@ -391,6 +391,11 @@
|
|||
|
||||
"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 time
|
||||
|
||||
from cura.Arrange import Arrange
|
||||
from cura.ShapeArray import ShapeArray
|
||||
from cura.Arranging.Arrange import Arrange
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
|
||||
|
||||
def gimmeShapeArray():
|
||||
|
@ -120,6 +118,13 @@ def test_arrayFromPolygon2():
|
|||
assert numpy.any(array)
|
||||
|
||||
|
||||
## Polygon -> array
|
||||
def test_fromPolygon():
|
||||
vertices = numpy.array([[0, 0.5], [0, 0], [0.5, 0]])
|
||||
array = ShapeArray.fromPolygon(vertices, scale=0.5)
|
||||
assert numpy.any(array.arr)
|
||||
|
||||
|
||||
## Line definition -> array with true/false
|
||||
def test_check():
|
||||
base_array = numpy.zeros([5, 5], dtype=float)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue