Cura/cura/Settings/SettingInheritanceManager.py
Jaime van Kessel e17eda9a33 Removed debug prints
CURA-2860
2016-11-22 15:25:54 +01:00

214 lines
10 KiB
Python

# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
import UM.Settings
from UM.Application import Application
import cura.Settings
from UM.Logger import Logger
## The settingInheritance manager is responsible for checking each setting in order to see if one of the "deeper"
# containers has a setting function and the topmost one with a value has a value. We need to have this check
# because some profiles tend to have 'hardcoded' values that break our inheritance. A good example of that are the
# speed settings. If all the children of print_speed have a single value override, changing the speed won't
# actually do anything, as only the 'leaf' settings are used by the engine.
class SettingInheritanceManager(QObject):
def __init__(self, parent = None):
super().__init__(parent)
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._global_container_stack = None
self._settings_with_inheritance_warning = []
self._active_container_stack = None
self._onGlobalContainerChanged()
cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
self._onActiveExtruderChanged()
settingsWithIntheritanceChanged = pyqtSignal()
## Get the keys of all children settings with an override.
@pyqtSlot(str, result = "QStringList")
def getChildrenKeysWithOverride(self, key):
definitions = self._global_container_stack.getBottom().findDefinitions(key=key)
if not definitions:
return
result = []
for key in definitions[0].getAllKeys():
if key in self._settings_with_inheritance_warning:
result.append(key)
return result
@pyqtSlot(str, str, result = "QStringList")
def getOverridesForExtruder(self, key, extruder_index):
multi_extrusion = self._global_container_stack.getProperty("machine_extruder_count", "value") > 1
if not multi_extrusion:
return self._settings_with_inheritance_warning
extruder = cura.Settings.ExtruderManager.getInstance().getExtruderStack(extruder_index)
if not extruder:
Logger.log("w", "Unable to find extruder for current machine with index %s", extruder_index)
return []
definitions = self._global_container_stack.getBottom().findDefinitions(key=key)
if not definitions:
return
result = []
for key in definitions[0].getAllKeys():
if self._settingIsOverwritingInheritance(key, extruder):
result.append(key)
return result
@pyqtSlot(str)
def manualRemoveOverride(self, key):
if key in self._settings_with_inheritance_warning:
self._settings_with_inheritance_warning.remove(key)
self.settingsWithIntheritanceChanged.emit()
@pyqtSlot()
def forceUpdate(self):
self._update()
def _onActiveExtruderChanged(self):
new_active_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack()
if not new_active_stack:
new_active_stack = self._global_container_stack
if new_active_stack != self._active_container_stack: # Check if changed
if self._active_container_stack: # Disconnect signal from old container (if any)
self._active_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
self._active_container_stack.containersChanged.disconnect(self._onContainersChanged)
self._active_container_stack = new_active_stack
self._active_container_stack.propertyChanged.connect(self._onPropertyChanged)
self._active_container_stack.containersChanged.connect(self._onContainersChanged)
self._update() # Ensure that the settings_with_inheritance_warning list is populated.
def _onPropertyChanged(self, key, property_name):
if property_name == "value" and self._global_container_stack:
definitions = self._global_container_stack.getBottom().findDefinitions(key = key)
if not definitions:
return
has_overwritten_inheritance = self._settingIsOverwritingInheritance(key)
settings_with_inheritance_warning_changed = False
# Check if the setting needs to be in the list.
if key not in self._settings_with_inheritance_warning and has_overwritten_inheritance:
self._settings_with_inheritance_warning.append(key)
settings_with_inheritance_warning_changed = True
elif key in self._settings_with_inheritance_warning and not has_overwritten_inheritance:
self._settings_with_inheritance_warning.remove(key)
settings_with_inheritance_warning_changed = True
# Find the topmost parent (Assumed to be a category)
parent = definitions[0].parent
while parent.parent is not None:
parent = parent.parent
if parent.key not in self._settings_with_inheritance_warning and has_overwritten_inheritance:
# Category was not in the list yet, so needs to be added now.
self._settings_with_inheritance_warning.append(parent.key)
settings_with_inheritance_warning_changed = True
elif parent.key in self._settings_with_inheritance_warning and not has_overwritten_inheritance:
# Category was in the list and one of it's settings is not overwritten.
if not self._recursiveCheck(parent): # Check if any of it's children have overwritten inheritance.
self._settings_with_inheritance_warning.remove(parent.key)
settings_with_inheritance_warning_changed = True
# Emit the signal if there was any change to the list.
if settings_with_inheritance_warning_changed:
self.settingsWithIntheritanceChanged.emit()
def _recursiveCheck(self, definition):
for child in definition.children:
if child.key in self._settings_with_inheritance_warning:
return True
if child.children:
if self._recursiveCheck(child):
return True
return False
@pyqtProperty("QVariantList", notify = settingsWithIntheritanceChanged)
def settingsWithInheritanceWarning(self):
return self._settings_with_inheritance_warning
## Check if a setting has an inheritance function that is overwritten
def _settingIsOverwritingInheritance(self, key, stack = None):
has_setting_function = False
if not stack:
stack = self._active_container_stack
containers = []
## Check if the setting has a user state. If not, it is never overwritten.
has_user_state = stack.getProperty(key, "state") == UM.Settings.InstanceState.User
if not has_user_state:
return False
## If a setting is not enabled, don't label it as overwritten (It's never visible anyway).
if not stack.getProperty(key, "enabled"):
return False
## Also check if the top container is not a setting function (this happens if the inheritance is restored).
if isinstance(stack.getTop().getProperty(key, "value"), UM.Settings.SettingFunction):
return False
## Mash all containers for all the stacks together.
while stack:
containers.extend(stack.getContainers())
stack = stack.getNextStack()
has_non_function_value = False
for container in containers:
try:
value = container.getProperty(key, "value")
except AttributeError:
continue
if value is not None:
# If a setting doesn't use any keys, it won't change it's value, so treat it as if it's a fixed value
has_setting_function = isinstance(value, UM.Settings.SettingFunction) and len(value.getUsedSettingKeys()) > 0
if has_setting_function is False:
has_non_function_value = True
continue
if has_setting_function:
break # There is a setting function somewhere, stop looking deeper.
return has_setting_function and has_non_function_value
def _update(self):
self._settings_with_inheritance_warning = [] # Reset previous data.
# Check all setting keys that we know of and see if they are overridden.
for setting_key in self._global_container_stack.getAllKeys():
override = self._settingIsOverwritingInheritance(setting_key)
if override:
self._settings_with_inheritance_warning.append(setting_key)
# Check all the categories if any of their children have their inheritance overwritten.
for category in self._global_container_stack.getBottom().findDefinitions(type = "category"):
if self._recursiveCheck(category):
self._settings_with_inheritance_warning.append(category.key)
# Notify others that things have changed.
self.settingsWithIntheritanceChanged.emit()
def _onGlobalContainerChanged(self):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
self._global_container_stack.containersChanged.disconnect(self._onContainersChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
if self._global_container_stack:
self._global_container_stack.containersChanged.connect(self._onContainersChanged)
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
self._onActiveExtruderChanged()
def _onContainersChanged(self, container):
# TODO: Multiple container changes in sequence now cause quite a few recalculations.
# This isn't that big of an issue, but it could be in the future.
self._update()
@staticmethod
def createSettingInheritanceManager(engine=None, script_engine=None):
return SettingInheritanceManager()