mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 16:48:58 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura
This commit is contained in:
commit
283d08a0d6
@ -111,6 +111,9 @@ class BuildVolume(SceneNode):
|
||||
# but it does not update the disallowed areas after material change
|
||||
Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged)
|
||||
|
||||
# Enable and disable extruder
|
||||
Application.getInstance().getMachineManager().extruderChanged.connect(self.updateNodeBoundaryCheck)
|
||||
|
||||
# list of settings which were updated
|
||||
self._changed_settings_since_last_rebuild = []
|
||||
|
||||
@ -217,30 +220,26 @@ class BuildVolume(SceneNode):
|
||||
group_nodes.append(node) # Keep list of affected group_nodes
|
||||
|
||||
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
|
||||
node._outside_buildarea = False
|
||||
bbox = node.getBoundingBox()
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
node._outside_buildarea = True
|
||||
if node.collidesWithBbox(build_volume_bounding_box):
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
|
||||
convex_hull = node.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
if not convex_hull.isValid():
|
||||
return
|
||||
# Check for collisions between disallowed areas and the object
|
||||
for area in self.getDisallowedAreas():
|
||||
overlap = convex_hull.intersectsPolygon(area)
|
||||
if overlap is None:
|
||||
continue
|
||||
node._outside_buildarea = True
|
||||
continue
|
||||
if node.collidesWithArea(self.getDisallowedAreas()):
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
|
||||
# Mark the node as outside build volume if the set extruder is disabled
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
if not self._global_container_stack.extruders[extruder_position].isEnabled:
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
|
||||
node.setOutsideBuildArea(False)
|
||||
|
||||
# Group nodes should override the _outside_buildarea property of their children.
|
||||
for group_node in group_nodes:
|
||||
for child_node in group_node.getAllChildren():
|
||||
child_node._outside_buildarea = group_node._outside_buildarea
|
||||
child_node.setOutsideBuildArea(group_node.isOutsideBuildArea())
|
||||
|
||||
## Update the outsideBuildArea of a single node, given bounds or current build volume
|
||||
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
|
||||
@ -260,24 +259,20 @@ class BuildVolume(SceneNode):
|
||||
build_volume_bounding_box = bounds
|
||||
|
||||
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
|
||||
bbox = node.getBoundingBox()
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
if node.collidesWithBbox(build_volume_bounding_box):
|
||||
node.setOutsideBuildArea(True)
|
||||
return
|
||||
|
||||
if node.collidesWithArea(self.getDisallowedAreas()):
|
||||
node.setOutsideBuildArea(True)
|
||||
return
|
||||
|
||||
# Mark the node as outside build volume if the set extruder is disabled
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
if not self._global_container_stack.extruders[extruder_position].isEnabled:
|
||||
node.setOutsideBuildArea(True)
|
||||
return
|
||||
|
||||
convex_hull = self.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
if not convex_hull.isValid():
|
||||
return
|
||||
# Check for collisions between disallowed areas and the object
|
||||
for area in self.getDisallowedAreas():
|
||||
overlap = convex_hull.intersectsPolygon(area)
|
||||
if overlap is None:
|
||||
continue
|
||||
node.setOutsideBuildArea(True)
|
||||
return
|
||||
node.setOutsideBuildArea(False)
|
||||
|
||||
## Recalculates the build volume & disallowed areas.
|
||||
|
@ -67,6 +67,8 @@ from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
|
||||
from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
|
||||
from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
|
||||
|
||||
from cura.Machines.MachineErrorChecker import MachineErrorChecker
|
||||
|
||||
from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||
|
||||
@ -142,12 +144,6 @@ class CuraApplication(QtApplication):
|
||||
|
||||
Q_ENUMS(ResourceTypes)
|
||||
|
||||
# FIXME: This signal belongs to the MachineManager, but the CuraEngineBackend plugin requires on it.
|
||||
# Because plugins are initialized before the ContainerRegistry, putting this signal in MachineManager
|
||||
# will make it initialized before ContainerRegistry does, and it won't find the active machine, thus
|
||||
# Cura will always show the Add Machine Dialog upon start.
|
||||
stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# this list of dir names will be used by UM to detect an old cura directory
|
||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
|
||||
@ -224,12 +220,14 @@ class CuraApplication(QtApplication):
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
self._extruder_manager = None
|
||||
self._material_manager = None
|
||||
self._quality_manager = None
|
||||
self._object_manager = None
|
||||
self._build_plate_model = None
|
||||
self._multi_build_plate_model = None
|
||||
self._setting_inheritance_manager = None
|
||||
self._simple_mode_settings_manager = None
|
||||
self._cura_scene_controller = None
|
||||
self._machine_error_checker = None
|
||||
|
||||
self._additional_components = {} # Components to add to certain areas in the interface
|
||||
|
||||
@ -743,19 +741,28 @@ class CuraApplication(QtApplication):
|
||||
self.preRun()
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
Logger.log("i", "Initializing variant manager")
|
||||
self._variant_manager = VariantManager(container_registry)
|
||||
self._variant_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing material manager")
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
self._material_manager = MaterialManager(container_registry, parent = self)
|
||||
self._material_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing quality manager")
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
self._quality_manager = QualityManager(container_registry, parent = self)
|
||||
self._quality_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing machine manager")
|
||||
self._machine_manager = MachineManager(self)
|
||||
|
||||
Logger.log("i", "Initializing machine error checker")
|
||||
self._machine_error_checker = MachineErrorChecker(self)
|
||||
self._machine_error_checker.initialize()
|
||||
|
||||
# Check if we should run as single instance or not
|
||||
self._setUpSingleInstanceServer()
|
||||
|
||||
@ -781,8 +788,11 @@ class CuraApplication(QtApplication):
|
||||
self._openFile(file_name)
|
||||
|
||||
self.started = True
|
||||
self.initializationFinished.emit()
|
||||
self.exec_()
|
||||
|
||||
initializationFinished = pyqtSignal()
|
||||
|
||||
## Run Cura without GUI elements and interaction (server mode).
|
||||
def runWithoutGUI(self):
|
||||
self._use_gui = False
|
||||
@ -847,6 +857,9 @@ class CuraApplication(QtApplication):
|
||||
def hasGui(self):
|
||||
return self._use_gui
|
||||
|
||||
def getMachineErrorChecker(self, *args) -> MachineErrorChecker:
|
||||
return self._machine_error_checker
|
||||
|
||||
def getMachineManager(self, *args) -> MachineManager:
|
||||
if self._machine_manager is None:
|
||||
self._machine_manager = MachineManager(self)
|
||||
@ -1605,6 +1618,8 @@ class CuraApplication(QtApplication):
|
||||
fixed_nodes.append(node_)
|
||||
arranger = Arrange.create(fixed_nodes = fixed_nodes)
|
||||
min_offset = 8
|
||||
default_extruder_position = self.getMachineManager().defaultExtruderPosition
|
||||
default_extruder_id = self._global_container_stack.extruders[default_extruder_position].getId()
|
||||
|
||||
for original_node in nodes:
|
||||
|
||||
@ -1670,6 +1685,8 @@ class CuraApplication(QtApplication):
|
||||
|
||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||
op.push()
|
||||
|
||||
node.callDecoration("setActiveExtruder", default_extruder_id)
|
||||
scene.sceneChanged.emit(node)
|
||||
|
||||
self.fileCompleted.emit(filename)
|
||||
|
181
cura/Machines/MachineErrorChecker.py
Normal file
181
cura/Machines/MachineErrorChecker.py
Normal file
@ -0,0 +1,181 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import time
|
||||
|
||||
from collections import deque
|
||||
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.SettingDefinition import SettingDefinition
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
|
||||
#
|
||||
# This class performs setting error checks for the currently active machine.
|
||||
#
|
||||
# The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag.
|
||||
# The idea here is to split the whole error check into small tasks, each of which only checks a single setting key
|
||||
# in a stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should
|
||||
# be good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
|
||||
# for it to finish the complete work.
|
||||
#
|
||||
class MachineErrorChecker(QObject):
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._global_stack = None
|
||||
|
||||
self._has_errors = True # Result of the error check, indicating whether there are errors in the stack
|
||||
self._error_keys = set() # A set of settings keys that have errors
|
||||
self._error_keys_in_progress = set() # The variable that stores the results of the currently in progress check
|
||||
|
||||
self._stacks_and_keys_to_check = None # a FIFO queue of tuples (stack, key) to check for errors
|
||||
|
||||
self._need_to_check = False # Whether we need to schedule a new check or not. This flag is set when a new
|
||||
# error check needs to take place while there is already one running at the moment.
|
||||
self._check_in_progress = False # Whether there is an error check running in progress at the moment.
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self._start_time = 0 # measure checking time
|
||||
|
||||
# This timer delays the starting of error check so we can react less frequently if the user is frequently
|
||||
# changing settings.
|
||||
self._error_check_timer = QTimer(self)
|
||||
self._error_check_timer.setInterval(100)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
|
||||
def initialize(self):
|
||||
self._error_check_timer.timeout.connect(self._rescheduleCheck)
|
||||
|
||||
# Reconnect all signals when the active machine gets changed.
|
||||
self._machine_manager.globalContainerChanged.connect(self._onMachineChanged)
|
||||
|
||||
# Whenever the machine settings get changed, we schedule an error check.
|
||||
self._machine_manager.globalContainerChanged.connect(self.startErrorCheck)
|
||||
self._machine_manager.globalValueChanged.connect(self.startErrorCheck)
|
||||
|
||||
self._onMachineChanged()
|
||||
|
||||
def _onMachineChanged(self):
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self.startErrorCheck)
|
||||
self._global_stack.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruders.values():
|
||||
extruder.propertyChanged.disconnect(self.startErrorCheck)
|
||||
extruder.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
self._global_stack = self._machine_manager.activeMachine
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self.startErrorCheck)
|
||||
self._global_stack.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruders.values():
|
||||
extruder.propertyChanged.connect(self.startErrorCheck)
|
||||
extruder.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
hasErrorUpdated = pyqtSignal()
|
||||
needToWaitForResultChanged = pyqtSignal()
|
||||
errorCheckFinished = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = hasErrorUpdated)
|
||||
def hasError(self) -> bool:
|
||||
return self._has_errors
|
||||
|
||||
@pyqtProperty(bool, notify = needToWaitForResultChanged)
|
||||
def needToWaitForResult(self) -> bool:
|
||||
return self._need_to_check or self._check_in_progress
|
||||
|
||||
# Starts the error check timer to schedule a new error check.
|
||||
def startErrorCheck(self, *args):
|
||||
if not self._check_in_progress:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self._error_check_timer.start()
|
||||
|
||||
# This function is called by the timer to reschedule a new error check.
|
||||
# If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
|
||||
# to notify the current check to stop and start a new one.
|
||||
def _rescheduleCheck(self):
|
||||
if self._check_in_progress and not self._need_to_check:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
return
|
||||
|
||||
self._error_keys_in_progress = set()
|
||||
self._need_to_check = False
|
||||
self.needToWaitForResultChanged.emit()
|
||||
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if global_stack is None:
|
||||
Logger.log("i", "No active machine, nothing to check.")
|
||||
return
|
||||
|
||||
# Populate the (stack, key) tuples to check
|
||||
self._stacks_and_keys_to_check = deque()
|
||||
for stack in [global_stack] + list(global_stack.extruders.values()):
|
||||
for key in stack.getAllKeys():
|
||||
self._stacks_and_keys_to_check.append((stack, key))
|
||||
|
||||
self._application.callLater(self._checkStack)
|
||||
self._start_time = time.time()
|
||||
Logger.log("d", "New error check scheduled.")
|
||||
|
||||
def _checkStack(self):
|
||||
if self._need_to_check:
|
||||
Logger.log("d", "Need to check for errors again. Discard the current progress and reschedule a check.")
|
||||
self._check_in_progress = False
|
||||
self._application.callLater(self.startErrorCheck)
|
||||
return
|
||||
|
||||
self._check_in_progress = True
|
||||
|
||||
# If there is nothing to check any more, it means there is no error.
|
||||
if not self._stacks_and_keys_to_check:
|
||||
# Finish
|
||||
self._setResult(False)
|
||||
return
|
||||
|
||||
# Get the next stack and key to check
|
||||
stack, key = self._stacks_and_keys_to_check.popleft()
|
||||
|
||||
enabled = stack.getProperty(key, "enabled")
|
||||
if not enabled:
|
||||
self._application.callLater(self._checkStack)
|
||||
return
|
||||
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state is None:
|
||||
# Setting is not validated. This can happen if there is only a setting definition.
|
||||
# We do need to validate it, because a setting definitions value can be set by a function, which could
|
||||
# be an invalid setting.
|
||||
definition = stack.getSettingDefinition(key)
|
||||
validator_type = SettingDefinition.getValidatorForType(definition.type)
|
||||
if validator_type:
|
||||
validator = validator_type(key)
|
||||
validation_state = validator(stack)
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
# Finish
|
||||
self._setResult(True)
|
||||
return
|
||||
|
||||
# Schedule the check for the next key
|
||||
self._application.callLater(self._checkStack)
|
||||
|
||||
def _setResult(self, result: bool):
|
||||
if result != self._has_errors:
|
||||
self._has_errors = result
|
||||
self.hasErrorUpdated.emit()
|
||||
self._machine_manager.stacksValidationChanged.emit()
|
||||
self._need_to_check = False
|
||||
self._check_in_progress = False
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self.errorCheckFinished.emit()
|
||||
Logger.log("i", "Error check finished, result = %s, time = %0.1fs", result, time.time() - self._start_time)
|
@ -39,6 +39,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._update)
|
||||
self._machine_manager.activeQualityGroupChanged.connect(self._update)
|
||||
self._machine_manager.extruderChanged.connect(self._update)
|
||||
self._quality_manager.qualitiesUpdated.connect(self._update)
|
||||
|
||||
self._layer_height_unit = "" # This is cached
|
||||
|
@ -21,6 +21,8 @@ class QualitySettingsModel(ListModel):
|
||||
UserValueRole = Qt.UserRole + 6
|
||||
CategoryRole = Qt.UserRole + 7
|
||||
|
||||
GLOBAL_STACK_POSITION = -1
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent = parent)
|
||||
|
||||
@ -36,8 +38,7 @@ class QualitySettingsModel(ListModel):
|
||||
self._application = Application.getInstance()
|
||||
self._quality_manager = self._application.getQualityManager()
|
||||
|
||||
self._selected_position = "" # empty string means GlobalStack
|
||||
# strings such as "0", "1", etc. mean extruder positions
|
||||
self._selected_position = self.GLOBAL_STACK_POSITION #Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
|
||||
self._selected_quality_item = None # The selected quality in the quality management page
|
||||
self._i18n_catalog = None
|
||||
|
||||
@ -54,7 +55,7 @@ class QualitySettingsModel(ListModel):
|
||||
self.selectedPositionChanged.emit()
|
||||
self._update()
|
||||
|
||||
@pyqtProperty(str, fset = setSelectedPosition, notify = selectedPositionChanged)
|
||||
@pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
|
||||
def selectedPosition(self):
|
||||
return self._selected_position
|
||||
|
||||
@ -83,7 +84,7 @@ class QualitySettingsModel(ListModel):
|
||||
quality_group = self._selected_quality_item["quality_group"]
|
||||
quality_changes_group = self._selected_quality_item["quality_changes_group"]
|
||||
|
||||
if self._selected_position == "":
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
quality_node = quality_group.node_for_global
|
||||
else:
|
||||
quality_node = quality_group.nodes_for_extruders.get(self._selected_position)
|
||||
@ -93,14 +94,14 @@ class QualitySettingsModel(ListModel):
|
||||
# Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
|
||||
# the settings in that quality_changes_group.
|
||||
if quality_changes_group is not None:
|
||||
if self._selected_position == "":
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
quality_changes_node = quality_changes_group.node_for_global
|
||||
else:
|
||||
quality_changes_node = quality_changes_group.nodes_for_extruders.get(self._selected_position)
|
||||
if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime
|
||||
try:
|
||||
quality_containers.insert(0, quality_changes_node.getContainer())
|
||||
except:
|
||||
except RuntimeError:
|
||||
# FIXME: This is to prevent incomplete update of QualityManager
|
||||
Logger.logException("d", "Failed to get container for quality changes node %s", quality_changes_node)
|
||||
return
|
||||
@ -127,7 +128,7 @@ class QualitySettingsModel(ListModel):
|
||||
profile_value = new_value
|
||||
|
||||
# Global tab should use resolve (if there is one)
|
||||
if self._selected_position == "":
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
resolve_value = global_container_stack.getProperty(definition.key, "resolve")
|
||||
if resolve_value is not None and definition.key in settings_keys:
|
||||
profile_value = resolve_value
|
||||
@ -135,10 +136,10 @@ class QualitySettingsModel(ListModel):
|
||||
if profile_value is not None:
|
||||
break
|
||||
|
||||
if not self._selected_position:
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
user_value = global_container_stack.userChanges.getProperty(definition.key, "value")
|
||||
else:
|
||||
extruder_stack = global_container_stack.extruders[self._selected_position]
|
||||
extruder_stack = global_container_stack.extruders[str(self._selected_position)]
|
||||
user_value = extruder_stack.userChanges.getProperty(definition.key, "value")
|
||||
|
||||
if profile_value is None and user_value is None:
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional, List
|
||||
from typing import Dict, Optional, List
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
@ -25,7 +25,7 @@ class QualityGroup(QObject):
|
||||
super().__init__(parent)
|
||||
self.name = name
|
||||
self.node_for_global = None # type: Optional["QualityGroup"]
|
||||
self.nodes_for_extruders = dict() # position str -> QualityGroup
|
||||
self.nodes_for_extruders = {} # type: Dict[int, "QualityGroup"]
|
||||
self.quality_type = quality_type
|
||||
self.is_available = False
|
||||
|
||||
|
@ -159,9 +159,9 @@ class QualityManager(QObject):
|
||||
# Updates the given quality groups' availabilities according to which extruders are being used/ enabled.
|
||||
def _updateQualityGroupsAvailability(self, machine: "GlobalStack", quality_group_list):
|
||||
used_extruders = set()
|
||||
# TODO: This will change after the Machine refactoring
|
||||
for i in range(machine.getProperty("machine_extruder_count", "value")):
|
||||
used_extruders.add(str(i))
|
||||
if machine.extruders[str(i)].isEnabled:
|
||||
used_extruders.add(str(i))
|
||||
|
||||
# Update the "is_available" flag for each quality group.
|
||||
for quality_group in quality_group_list:
|
||||
|
@ -55,6 +55,14 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
||||
|
||||
printer_type = self._properties.get(b"machine", b"").decode("utf-8")
|
||||
if printer_type.startswith("9511"):
|
||||
self._printer_type = "ultimaker3_extended"
|
||||
elif printer_type.startswith("9066"):
|
||||
self._printer_type = "ultimaker3"
|
||||
else:
|
||||
self._printer_type = "unknown"
|
||||
|
||||
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs) -> None:
|
||||
raise NotImplementedError("requestWrite needs to be implemented")
|
||||
|
||||
@ -301,6 +309,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
def firmwareVersion(self) -> str:
|
||||
return self._properties.get(b"firmware_version", b"").decode("utf-8")
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def printerType(self) -> str:
|
||||
return self._printer_type
|
||||
|
||||
## IPadress of this printer
|
||||
@pyqtProperty(str, constant=True)
|
||||
def ipAddress(self) -> str:
|
||||
|
@ -1,9 +1,13 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from copy import deepcopy
|
||||
from typing import List
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from copy import deepcopy
|
||||
from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
|
||||
## Scene nodes that are models are only seen when selecting the corresponding build plate
|
||||
@ -11,6 +15,8 @@ from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
class CuraSceneNode(SceneNode):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if "no_setting_override" not in kwargs:
|
||||
self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||
self._outside_buildarea = False
|
||||
|
||||
def setOutsideBuildArea(self, new_value):
|
||||
@ -72,9 +78,34 @@ class CuraSceneNode(SceneNode):
|
||||
1.0
|
||||
]
|
||||
|
||||
## Return if the provided bbox collides with the bbox of this scene node
|
||||
def collidesWithBbox(self, check_bbox):
|
||||
bbox = self.getBoundingBox()
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
## Return if any area collides with the convex hull of this scene node
|
||||
def collidesWithArea(self, areas):
|
||||
convex_hull = self.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
if not convex_hull.isValid():
|
||||
return False
|
||||
|
||||
# Check for collisions between disallowed areas and the object
|
||||
for area in areas:
|
||||
overlap = convex_hull.intersectsPolygon(area)
|
||||
if overlap is None:
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
||||
## Taken from SceneNode, but replaced SceneNode with CuraSceneNode
|
||||
def __deepcopy__(self, memo):
|
||||
copy = CuraSceneNode()
|
||||
copy = CuraSceneNode(no_setting_override = True) # Setting override will be added later
|
||||
copy.setTransformation(self.getLocalTransformation())
|
||||
copy.setMeshData(self._mesh_data)
|
||||
copy.setVisible(deepcopy(self._visible, memo))
|
||||
|
@ -334,10 +334,13 @@ class ContainerManager(QObject):
|
||||
|
||||
# Go through global and extruder stacks and clear their topmost container (the user settings).
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
container = stack.getTop()
|
||||
container = stack.userChanges
|
||||
container.clear()
|
||||
send_emits_containers.append(container)
|
||||
|
||||
# user changes are possibly added to make the current setup match the current enabled extruders
|
||||
Application.getInstance().getMachineManager().correctExtruderSettings()
|
||||
|
||||
for container in send_emits_containers:
|
||||
container.sendPostponedEmits()
|
||||
|
||||
|
@ -273,11 +273,11 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
elif profile_index < len(machine_extruders) + 1:
|
||||
# This is assumed to be an extruder profile
|
||||
extruder_id = machine_extruders[profile_index - 1].definition.getId()
|
||||
extuder_position = str(profile_index - 1)
|
||||
extruder_position = str(profile_index - 1)
|
||||
if not profile.getMetaDataEntry("position"):
|
||||
profile.addMetaDataEntry("position", extuder_position)
|
||||
profile.addMetaDataEntry("position", extruder_position)
|
||||
else:
|
||||
profile.setMetaDataEntry("position", extuder_position)
|
||||
profile.setMetaDataEntry("position", extruder_position)
|
||||
profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")
|
||||
|
||||
else: #More extruders in the imported file than in the machine.
|
||||
|
@ -241,6 +241,13 @@ class ExtruderManager(QObject):
|
||||
result.append(extruder_stack.getProperty(setting_key, prop))
|
||||
return result
|
||||
|
||||
def extruderValueWithDefault(self, value):
|
||||
machine_manager = self._application.getMachineManager()
|
||||
if value == "-1":
|
||||
return machine_manager.defaultExtruderPosition
|
||||
else:
|
||||
return value
|
||||
|
||||
## Gets the extruder stacks that are actually being used at the moment.
|
||||
#
|
||||
# An extruder stack is being used if it is the extruder to print any mesh
|
||||
@ -252,7 +259,7 @@ class ExtruderManager(QObject):
|
||||
#
|
||||
# \return A list of extruder stacks.
|
||||
def getUsedExtruderStacks(self) -> List["ContainerStack"]:
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
used_extruder_stack_ids = set()
|
||||
@ -302,16 +309,19 @@ class ExtruderManager(QObject):
|
||||
|
||||
# Check support extruders
|
||||
if support_enabled:
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_infill_extruder_nr", "value"))])
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_extruder_nr_layer_0", "value"))])
|
||||
used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_infill_extruder_nr", "value")))])
|
||||
used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_extruder_nr_layer_0", "value")))])
|
||||
if support_bottom_enabled:
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_bottom_extruder_nr", "value"))])
|
||||
used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_bottom_extruder_nr", "value")))])
|
||||
if support_roof_enabled:
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_roof_extruder_nr", "value"))])
|
||||
used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_roof_extruder_nr", "value")))])
|
||||
|
||||
# The platform adhesion extruder. Not used if using none.
|
||||
if global_stack.getProperty("adhesion_type", "value") != "none":
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("adhesion_extruder_nr", "value"))])
|
||||
extruder_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
|
||||
if extruder_nr == "-1":
|
||||
extruder_nr = Application.getInstance().getMachineManager().defaultExtruderPosition
|
||||
used_extruder_stack_ids.add(self.extruderIds[extruder_nr])
|
||||
|
||||
try:
|
||||
return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids]
|
||||
@ -485,6 +495,8 @@ class ExtruderManager(QObject):
|
||||
|
||||
result = []
|
||||
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
|
||||
if not extruder.isEnabled:
|
||||
continue
|
||||
# only include values from extruders that are "active" for the current machine instance
|
||||
if int(extruder.getMetaDataEntry("position")) >= global_stack.getProperty("machine_extruder_count", "value"):
|
||||
continue
|
||||
@ -656,6 +668,8 @@ class ExtruderManager(QObject):
|
||||
# global stack if not found.
|
||||
@staticmethod
|
||||
def getExtruderValue(extruder_index, key):
|
||||
if extruder_index == -1:
|
||||
extruder_index = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
|
||||
|
||||
if extruder:
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
from typing import Any, TYPE_CHECKING, Optional
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Decorators import override
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
|
||||
from UM.Util import parseBool
|
||||
|
||||
from . import Exceptions
|
||||
from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
|
||||
@ -30,6 +31,8 @@ class ExtruderStack(CuraContainerStack):
|
||||
|
||||
self.propertiesChanged.connect(self._onPropertiesChanged)
|
||||
|
||||
enabledChanged = pyqtSignal()
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# This will set the next stack and ensure that we register this stack as an extruder.
|
||||
@ -46,6 +49,16 @@ class ExtruderStack(CuraContainerStack):
|
||||
def getNextStack(self) -> Optional["GlobalStack"]:
|
||||
return super().getNextStack()
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
if "enabled" not in self._metadata:
|
||||
self.addMetaDataEntry("enabled", "True")
|
||||
self.setMetaDataEntry("enabled", str(enabled))
|
||||
self.enabledChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = enabledChanged)
|
||||
def isEnabled(self):
|
||||
return parseBool(self.getMetaDataEntry("enabled", "True"))
|
||||
|
||||
@classmethod
|
||||
def getLoadingPriority(cls) -> int:
|
||||
return 3
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer
|
||||
from typing import Iterable
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
@ -24,6 +24,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
|
||||
## Human-readable name of the extruder.
|
||||
NameRole = Qt.UserRole + 2
|
||||
## Is the extruder enabled?
|
||||
EnabledRole = Qt.UserRole + 9
|
||||
|
||||
## Colour of the material loaded in the extruder.
|
||||
ColorRole = Qt.UserRole + 3
|
||||
@ -43,6 +45,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
|
||||
# The variant of the extruder.
|
||||
VariantRole = Qt.UserRole + 7
|
||||
StackRole = Qt.UserRole + 8
|
||||
|
||||
## List of colours to display if there is no material or the material has no known
|
||||
# colour.
|
||||
@ -57,11 +60,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.EnabledRole, "enabled")
|
||||
self.addRoleName(self.ColorRole, "color")
|
||||
self.addRoleName(self.IndexRole, "index")
|
||||
self.addRoleName(self.DefinitionRole, "definition")
|
||||
self.addRoleName(self.MaterialRole, "material")
|
||||
self.addRoleName(self.VariantRole, "variant")
|
||||
self.addRoleName(self.StackRole, "stack")
|
||||
|
||||
self._update_extruder_timer = QTimer()
|
||||
self._update_extruder_timer.setInterval(100)
|
||||
@ -183,11 +188,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||
item = {
|
||||
"id": extruder.getId(),
|
||||
"name": extruder.getName(),
|
||||
"enabled": extruder.isEnabled,
|
||||
"color": color,
|
||||
"index": position,
|
||||
"definition": extruder.getBottom().getId(),
|
||||
"material": extruder.material.getName() if extruder.material else "",
|
||||
"variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core
|
||||
"stack": extruder,
|
||||
}
|
||||
|
||||
items.append(item)
|
||||
|
@ -4,7 +4,7 @@
|
||||
import collections
|
||||
import time
|
||||
#Type hinting.
|
||||
from typing import Union, List, Dict, TYPE_CHECKING, Optional
|
||||
from typing import List, Dict, TYPE_CHECKING, Optional
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Signal import Signal
|
||||
@ -20,7 +20,6 @@ from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
|
||||
@ -52,12 +51,9 @@ class MachineManager(QObject):
|
||||
self._current_quality_group = None
|
||||
self._current_quality_changes_group = None
|
||||
|
||||
self.machine_extruder_material_update_dict = collections.defaultdict(list)
|
||||
self._default_extruder_position = "0" # to be updated when extruders are switched on and off
|
||||
|
||||
self._error_check_timer = QTimer()
|
||||
self._error_check_timer.setInterval(250)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
|
||||
self.machine_extruder_material_update_dict = collections.defaultdict(list)
|
||||
|
||||
self._instance_container_timer = QTimer()
|
||||
self._instance_container_timer.setInterval(250)
|
||||
@ -123,6 +119,7 @@ class MachineManager(QObject):
|
||||
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
|
||||
# be reflected on the GUI. This signal emission makes sure that it happens.
|
||||
self._material_manager.materialsUpdated.connect(self.rootMaterialChanged)
|
||||
self.rootMaterialChanged.connect(self._onRootMaterialChanged)
|
||||
|
||||
activeQualityGroupChanged = pyqtSignal()
|
||||
activeQualityChangesGroupChanged = pyqtSignal()
|
||||
@ -132,6 +129,7 @@ class MachineManager(QObject):
|
||||
activeVariantChanged = pyqtSignal()
|
||||
activeQualityChanged = pyqtSignal()
|
||||
activeStackChanged = pyqtSignal() # Emitted whenever the active stack is changed (ie: when changing between extruders, changing a profile, but not when changing a value)
|
||||
extruderChanged = pyqtSignal()
|
||||
|
||||
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
|
||||
activeStackValueChanged = pyqtSignal() # Emitted whenever a value inside the active stack is changed.
|
||||
@ -189,7 +187,9 @@ class MachineManager(QObject):
|
||||
|
||||
# Update the local global container stack reference
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._global_container_stack:
|
||||
self.updateDefaultExtruder()
|
||||
self.updateNumberExtrudersEnabled()
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
# after switching the global stack we reconnect all the signals and set the variant and material references
|
||||
@ -222,15 +222,6 @@ class MachineManager(QObject):
|
||||
del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self._error_check_timer.start()
|
||||
|
||||
## Update self._stacks_valid according to _checkStacksForErrors and emit if change.
|
||||
def _updateStacksHaveErrors(self) -> None:
|
||||
old_stacks_have_errors = self._stacks_have_errors
|
||||
self._stacks_have_errors = self._checkStacksHaveErrors()
|
||||
if old_stacks_have_errors != self._stacks_have_errors:
|
||||
self.stacksValidationChanged.emit()
|
||||
Application.getInstance().stacksValidationFinished.emit()
|
||||
|
||||
def _onActiveExtruderStackChanged(self) -> None:
|
||||
self.blurSettings.emit() # Ensure no-one has focus.
|
||||
@ -250,8 +241,6 @@ class MachineManager(QObject):
|
||||
|
||||
self.rootMaterialChanged.emit()
|
||||
|
||||
self._error_check_timer.start()
|
||||
|
||||
def _onInstanceContainersChanged(self, container) -> None:
|
||||
self._instance_container_timer.start()
|
||||
|
||||
@ -260,9 +249,6 @@ class MachineManager(QObject):
|
||||
# Notify UI items, such as the "changed" star in profile pull down menu.
|
||||
self.activeStackValueChanged.emit()
|
||||
|
||||
elif property_name == "validationState":
|
||||
self._error_check_timer.start()
|
||||
|
||||
## Given a global_stack, make sure that it's all valid by searching for this quality group and applying it again
|
||||
def _initMachineState(self, global_stack):
|
||||
material_dict = {}
|
||||
@ -329,7 +315,7 @@ class MachineManager(QObject):
|
||||
return False
|
||||
|
||||
if self._global_container_stack.hasErrors():
|
||||
Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start))
|
||||
Logger.log("d", "Checking global stack for errors took %0.2f s and we found an error" % (time.time() - time_start))
|
||||
return True
|
||||
|
||||
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
|
||||
@ -641,6 +627,8 @@ class MachineManager(QObject):
|
||||
buildplate_compatible = True # It is compatible by default
|
||||
extruder_stacks = self._global_container_stack.extruders.values()
|
||||
for stack in extruder_stacks:
|
||||
if not stack.isEnabled:
|
||||
continue
|
||||
material_container = stack.material
|
||||
if material_container == self._empty_material_container:
|
||||
continue
|
||||
@ -698,6 +686,43 @@ class MachineManager(QObject):
|
||||
if containers:
|
||||
return containers[0].definition.getId()
|
||||
|
||||
def getIncompatibleSettingsOnEnabledExtruders(self, container):
|
||||
extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||
result = []
|
||||
for setting_instance in container.findInstances():
|
||||
setting_key = setting_instance.definition.key
|
||||
setting_enabled = self._global_container_stack.getProperty(setting_key, "enabled")
|
||||
if not setting_enabled:
|
||||
# A setting is not visible anymore
|
||||
result.append(setting_key)
|
||||
Logger.log("d", "Reset setting [%s] from [%s] because the setting is no longer enabled", setting_key, container)
|
||||
continue
|
||||
|
||||
if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
|
||||
continue
|
||||
|
||||
old_value = container.getProperty(setting_key, "value")
|
||||
if int(old_value) >= extruder_count or not self._global_container_stack.extruders[str(old_value)].isEnabled:
|
||||
result.append(setting_key)
|
||||
Logger.log("d", "Reset setting [%s] in [%s] because its old value [%s] is no longer valid", setting_key, container, old_value)
|
||||
return result
|
||||
|
||||
## Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed
|
||||
def correctExtruderSettings(self):
|
||||
for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges):
|
||||
self._global_container_stack.userChanges.removeInstance(setting_key)
|
||||
add_user_changes = self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.qualityChanges)
|
||||
for setting_key in add_user_changes:
|
||||
# Apply quality changes that are incompatible to user changes, so we do not change the quality changes itself.
|
||||
self._global_container_stack.userChanges.setProperty(setting_key, "value", self._default_extruder_position)
|
||||
if add_user_changes:
|
||||
caution_message = Message(catalog.i18nc(
|
||||
"@info:generic",
|
||||
"Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)),
|
||||
lifetime=0,
|
||||
title = catalog.i18nc("@info:title", "Settings updated"))
|
||||
caution_message.show()
|
||||
|
||||
## Set the amount of extruders on the active machine (global stack)
|
||||
# \param extruder_count int the number of extruders to set
|
||||
def setActiveMachineExtruderCount(self, extruder_count):
|
||||
@ -711,16 +736,11 @@ class MachineManager(QObject):
|
||||
if extruder_count == previous_extruder_count:
|
||||
return
|
||||
|
||||
# reset all extruder number settings whose value is no longer valid
|
||||
for setting_instance in self._global_container_stack.userChanges.findInstances():
|
||||
setting_key = setting_instance.definition.key
|
||||
if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
|
||||
continue
|
||||
definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
|
||||
|
||||
old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value"))
|
||||
if old_value >= extruder_count:
|
||||
self._global_container_stack.userChanges.removeInstance(setting_key)
|
||||
Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value)
|
||||
self.updateDefaultExtruder()
|
||||
self.updateNumberExtrudersEnabled()
|
||||
self.correctExtruderSettings()
|
||||
|
||||
# Check to see if any objects are set to print with an extruder that will no longer exist
|
||||
root_node = Application.getInstance().getController().getScene().getRoot()
|
||||
@ -731,21 +751,19 @@ class MachineManager(QObject):
|
||||
if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
|
||||
node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())
|
||||
|
||||
definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
|
||||
|
||||
# Make sure one of the extruder stacks is active
|
||||
extruder_manager.setActiveExtruderIndex(0)
|
||||
|
||||
# Move settable_per_extruder values out of the global container
|
||||
# After CURA-4482 this should not be the case anymore, but we still want to support older project files.
|
||||
global_user_container = self._global_container_stack.getTop()
|
||||
global_user_container = self._global_container_stack.userChanges
|
||||
|
||||
# Make sure extruder_stacks exists
|
||||
extruder_stacks = []
|
||||
|
||||
if previous_extruder_count == 1:
|
||||
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
global_user_container = self._global_container_stack.getTop()
|
||||
global_user_container = self._global_container_stack.userChanges
|
||||
|
||||
for setting_instance in global_user_container.findInstances():
|
||||
setting_key = setting_instance.definition.key
|
||||
@ -754,11 +772,12 @@ class MachineManager(QObject):
|
||||
if settable_per_extruder:
|
||||
limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
|
||||
extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
|
||||
extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
|
||||
extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
|
||||
global_user_container.removeInstance(setting_key)
|
||||
|
||||
# Signal that the global stack has changed
|
||||
Application.getInstance().globalContainerStackChanged.emit()
|
||||
self.forceUpdateAllSettings()
|
||||
|
||||
@pyqtSlot(int, result = QObject)
|
||||
def getExtruder(self, position: int):
|
||||
@ -767,6 +786,54 @@ class MachineManager(QObject):
|
||||
extruder = self._global_container_stack.extruders.get(str(position))
|
||||
return extruder
|
||||
|
||||
def updateDefaultExtruder(self):
|
||||
extruder_items = sorted(self._global_container_stack.extruders.items())
|
||||
old_position = self._default_extruder_position
|
||||
new_default_position = "0"
|
||||
for position, extruder in extruder_items:
|
||||
if extruder.isEnabled:
|
||||
new_default_position = position
|
||||
break
|
||||
if new_default_position != old_position:
|
||||
self._default_extruder_position = new_default_position
|
||||
self.extruderChanged.emit()
|
||||
|
||||
def updateNumberExtrudersEnabled(self):
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
extruder_count = 0
|
||||
for position, extruder in self._global_container_stack.extruders.items():
|
||||
if extruder.isEnabled:
|
||||
extruder_count += 1
|
||||
definition_changes_container.setProperty("extruders_enabled_count", "value", extruder_count)
|
||||
|
||||
@pyqtProperty(str, notify = extruderChanged)
|
||||
def defaultExtruderPosition(self):
|
||||
return self._default_extruder_position
|
||||
|
||||
## This will fire the propertiesChanged for all settings so they will be updated in the front-end
|
||||
def forceUpdateAllSettings(self):
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
property_names = ["value", "resolve"]
|
||||
for setting_key in self._global_container_stack.getAllKeys():
|
||||
self._global_container_stack.propertiesChanged.emit(setting_key, property_names)
|
||||
|
||||
@pyqtSlot(int, bool)
|
||||
def setExtruderEnabled(self, position: int, enabled) -> None:
|
||||
extruder = self.getExtruder(position)
|
||||
extruder.setEnabled(enabled)
|
||||
self.updateDefaultExtruder()
|
||||
self.updateNumberExtrudersEnabled()
|
||||
self.correctExtruderSettings()
|
||||
# ensure that the quality profile is compatible with current combination, or choose a compatible one if available
|
||||
self._updateQualityWithMaterial()
|
||||
self.extruderChanged.emit()
|
||||
# update material compatibility color
|
||||
self.activeQualityGroupChanged.emit()
|
||||
# update items in SettingExtruder
|
||||
ExtruderManager.getInstance().extrudersChanged.emit(self._global_container_stack.getId())
|
||||
# Make sure the front end reflects changes
|
||||
self.forceUpdateAllSettings()
|
||||
|
||||
def _onMachineNameChanged(self):
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
@ -787,27 +854,32 @@ class MachineManager(QObject):
|
||||
container = extruder.userChanges
|
||||
container.setProperty(setting_name, property_name, property_value)
|
||||
|
||||
@pyqtProperty("QVariantList", notify = rootMaterialChanged)
|
||||
@pyqtProperty("QVariantList", notify = globalContainerChanged)
|
||||
def currentExtruderPositions(self):
|
||||
return sorted(list(self._current_root_material_id.keys()))
|
||||
if self._global_container_stack is None:
|
||||
return []
|
||||
return sorted(list(self._global_container_stack.extruders.keys()))
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialId(self):
|
||||
# initial filling the current_root_material_id
|
||||
## Update _current_root_material_id and _current_root_material_name when
|
||||
# the current root material was changed.
|
||||
def _onRootMaterialChanged(self):
|
||||
self._current_root_material_id = {}
|
||||
for position in self._global_container_stack.extruders:
|
||||
self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
|
||||
return self._current_root_material_id
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialName(self):
|
||||
# initial filling the current_root_material_name
|
||||
if self._global_container_stack:
|
||||
for position in self._global_container_stack.extruders:
|
||||
self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
|
||||
self._current_root_material_name = {}
|
||||
for position in self._global_container_stack.extruders:
|
||||
if position not in self._current_root_material_name:
|
||||
material = self._global_container_stack.extruders[position].material
|
||||
self._current_root_material_name[position] = material.getName()
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialId(self):
|
||||
return self._current_root_material_id
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialName(self):
|
||||
return self._current_root_material_name
|
||||
|
||||
## Return the variant names in the extruder stack(s).
|
||||
@ -854,9 +926,9 @@ class MachineManager(QObject):
|
||||
|
||||
# Set quality and quality_changes for each ExtruderStack
|
||||
for position, node in quality_group.nodes_for_extruders.items():
|
||||
self._global_container_stack.extruders[position].quality = node.getContainer()
|
||||
self._global_container_stack.extruders[str(position)].quality = node.getContainer()
|
||||
if empty_quality_changes:
|
||||
self._global_container_stack.extruders[position].qualityChanges = self._empty_quality_changes_container
|
||||
self._global_container_stack.extruders[str(position)].qualityChanges = self._empty_quality_changes_container
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
@ -921,6 +993,8 @@ class MachineManager(QObject):
|
||||
# check material - variant compatibility
|
||||
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
|
||||
for position, extruder in self._global_container_stack.extruders.items():
|
||||
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
if not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
return True
|
||||
|
@ -62,7 +62,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
|
||||
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
|
||||
# has not been updated yet.
|
||||
deep_copy._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
|
||||
deep_copy._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
|
||||
|
||||
return deep_copy
|
||||
|
||||
@ -90,10 +90,18 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
def isNonPrintingMesh(self):
|
||||
return self._is_non_printing_mesh
|
||||
|
||||
def evaluateIsNonPrintingMesh(self):
|
||||
return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
|
||||
|
||||
def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
|
||||
# Trigger slice/need slicing if the value has changed.
|
||||
if property_name == "value":
|
||||
self._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
|
||||
object_has_instance_setting = False
|
||||
for container in self._stack.getContainers():
|
||||
if container.hasProperty(instance, "value"):
|
||||
object_has_instance_setting = True
|
||||
break
|
||||
if property_name == "value" and object_has_instance_setting:
|
||||
# Trigger slice/need slicing if the value has changed.
|
||||
self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
|
||||
|
||||
Application.getInstance().getBackend().needsSlicing()
|
||||
Application.getInstance().getBackend().tickle()
|
||||
|
@ -10,7 +10,6 @@ from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
@ -32,6 +31,7 @@ import Arcus
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
backendError = Signal()
|
||||
@ -62,23 +62,26 @@ class CuraEngineBackend(QObject, Backend):
|
||||
default_engine_location = execpath
|
||||
break
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._multi_build_plate_model = None
|
||||
self._machine_error_checker = None
|
||||
|
||||
if not default_engine_location:
|
||||
raise EnvironmentError("Could not find CuraEngine")
|
||||
|
||||
Logger.log("i", "Found CuraEngine at: %s" %(default_engine_location))
|
||||
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
|
||||
|
||||
default_engine_location = os.path.abspath(default_engine_location)
|
||||
Preferences.getInstance().addPreference("backend/location", default_engine_location)
|
||||
|
||||
# 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().getMultiBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
self._onActiveViewChanged()
|
||||
|
||||
self._stored_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 = self._application.getController().getScene()
|
||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
|
||||
@ -86,18 +89,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||
# - whenever there is a value change, we start the timer
|
||||
# - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
|
||||
# auto-slicing timer when that error check is finished
|
||||
# If there is an error check, it will set the "_is_error_check_scheduled" flag, stop the auto-slicing timer,
|
||||
# and only wait for the error check to be finished to start the auto-slicing timer again.
|
||||
# If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
|
||||
# to start the auto-slicing timer again.
|
||||
#
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
|
||||
|
||||
# A flag indicating if an error check was scheduled
|
||||
# If so, we will stop the auto-slice timer and start upon the error check
|
||||
self._is_error_check_scheduled = False
|
||||
|
||||
# Listeners for receiving messages from the back-end.
|
||||
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
|
||||
@ -123,13 +118,6 @@ class CuraEngineBackend(QObject, Backend):
|
||||
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)
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
self._slice_start_time = None
|
||||
|
||||
Preferences.getInstance().addPreference("general/auto_slice", True)
|
||||
@ -144,6 +132,30 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self.determineAutoSlicing()
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._application.initializationFinished.connect(self.initialize)
|
||||
|
||||
def initialize(self):
|
||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||
|
||||
self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
# extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
|
||||
ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
self._application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
self._machine_error_checker = self._application.getMachineErrorChecker()
|
||||
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
||||
|
||||
## Terminate the engine process.
|
||||
#
|
||||
# This function should terminate the engine process.
|
||||
@ -529,11 +541,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
elif property == "validationState":
|
||||
if self._use_timer:
|
||||
self._is_error_check_scheduled = True
|
||||
self._change_timer.stop()
|
||||
|
||||
def _onStackErrorCheckFinished(self):
|
||||
self._is_error_check_scheduled = False
|
||||
if not self._slicing and self._build_plates_to_be_sliced:
|
||||
self.needsSlicing()
|
||||
self._onChanged()
|
||||
@ -559,12 +569,15 @@ 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:
|
||||
if self._machine_error_checker is None:
|
||||
self._change_timer.stop()
|
||||
return
|
||||
|
||||
if self._machine_error_checker.needToWaitForResult:
|
||||
self._change_timer.stop()
|
||||
else:
|
||||
self._change_timer.start()
|
||||
@ -630,7 +643,11 @@ class CuraEngineBackend(QObject, Backend):
|
||||
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:
|
||||
if self._machine_error_checker is None:
|
||||
self._change_timer.stop()
|
||||
return
|
||||
|
||||
if self._machine_error_checker.needToWaitForResult:
|
||||
self._change_timer.stop()
|
||||
else:
|
||||
self._change_timer.start()
|
||||
@ -782,3 +799,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
def tickle(self):
|
||||
if self._use_timer:
|
||||
self._change_timer.start()
|
||||
|
||||
def _extruderChanged(self):
|
||||
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
||||
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||
self._invokeSlice()
|
||||
|
@ -81,7 +81,8 @@ class ProcessSlicedLayersJob(Job):
|
||||
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
new_node = CuraSceneNode()
|
||||
# The no_setting_override is here because adding the SettingOverrideDecorator will trigger a reslice
|
||||
new_node = CuraSceneNode(no_setting_override = True)
|
||||
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
|
||||
|
||||
# Force garbage collection.
|
||||
|
@ -129,8 +129,10 @@ class StartSliceJob(Job):
|
||||
self.setResult(StartJobResult.MaterialIncompatible)
|
||||
return
|
||||
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
||||
for position, extruder_stack in stack.extruders.items():
|
||||
material = extruder_stack.findContainer({"type": "material"})
|
||||
if not extruder_stack.isEnabled:
|
||||
continue
|
||||
if material:
|
||||
if material.getMetaDataEntry("compatible") == False:
|
||||
self.setResult(StartJobResult.MaterialIncompatible)
|
||||
@ -189,11 +191,18 @@ 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:
|
||||
has_printing_mesh = True
|
||||
# Find a reason not to add the node
|
||||
if node.callDecoration("getBuildPlateNumber") != self._build_plate_number:
|
||||
continue
|
||||
if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh:
|
||||
continue
|
||||
node_position = node.callDecoration("getActiveExtruderPosition")
|
||||
if not stack.extruders[str(node_position)].isEnabled:
|
||||
continue
|
||||
|
||||
temp_list.append(node)
|
||||
if not is_non_printing_mesh:
|
||||
has_printing_mesh = True
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
@ -269,9 +278,15 @@ class StartSliceJob(Job):
|
||||
# \return A dictionary of replacement tokens to the values they should be
|
||||
# replaced with.
|
||||
def _buildReplacementTokens(self, stack) -> dict:
|
||||
default_extruder_position = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
result = {}
|
||||
for key in stack.getAllKeys():
|
||||
result[key] = stack.getProperty(key, "value")
|
||||
setting_type = stack.getProperty(key, "type")
|
||||
value = stack.getProperty(key, "value")
|
||||
if setting_type == "extruder" and value == -1:
|
||||
# replace with the default value
|
||||
value = default_extruder_position
|
||||
result[key] = value
|
||||
Job.yieldThread()
|
||||
|
||||
result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
|
||||
@ -377,11 +392,11 @@ class StartSliceJob(Job):
|
||||
# limit_to_extruder property.
|
||||
def _buildGlobalInheritsStackMessage(self, stack):
|
||||
for key in stack.getAllKeys():
|
||||
extruder = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
|
||||
if extruder >= 0: #Set to a specific extruder.
|
||||
extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
|
||||
if extruder_position >= 0: # Set to a specific extruder.
|
||||
setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder")
|
||||
setting_extruder.name = key
|
||||
setting_extruder.extruder = extruder
|
||||
setting_extruder.extruder = extruder_position
|
||||
Job.yieldThread()
|
||||
|
||||
## Check if a node has per object settings and ensure that they are set correctly in the message
|
||||
|
@ -78,17 +78,13 @@ class SolidView(View):
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
if node.getMeshData() and node.isVisible() and not node.callDecoration("getLayerData"):
|
||||
uniforms = {}
|
||||
shade_factor = 1.0
|
||||
|
||||
per_mesh_stack = node.callDecoration("getStack")
|
||||
|
||||
# Get color to render this mesh in from ExtrudersModel
|
||||
extruder_index = 0
|
||||
extruder_id = node.callDecoration("getActiveExtruder")
|
||||
if extruder_id:
|
||||
extruder_index = max(0, self._extruders_model.find("id", extruder_id))
|
||||
extruder_index = int(node.callDecoration("getActiveExtruderPosition"))
|
||||
|
||||
# Use the support extruder instead of the active extruder if this is a support_mesh
|
||||
if per_mesh_stack:
|
||||
|
@ -277,7 +277,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
|
||||
# Compatible is a special case, as it's added as a meta data entry (instead of an instance).
|
||||
material_container = variant_dict["material_container"]
|
||||
compatible = container.getMetaDataEntry("compatible")
|
||||
compatible = material_container.getMetaDataEntry("compatible")
|
||||
if compatible is not None:
|
||||
builder.start("setting", {"key": "hardware compatible"})
|
||||
if compatible:
|
||||
|
@ -211,6 +211,18 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"extruders_enabled_count":
|
||||
{
|
||||
"label": "Number of Extruders that are enabled",
|
||||
"description": "Number of extruder trains that are enabled; automatically set in software",
|
||||
"default_value": "machine_extruder_count",
|
||||
"minimum_value": "1",
|
||||
"maximum_value": "16",
|
||||
"type": "int",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_nozzle_tip_outer_diameter":
|
||||
{
|
||||
"label": "Outer nozzle diameter",
|
||||
@ -887,7 +899,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1",
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
"children": {
|
||||
"wall_0_extruder_nr":
|
||||
{
|
||||
@ -900,7 +912,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1"
|
||||
"enabled": "extruders_enabled_count > 1"
|
||||
},
|
||||
"wall_x_extruder_nr":
|
||||
{
|
||||
@ -913,7 +925,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1"
|
||||
"enabled": "extruders_enabled_count > 1"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -970,7 +982,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
|
||||
"enabled": "extruders_enabled_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
|
||||
},
|
||||
"roofing_layer_count":
|
||||
{
|
||||
@ -995,7 +1007,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1"
|
||||
"enabled": "extruders_enabled_count > 1"
|
||||
},
|
||||
"top_bottom_thickness":
|
||||
{
|
||||
@ -1465,7 +1477,7 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "machine_extruder_count > 1"
|
||||
"enabled": "extruders_enabled_count > 1"
|
||||
},
|
||||
"infill_sparse_density":
|
||||
{
|
||||
@ -1916,7 +1928,7 @@
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "10.0",
|
||||
"maximum_value": "machine_nozzle_heat_up_speed",
|
||||
"enabled": "material_flow_dependent_temperature or (machine_extruder_count > 1 and material_final_print_temperature != material_print_temperature)",
|
||||
"enabled": "material_flow_dependent_temperature or (extruders_enabled_count > 1 and material_final_print_temperature != material_print_temperature)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
@ -2179,7 +2191,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "260",
|
||||
"enabled": "machine_extruder_count > 1 and machine_nozzle_temp_enabled",
|
||||
"enabled": "extruders_enabled_count > 1 and machine_nozzle_temp_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
@ -3281,7 +3293,7 @@
|
||||
"description": "After the machine switched from one extruder to the other, the build plate is lowered to create clearance between the nozzle and the print. This prevents the nozzle from leaving oozed material on the outside of a print.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "retraction_hop_enabled and machine_extruder_count > 1",
|
||||
"enabled": "retraction_hop_enabled and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
@ -3459,7 +3471,8 @@
|
||||
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"value": "-1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children": {
|
||||
@ -3470,7 +3483,7 @@
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -3481,7 +3494,7 @@
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -3492,7 +3505,7 @@
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children":
|
||||
@ -3504,7 +3517,7 @@
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_interface_extruder_nr",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -3515,7 +3528,7 @@
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_interface_extruder_nr",
|
||||
"enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
@ -4185,7 +4198,8 @@
|
||||
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"enabled": "machine_extruder_count > 1 and resolveOrValue('adhesion_type') != 'none'",
|
||||
"value": "-1",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('adhesion_type') != 'none'",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -4756,7 +4770,7 @@
|
||||
"label": "Enable Prime Tower",
|
||||
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
|
||||
"type": "bool",
|
||||
"enabled": "machine_extruder_count > 1",
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
"default_value": false,
|
||||
"resolve": "any(extruderValues('prime_tower_enable'))",
|
||||
"settable_per_mesh": false,
|
||||
@ -4904,7 +4918,7 @@
|
||||
"description": "Enable exterior ooze shield. This will create a shell around the model which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
|
||||
"type": "bool",
|
||||
"resolve": "any(extruderValues('ooze_shield_enabled'))",
|
||||
"enabled": "machine_extruder_count > 1",
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
@ -4997,7 +5011,7 @@
|
||||
"description": "Remove areas where multiple meshes are overlapping with each other. This may be used if merged dual material objects overlap with each other.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"value": "machine_extruder_count > 1",
|
||||
"value": "extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true
|
||||
@ -5044,7 +5058,7 @@
|
||||
"one_at_a_time": "One at a Time"
|
||||
},
|
||||
"default_value": "all_at_once",
|
||||
"enabled": "machine_extruder_count == 1",
|
||||
"enabled": "extruders_enabled_count == 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
|
@ -43,10 +43,10 @@
|
||||
{
|
||||
"default_value":
|
||||
[
|
||||
[ -29, 6.1 ],
|
||||
[ -29, -33.9 ],
|
||||
[ 71, 6.1 ],
|
||||
[ 71, -33.9 ]
|
||||
[ -41.9, -45.8 ],
|
||||
[ -41.9, 33.9 ],
|
||||
[ 59.9, 33.9 ],
|
||||
[ 59.9, -45.8 ]
|
||||
]
|
||||
},
|
||||
"machine_gcode_flavor": { "default_value": "Griffin" },
|
||||
|
@ -194,14 +194,31 @@ UM.MainWindow
|
||||
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index }
|
||||
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index }
|
||||
|
||||
MenuSeparator {
|
||||
MenuSeparator
|
||||
{
|
||||
visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "Set as Active Extruder")
|
||||
onTriggered: Cura.ExtruderManager.setActiveExtruderIndex(model.index)
|
||||
onTriggered: Cura.MachineManager.setExtruderIndex(model.index)
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "Enable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true)
|
||||
visible: !Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "Disable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false)
|
||||
visible: Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
}
|
||||
|
||||
}
|
||||
onObjectAdded: settingsMenu.insertItem(index, object)
|
||||
onObjectRemoved: settingsMenu.removeItem(object)
|
||||
|
@ -19,7 +19,7 @@ Button
|
||||
iconSource: UM.Theme.getIcon("extruder_button")
|
||||
|
||||
checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1
|
||||
enabled: UM.Selection.hasSelection
|
||||
enabled: UM.Selection.hasSelection && extruder.stack.isEnabled
|
||||
|
||||
property color customColor: base.hovered ? UM.Theme.getColor("button_hover") : UM.Theme.getColor("button");
|
||||
|
||||
|
@ -19,14 +19,17 @@ Item
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
Cura.MaterialManagementModel {
|
||||
Cura.MaterialManagementModel
|
||||
{
|
||||
id: materialsModel
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
id: titleLabel
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@ -170,22 +173,27 @@ Item
|
||||
Connections
|
||||
{
|
||||
target: materialsModel
|
||||
onItemsChanged: {
|
||||
onItemsChanged:
|
||||
{
|
||||
var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id;
|
||||
var position = Cura.ExtruderManager.activeExtruderIndex;
|
||||
|
||||
// try to pick the currently selected item; it may have been moved
|
||||
if (base.newRootMaterialIdToSwitchTo == "") {
|
||||
if (base.newRootMaterialIdToSwitchTo == "")
|
||||
{
|
||||
base.newRootMaterialIdToSwitchTo = currentItemId;
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < materialsModel.rowCount(); ++idx) {
|
||||
for (var idx = 0; idx < materialsModel.rowCount(); ++idx)
|
||||
{
|
||||
var item = materialsModel.getItem(idx);
|
||||
if (item.root_material_id == base.newRootMaterialIdToSwitchTo) {
|
||||
if (item.root_material_id == base.newRootMaterialIdToSwitchTo)
|
||||
{
|
||||
// Switch to the newly created profile if needed
|
||||
materialListView.currentIndex = idx;
|
||||
materialListView.activateDetailsWithIndex(materialListView.currentIndex);
|
||||
if (base.toActivateNewMaterial) {
|
||||
if (base.toActivateNewMaterial)
|
||||
{
|
||||
Cura.MachineManager.setMaterial(position, item.container_node);
|
||||
}
|
||||
base.newRootMaterialIdToSwitchTo = "";
|
||||
@ -196,7 +204,8 @@ Item
|
||||
|
||||
materialListView.currentIndex = 0;
|
||||
materialListView.activateDetailsWithIndex(materialListView.currentIndex);
|
||||
if (base.toActivateNewMaterial) {
|
||||
if (base.toActivateNewMaterial)
|
||||
{
|
||||
Cura.MachineManager.setMaterial(position, materialsModel.getItem(0).container_node);
|
||||
}
|
||||
base.newRootMaterialIdToSwitchTo = "";
|
||||
@ -233,14 +242,17 @@ Item
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Import Material");
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Could not import material <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message);
|
||||
if (result.status == "success") {
|
||||
if (result.status == "success")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Information;
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Successfully imported material <filename>%1</filename>").arg(fileUrl);
|
||||
}
|
||||
else if (result.status == "duplicate") {
|
||||
else if (result.status == "duplicate")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Warning;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical;
|
||||
}
|
||||
messageDialog.open();
|
||||
@ -260,12 +272,14 @@ Item
|
||||
var result = Cura.ContainerManager.exportContainer(base.currentItem.root_material_id, selectedNameFilter, fileUrl);
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Export Material");
|
||||
if (result.status == "error") {
|
||||
if (result.status == "error")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical;
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags <filename> and <message>!", "Failed to export material to <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message);
|
||||
messageDialog.open();
|
||||
}
|
||||
else if (result.status == "success") {
|
||||
else if (result.status == "success")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Information;
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Successfully exported material to <filename>%1</filename>").arg(result.path);
|
||||
messageDialog.open();
|
||||
@ -283,7 +297,8 @@ Item
|
||||
Item {
|
||||
id: contentsItem
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: titleLabel.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@ -297,7 +312,8 @@ Item
|
||||
|
||||
Item
|
||||
{
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: buttonRow.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
left: parent.left
|
||||
@ -310,12 +326,14 @@ Item
|
||||
Label
|
||||
{
|
||||
id: captionLabel
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
visible: text != ""
|
||||
text: {
|
||||
text:
|
||||
{
|
||||
var caption = catalog.i18nc("@action:label", "Printer") + ": " + Cura.MachineManager.activeMachineName;
|
||||
if (Cura.MachineManager.hasVariants)
|
||||
{
|
||||
@ -330,14 +348,16 @@ Item
|
||||
ScrollView
|
||||
{
|
||||
id: materialScrollView
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: captionLabel.visible ? captionLabel.bottom : parent.top
|
||||
topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
parent: viewport
|
||||
anchors.fill: parent
|
||||
color: palette.light
|
||||
@ -418,13 +438,15 @@ Item
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
parent.ListView.view.currentIndex = model.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function activateDetailsWithIndex(index) {
|
||||
function activateDetailsWithIndex(index)
|
||||
{
|
||||
var model = materialsModel.getItem(index);
|
||||
base.currentItem = model;
|
||||
materialDetailsView.containerId = model.container_id;
|
||||
@ -446,7 +468,8 @@ Item
|
||||
{
|
||||
id: detailsPanel
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
left: materialScrollView.right
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
top: parent.top
|
||||
|
@ -11,7 +11,7 @@ Tab
|
||||
{
|
||||
id: base
|
||||
|
||||
property string extruderPosition: ""
|
||||
property int extruderPosition: -1 //Denotes the global stack.
|
||||
property var qualityItem: null
|
||||
|
||||
property bool isQualityItemCurrentlyActivated:
|
||||
|
@ -17,14 +17,39 @@ SettingItem
|
||||
id: control
|
||||
anchors.fill: parent
|
||||
|
||||
model: Cura.ExtrudersModel { onModelChanged: control.color = getItem(control.currentIndex).color }
|
||||
model: Cura.ExtrudersModel
|
||||
{
|
||||
onModelChanged: {
|
||||
control.color = getItem(control.currentIndex).color;
|
||||
}
|
||||
}
|
||||
|
||||
textRole: "name"
|
||||
|
||||
// knowing the extruder position, try to find the item index in the model
|
||||
function getIndexByPosition(position)
|
||||
{
|
||||
for (var item_index in model.items)
|
||||
{
|
||||
var item = model.getItem(item_index)
|
||||
if (item.index == position)
|
||||
{
|
||||
return item_index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
onActivated:
|
||||
{
|
||||
forceActiveFocus();
|
||||
propertyProvider.setPropertyValue("value", model.getItem(index).index);
|
||||
if (model.getItem(index).enabled)
|
||||
{
|
||||
forceActiveFocus();
|
||||
propertyProvider.setPropertyValue("value", model.getItem(index).index);
|
||||
} else
|
||||
{
|
||||
currentIndex = propertyProvider.properties.value; // keep the old value
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged:
|
||||
@ -64,6 +89,23 @@ SettingItem
|
||||
value: control.currentText != "" ? control.model.getItem(control.currentIndex).color : ""
|
||||
}
|
||||
|
||||
Binding
|
||||
{
|
||||
target: control
|
||||
property: "currentIndex"
|
||||
value:
|
||||
{
|
||||
if(propertyProvider.properties.value == -1)
|
||||
{
|
||||
return control.getIndexByPosition(Cura.MachineManager.defaultExtruderPosition);
|
||||
}
|
||||
return propertyProvider.properties.value
|
||||
}
|
||||
// Sometimes when the value is already changed, the model is still being built.
|
||||
// The when clause ensures that the current index is not updated when this happens.
|
||||
when: control.model.items.length > 0
|
||||
}
|
||||
|
||||
indicator: UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
@ -173,7 +215,13 @@ SettingItem
|
||||
{
|
||||
text: model.name
|
||||
renderType: Text.NativeRendering
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
color: {
|
||||
if (model.enabled) {
|
||||
UM.Theme.getColor("setting_control_text")
|
||||
} else {
|
||||
UM.Theme.getColor("action_button_disabled_text");
|
||||
}
|
||||
}
|
||||
font: UM.Theme.getFont("default")
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
@ -374,8 +374,6 @@ Item
|
||||
key: model.key ? model.key : ""
|
||||
watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
|
||||
storeIndex: 0
|
||||
// Due to the way setPropertyValue works, removeUnusedValue gives the correct output in case of resolve
|
||||
removeUnusedValue: model.resolve == undefined
|
||||
}
|
||||
|
||||
Connections
|
||||
|
@ -91,10 +91,42 @@ Column
|
||||
exclusiveGroup: extruderMenuGroup
|
||||
checked: base.currentExtruderIndex == index
|
||||
|
||||
onClicked:
|
||||
property bool extruder_enabled: true
|
||||
|
||||
MouseArea
|
||||
{
|
||||
forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values.
|
||||
Cura.ExtruderManager.setActiveExtruderIndex(index);
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
switch (mouse.button) {
|
||||
case Qt.LeftButton:
|
||||
forceActiveFocus(); // Changing focus applies the currently-being-typed values so it can change the displayed setting values.
|
||||
Cura.ExtruderManager.setActiveExtruderIndex(index);
|
||||
break;
|
||||
case Qt.RightButton:
|
||||
extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
extruderMenu.popup();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Menu
|
||||
{
|
||||
id: extruderMenu
|
||||
|
||||
MenuItem {
|
||||
text: catalog.i18nc("@action:inmenu", "Enable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true)
|
||||
visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: catalog.i18nc("@action:inmenu", "Disable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false)
|
||||
visible: extruder_enabled
|
||||
}
|
||||
}
|
||||
|
||||
style: ButtonStyle
|
||||
@ -114,6 +146,18 @@ Column
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
}
|
||||
|
||||
function buttonColor(index) {
|
||||
var extruder = Cura.MachineManager.getExtruder(index);
|
||||
if (extruder.isEnabled) {
|
||||
return (
|
||||
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");
|
||||
} else {
|
||||
return UM.Theme.getColor("action_button_disabled_text");
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: extruderButtonFace
|
||||
@ -131,9 +175,7 @@ Column
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
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")
|
||||
color: buttonColor(index)
|
||||
|
||||
font: UM.Theme.getFont("large_nonbold")
|
||||
text: catalog.i18nc("@label", "Extruder")
|
||||
@ -176,9 +218,7 @@ Column
|
||||
id: extruderNumberText
|
||||
anchors.centerIn: parent
|
||||
text: index + 1;
|
||||
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")
|
||||
color: buttonColor(index)
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ Item
|
||||
property Action configureSettings;
|
||||
property variant minimumPrintTime: PrintInformation.minimumPrintTime;
|
||||
property variant maximumPrintTime: PrintInformation.maximumPrintTime;
|
||||
property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || machineExtruderCount.properties.value == 1
|
||||
property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1
|
||||
|
||||
Component.onCompleted: PrintInformation.enabled = true
|
||||
Component.onDestruction: PrintInformation.enabled = false
|
||||
@ -67,10 +67,8 @@ Item
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onActiveQualityChanged: qualityModel.update()
|
||||
onActiveMaterialChanged: qualityModel.update()
|
||||
onActiveVariantChanged: qualityModel.update()
|
||||
target: Cura.QualityProfilesDropDownMenuModel
|
||||
onItemsChanged: qualityModel.update()
|
||||
}
|
||||
|
||||
Connections {
|
||||
@ -518,7 +516,12 @@ Item
|
||||
// Update the slider value to represent the rounded value
|
||||
infillSlider.value = roundedSliderValue
|
||||
|
||||
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
|
||||
// Update value only if the Recomended mode is Active,
|
||||
// Otherwise if I change the value in the Custom mode the Recomended view will try to repeat
|
||||
// same operation
|
||||
if (UM.Preferences.getValue("cura/active_mode") == 0) {
|
||||
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
|
||||
}
|
||||
}
|
||||
|
||||
style: SliderStyle
|
||||
@ -788,25 +791,10 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: supportExtruderLabel
|
||||
visible: supportExtruderCombobox.visible
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.right: infillCellLeft.right
|
||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.verticalCenter: supportExtruderCombobox.verticalCenter
|
||||
text: catalog.i18nc("@label", "Support Extruder");
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: supportExtruderCombobox
|
||||
visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)
|
||||
visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1)
|
||||
model: extruderModel
|
||||
|
||||
property string color_override: "" // for manually setting values
|
||||
@ -820,11 +808,12 @@ Item
|
||||
|
||||
textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started
|
||||
|
||||
anchors.top: enableSupportCheckBox.bottom
|
||||
anchors.topMargin: ((supportEnabled.properties.value === "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("sidebar_margin").height : 0
|
||||
anchors.left: infillCellRight.left
|
||||
anchors.top: enableSupportCheckBox.top
|
||||
//anchors.topMargin: ((supportEnabled.properties.value === "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("sidebar_margin").height : 0
|
||||
anchors.left: enableSupportCheckBox.right
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width / 2)
|
||||
|
||||
width: Math.round(UM.Theme.getSize("sidebar").width * .55)
|
||||
width: Math.round(UM.Theme.getSize("sidebar").width * .55) - Math.round(UM.Theme.getSize("sidebar_margin").width / 2) - enableSupportCheckBox.width
|
||||
height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0
|
||||
|
||||
Behavior on height { NumberAnimation { duration: 100 } }
|
||||
@ -891,7 +880,7 @@ Item
|
||||
id: adhesionCheckBox
|
||||
property alias _hovered: adhesionMouseArea.containsMouse
|
||||
|
||||
anchors.top: enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom
|
||||
anchors.top: enableSupportCheckBox.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
anchors.left: infillCellRight.left
|
||||
|
||||
@ -1022,9 +1011,9 @@ Item
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: machineExtruderCount
|
||||
id: extrudersEnabledCount
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
key: "machine_extruder_count"
|
||||
key: "extruders_enabled_count"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user