mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 06:39:04 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura
This commit is contained in:
commit
335d3d4329
@ -3,30 +3,26 @@
|
|||||||
from cura.Backups.BackupsManager import BackupsManager
|
from cura.Backups.BackupsManager import BackupsManager
|
||||||
|
|
||||||
|
|
||||||
|
## The back-ups API provides a version-proof bridge between Cura's
|
||||||
|
# BackupManager and plug-ins that hook into it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ``from cura.API import CuraAPI
|
||||||
|
# api = CuraAPI()
|
||||||
|
# api.backups.createBackup()
|
||||||
|
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
|
||||||
class Backups:
|
class Backups:
|
||||||
"""
|
|
||||||
The backups API provides a version-proof bridge between Cura's BackupManager and plugins that hook into it.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
from cura.API import CuraAPI
|
|
||||||
api = CuraAPI()
|
|
||||||
api.backups.createBackup()
|
|
||||||
api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})
|
|
||||||
"""
|
|
||||||
|
|
||||||
manager = BackupsManager() # Re-used instance of the backups manager.
|
manager = BackupsManager() # Re-used instance of the backups manager.
|
||||||
|
|
||||||
|
## Create a new back-up using the BackupsManager.
|
||||||
|
# \return Tuple containing a ZIP file with the back-up data and a dict
|
||||||
|
# with metadata about the back-up.
|
||||||
def createBackup(self) -> (bytes, dict):
|
def createBackup(self) -> (bytes, dict):
|
||||||
"""
|
|
||||||
Create a new backup using the BackupsManager.
|
|
||||||
:return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup.
|
|
||||||
"""
|
|
||||||
return self.manager.createBackup()
|
return self.manager.createBackup()
|
||||||
|
|
||||||
|
## Restore a back-up using the BackupsManager.
|
||||||
|
# \param zip_file A ZIP file containing the actual back-up data.
|
||||||
|
# \param meta_data Some metadata needed for restoring a back-up, like the
|
||||||
|
# Cura version number.
|
||||||
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
|
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
|
||||||
"""
|
|
||||||
Restore a backup using the BackupManager.
|
|
||||||
:param zip_file: A ZIP file containing the actual backup data.
|
|
||||||
:param meta_data: Some meta data needed for restoring a backup, like the Cura version number.
|
|
||||||
"""
|
|
||||||
return self.manager.restoreBackup(zip_file, meta_data)
|
return self.manager.restoreBackup(zip_file, meta_data)
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
from UM.PluginRegistry import PluginRegistry
|
from UM.PluginRegistry import PluginRegistry
|
||||||
from cura.API.Backups import Backups
|
from cura.API.Backups import Backups
|
||||||
|
|
||||||
|
## The official Cura API that plug-ins can use to interact with Cura.
|
||||||
|
#
|
||||||
|
# Python does not technically prevent talking to other classes as well, but
|
||||||
|
# this API provides a version-safe interface with proper deprecation warnings
|
||||||
|
# etc. Usage of any other methods than the ones provided in this API can cause
|
||||||
|
# plug-ins to be unstable.
|
||||||
class CuraAPI:
|
class CuraAPI:
|
||||||
"""
|
|
||||||
The official Cura API that plugins can use to interact with Cura.
|
|
||||||
Python does not technically prevent talking to other classes as well,
|
|
||||||
but this API provides a version-safe interface with proper deprecation warnings etc.
|
|
||||||
Usage of any other methods than the ones provided in this API can cause plugins to be unstable.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# For now we use the same API version to be consistent.
|
# For now we use the same API version to be consistent.
|
||||||
VERSION = PluginRegistry.APIVersion
|
VERSION = PluginRegistry.APIVersion
|
||||||
|
@ -17,12 +17,11 @@ from UM.Resources import Resources
|
|||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
|
## The back-up class holds all data about a back-up.
|
||||||
|
#
|
||||||
|
# It is also responsible for reading and writing the zip file to the user data
|
||||||
|
# folder.
|
||||||
class Backup:
|
class Backup:
|
||||||
"""
|
|
||||||
The backup class holds all data about a backup.
|
|
||||||
It is also responsible for reading and writing the zip file to the user data folder.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# These files should be ignored when making a backup.
|
# These files should be ignored when making a backup.
|
||||||
IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
|
IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
|
||||||
|
|
||||||
@ -33,10 +32,8 @@ class Backup:
|
|||||||
self.zip_file = zip_file # type: Optional[bytes]
|
self.zip_file = zip_file # type: Optional[bytes]
|
||||||
self.meta_data = meta_data # type: Optional[dict]
|
self.meta_data = meta_data # type: Optional[dict]
|
||||||
|
|
||||||
|
## Create a back-up from the current user config folder.
|
||||||
def makeFromCurrent(self) -> (bool, Optional[str]):
|
def makeFromCurrent(self) -> (bool, Optional[str]):
|
||||||
"""
|
|
||||||
Create a backup from the current user config folder.
|
|
||||||
"""
|
|
||||||
cura_release = CuraApplication.getInstance().getVersion()
|
cura_release = CuraApplication.getInstance().getVersion()
|
||||||
version_data_dir = Resources.getDataStoragePath()
|
version_data_dir = Resources.getDataStoragePath()
|
||||||
|
|
||||||
@ -75,12 +72,10 @@ class Backup:
|
|||||||
"plugin_count": str(plugin_count)
|
"plugin_count": str(plugin_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## Make a full archive from the given root path with the given name.
|
||||||
|
# \param root_path The root directory to archive recursively.
|
||||||
|
# \return The archive as bytes.
|
||||||
def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]:
|
def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]:
|
||||||
"""
|
|
||||||
Make a full archive from the given root path with the given name.
|
|
||||||
:param root_path: The root directory to archive recursively.
|
|
||||||
:return: The archive as bytes.
|
|
||||||
"""
|
|
||||||
ignore_string = re.compile("|".join(self.IGNORED_FILES))
|
ignore_string = re.compile("|".join(self.IGNORED_FILES))
|
||||||
try:
|
try:
|
||||||
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
||||||
@ -99,15 +94,13 @@ class Backup:
|
|||||||
"Could not create archive from user data directory: {}".format(error)))
|
"Could not create archive from user data directory: {}".format(error)))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
## Show a UI message.
|
||||||
def _showMessage(self, message: str) -> None:
|
def _showMessage(self, message: str) -> None:
|
||||||
"""Show a UI message"""
|
|
||||||
Message(message, title=self.catalog.i18nc("@info:title", "Backup"), lifetime=30).show()
|
Message(message, title=self.catalog.i18nc("@info:title", "Backup"), lifetime=30).show()
|
||||||
|
|
||||||
|
## Restore this back-up.
|
||||||
|
# \return Whether we had success or not.
|
||||||
def restore(self) -> bool:
|
def restore(self) -> bool:
|
||||||
"""
|
|
||||||
Restore this backups
|
|
||||||
:return: A boolean whether we had success or not.
|
|
||||||
"""
|
|
||||||
if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None):
|
if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None):
|
||||||
# We can restore without the minimum required information.
|
# We can restore without the minimum required information.
|
||||||
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
||||||
@ -140,14 +133,12 @@ class Backup:
|
|||||||
|
|
||||||
return extracted
|
return extracted
|
||||||
|
|
||||||
|
## Extract the whole archive to the given target path.
|
||||||
|
# \param archive The archive as ZipFile.
|
||||||
|
# \param target_path The target path.
|
||||||
|
# \return Whether we had success or not.
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extractArchive(archive: "ZipFile", target_path: str) -> bool:
|
def _extractArchive(archive: "ZipFile", target_path: str) -> bool:
|
||||||
"""
|
|
||||||
Extract the whole archive to the given target path.
|
|
||||||
:param archive: The archive as ZipFile.
|
|
||||||
:param target_path: The target path.
|
|
||||||
:return: A boolean whether we had success or not.
|
|
||||||
"""
|
|
||||||
Logger.log("d", "Removing current data in location: %s", target_path)
|
Logger.log("d", "Removing current data in location: %s", target_path)
|
||||||
Resources.factoryReset()
|
Resources.factoryReset()
|
||||||
Logger.log("d", "Extracting backup to location: %s", target_path)
|
Logger.log("d", "Extracting backup to location: %s", target_path)
|
||||||
|
@ -7,19 +7,18 @@ from cura.Backups.Backup import Backup
|
|||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
|
## The BackupsManager is responsible for managing the creating and restoring of
|
||||||
|
# back-ups.
|
||||||
|
#
|
||||||
|
# Back-ups themselves are represented in a different class.
|
||||||
class BackupsManager:
|
class BackupsManager:
|
||||||
"""
|
|
||||||
The BackupsManager is responsible for managing the creating and restoring of backups.
|
|
||||||
Backups themselves are represented in a different class.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._application = CuraApplication.getInstance()
|
self._application = CuraApplication.getInstance()
|
||||||
|
|
||||||
|
## Get a back-up of the current configuration.
|
||||||
|
# \return A tuple containing a ZipFile (the actual back-up) and a dict
|
||||||
|
# containing some metadata (like version).
|
||||||
def createBackup(self) -> (Optional[bytes], Optional[dict]):
|
def createBackup(self) -> (Optional[bytes], Optional[dict]):
|
||||||
"""
|
|
||||||
Get a backup of the current configuration.
|
|
||||||
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version).
|
|
||||||
"""
|
|
||||||
self._disableAutoSave()
|
self._disableAutoSave()
|
||||||
backup = Backup()
|
backup = Backup()
|
||||||
backup.makeFromCurrent()
|
backup.makeFromCurrent()
|
||||||
@ -27,12 +26,11 @@ class BackupsManager:
|
|||||||
# We don't return a Backup here because we want plugins only to interact with our API and not full objects.
|
# We don't return a Backup here because we want plugins only to interact with our API and not full objects.
|
||||||
return backup.zip_file, backup.meta_data
|
return backup.zip_file, backup.meta_data
|
||||||
|
|
||||||
|
## Restore a back-up from a given ZipFile.
|
||||||
|
# \param zip_file A bytes object containing the actual back-up.
|
||||||
|
# \param meta_data A dict containing some metadata that is needed to
|
||||||
|
# restore the back-up correctly.
|
||||||
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
|
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
|
||||||
"""
|
|
||||||
Restore a backup from a given ZipFile.
|
|
||||||
:param zip_file: A bytes object containing the actual backup.
|
|
||||||
:param meta_data: A dict containing some meta data that is needed to restore the backup correctly.
|
|
||||||
"""
|
|
||||||
if not meta_data.get("cura_release", None):
|
if not meta_data.get("cura_release", None):
|
||||||
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
|
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
|
||||||
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
|
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
|
||||||
@ -47,10 +45,11 @@ class BackupsManager:
|
|||||||
# We don't want to store the data at this point as that would override the just-restored backup.
|
# We don't want to store the data at this point as that would override the just-restored backup.
|
||||||
self._application.windowClosed(save_data=False)
|
self._application.windowClosed(save_data=False)
|
||||||
|
|
||||||
|
## Here we try to disable the auto-save plug-in as it might interfere with
|
||||||
|
# restoring a back-up.
|
||||||
def _disableAutoSave(self):
|
def _disableAutoSave(self):
|
||||||
"""Here we try to disable the auto-save plugin as it might interfere with restoring a backup."""
|
|
||||||
self._application.setSaveDataEnabled(False)
|
self._application.setSaveDataEnabled(False)
|
||||||
|
|
||||||
|
## Re-enable auto-save after we're done.
|
||||||
def _enableAutoSave(self):
|
def _enableAutoSave(self):
|
||||||
"""Re-enable auto-save after we're done."""
|
|
||||||
self._application.setSaveDataEnabled(True)
|
self._application.setSaveDataEnabled(True)
|
||||||
|
@ -8,6 +8,7 @@ from UM.Scene.SceneNode import SceneNode
|
|||||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
|
from UM.Scene.SceneNodeSettings import SceneNodeSettings
|
||||||
|
|
||||||
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
|
||||||
@ -80,6 +81,10 @@ class PlatformPhysics:
|
|||||||
|
|
||||||
# only push away objects if this node is a printing mesh
|
# only push away objects if this node is a printing mesh
|
||||||
if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"):
|
if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"):
|
||||||
|
# Do not move locked nodes
|
||||||
|
if node.getSetting(SceneNodeSettings.LockPosition):
|
||||||
|
continue
|
||||||
|
|
||||||
# Check for collisions between convex hulls
|
# Check for collisions between convex hulls
|
||||||
for other_node in BreadthFirstIterator(root):
|
for other_node in BreadthFirstIterator(root):
|
||||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||||
|
@ -306,6 +306,11 @@ class MachineManager(QObject):
|
|||||||
for position, extruder in global_stack.extruders.items():
|
for position, extruder in global_stack.extruders.items():
|
||||||
material_dict[position] = extruder.material.getMetaDataEntry("base_file")
|
material_dict[position] = extruder.material.getMetaDataEntry("base_file")
|
||||||
self._current_root_material_id = material_dict
|
self._current_root_material_id = material_dict
|
||||||
|
|
||||||
|
# Update materials to make sure that the diameters match with the machine's
|
||||||
|
for position in global_stack.extruders:
|
||||||
|
self.updateMaterialWithVariant(position)
|
||||||
|
|
||||||
global_quality = global_stack.quality
|
global_quality = global_stack.quality
|
||||||
quality_type = global_quality.getMetaDataEntry("quality_type")
|
quality_type = global_quality.getMetaDataEntry("quality_type")
|
||||||
global_quality_changes = global_stack.qualityChanges
|
global_quality_changes = global_stack.qualityChanges
|
||||||
@ -1200,7 +1205,7 @@ class MachineManager(QObject):
|
|||||||
current_quality_type, quality_type)
|
current_quality_type, quality_type)
|
||||||
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
|
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
|
||||||
|
|
||||||
def _updateMaterialWithVariant(self, position: Optional[str]):
|
def updateMaterialWithVariant(self, position: Optional[str]):
|
||||||
if self._global_container_stack is None:
|
if self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
if position is None:
|
if position is None:
|
||||||
@ -1286,7 +1291,7 @@ class MachineManager(QObject):
|
|||||||
self._setMaterial(position, material_container_node)
|
self._setMaterial(position, material_container_node)
|
||||||
else:
|
else:
|
||||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||||
self._updateMaterialWithVariant(position)
|
self.updateMaterialWithVariant(position)
|
||||||
|
|
||||||
if configuration.buildplateConfiguration is not None:
|
if configuration.buildplateConfiguration is not None:
|
||||||
global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
|
global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
|
||||||
@ -1332,7 +1337,7 @@ class MachineManager(QObject):
|
|||||||
self.blurSettings.emit()
|
self.blurSettings.emit()
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
self._setGlobalVariant(container_node)
|
self._setGlobalVariant(container_node)
|
||||||
self._updateMaterialWithVariant(None) # Update all materials
|
self.updateMaterialWithVariant(None) # Update all materials
|
||||||
self._updateQualityWithMaterial()
|
self._updateQualityWithMaterial()
|
||||||
|
|
||||||
@pyqtSlot(str, str)
|
@pyqtSlot(str, str)
|
||||||
@ -1369,7 +1374,7 @@ class MachineManager(QObject):
|
|||||||
self.blurSettings.emit()
|
self.blurSettings.emit()
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
self._setVariantNode(position, container_node)
|
self._setVariantNode(position, container_node)
|
||||||
self._updateMaterialWithVariant(position)
|
self.updateMaterialWithVariant(position)
|
||||||
self._updateQualityWithMaterial()
|
self._updateQualityWithMaterial()
|
||||||
|
|
||||||
# See if we need to show the Discard or Keep changes screen
|
# See if we need to show the Discard or Keep changes screen
|
||||||
@ -1433,5 +1438,5 @@ class MachineManager(QObject):
|
|||||||
if self._global_container_stack is None:
|
if self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
self._updateMaterialWithVariant(None)
|
self.updateMaterialWithVariant(None)
|
||||||
self._updateQualityWithMaterial()
|
self._updateQualityWithMaterial()
|
||||||
|
@ -9,6 +9,7 @@ from UM.Signal import Signal, signalemitter
|
|||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
from UM.Util import parseBool
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
user_container = InstanceContainer(container_id = self._generateUniqueName())
|
user_container = InstanceContainer(container_id = self._generateUniqueName())
|
||||||
user_container.addMetaDataEntry("type", "user")
|
user_container.addMetaDataEntry("type", "user")
|
||||||
self._stack.userChanges = user_container
|
self._stack.userChanges = user_container
|
||||||
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
|
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0)
|
||||||
|
|
||||||
self._is_non_printing_mesh = False
|
self._is_non_printing_mesh = False
|
||||||
self._is_non_thumbnail_visible_mesh = False
|
self._is_non_thumbnail_visible_mesh = False
|
||||||
@ -48,13 +49,25 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
|
|
||||||
Application.getInstance().getContainerRegistry().addContainer(self._stack)
|
Application.getInstance().getContainerRegistry().addContainer(self._stack)
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
|
Application.getInstance().globalContainerStackChanged.connect(self._onNumberOfExtrudersEnabledChanged)
|
||||||
|
Application.getInstance().getMachineManager().numberExtrudersEnabledChanged.connect(self._onNumberOfExtrudersEnabledChanged)
|
||||||
|
|
||||||
self.activeExtruderChanged.connect(self._updateNextStack)
|
self.activeExtruderChanged.connect(self._updateNextStack)
|
||||||
self._updateNextStack()
|
self._updateNextStack()
|
||||||
|
|
||||||
def _generateUniqueName(self):
|
def _generateUniqueName(self):
|
||||||
return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
|
return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
|
||||||
|
|
||||||
|
def _onNumberOfExtrudersEnabledChanged(self, *args, **kwargs):
|
||||||
|
if not parseBool(self._extruder_stack.getMetaDataEntry("enabled", "True")):
|
||||||
|
# switch to the first extruder that's available
|
||||||
|
global_stack = Application.getInstance().getMachineManager().activeMachine
|
||||||
|
for _, extruder in sorted(list(global_stack.extruders.items())):
|
||||||
|
if parseBool(extruder.getMetaDataEntry("enabled", "True")):
|
||||||
|
self._extruder_stack = extruder
|
||||||
|
self._updateNextStack()
|
||||||
|
break
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
## Create a fresh decorator object
|
## Create a fresh decorator object
|
||||||
deep_copy = SettingOverrideDecorator()
|
deep_copy = SettingOverrideDecorator()
|
||||||
@ -63,13 +76,13 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
|
instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
|
||||||
|
|
||||||
# A unique name must be added, or replaceContainer will not replace it
|
# A unique name must be added, or replaceContainer will not replace it
|
||||||
instance_container.setMetaDataEntry("id", self._generateUniqueName)
|
instance_container.setMetaDataEntry("id", self._generateUniqueName())
|
||||||
|
|
||||||
## Set the copied instance as the first (and only) instance container of the stack.
|
## Set the copied instance as the first (and only) instance container of the stack.
|
||||||
deep_copy._stack.replaceContainer(0, instance_container)
|
deep_copy._stack.replaceContainer(0, instance_container)
|
||||||
|
|
||||||
# Properly set the right extruder on the copy
|
# Properly set the right extruder on the copy
|
||||||
deep_copy.setActiveExtruder(self._extruder_stack)
|
deep_copy.setActiveExtruder(self._extruder_stack.getId())
|
||||||
|
|
||||||
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
|
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
|
||||||
# has not been updated yet.
|
# has not been updated yet.
|
||||||
@ -82,7 +95,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
#
|
#
|
||||||
# \return An extruder's container stack.
|
# \return An extruder's container stack.
|
||||||
def getActiveExtruder(self):
|
def getActiveExtruder(self):
|
||||||
return self._extruder_stack
|
return self._extruder_stack.getId()
|
||||||
|
|
||||||
## Gets the signal that emits if the active extruder changed.
|
## Gets the signal that emits if the active extruder changed.
|
||||||
#
|
#
|
||||||
@ -124,20 +137,16 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
# kept up to date.
|
# kept up to date.
|
||||||
def _updateNextStack(self):
|
def _updateNextStack(self):
|
||||||
if self._extruder_stack:
|
if self._extruder_stack:
|
||||||
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
|
|
||||||
if extruder_stack:
|
|
||||||
if self._stack.getNextStack():
|
if self._stack.getNextStack():
|
||||||
old_extruder_stack_id = self._stack.getNextStack().getId()
|
old_extruder_stack_id = self._stack.getNextStack().getId()
|
||||||
else:
|
else:
|
||||||
old_extruder_stack_id = ""
|
old_extruder_stack_id = ""
|
||||||
|
|
||||||
self._stack.setNextStack(extruder_stack[0])
|
self._stack.setNextStack(self._extruder_stack)
|
||||||
# Trigger slice/need slicing if the extruder changed.
|
# Trigger slice/need slicing if the extruder changed.
|
||||||
if self._stack.getNextStack().getId() != old_extruder_stack_id:
|
if self._stack.getNextStack().getId() != old_extruder_stack_id:
|
||||||
Application.getInstance().getBackend().needsSlicing()
|
Application.getInstance().getBackend().needsSlicing()
|
||||||
Application.getInstance().getBackend().tickle()
|
Application.getInstance().getBackend().tickle()
|
||||||
else:
|
|
||||||
Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
|
|
||||||
else:
|
else:
|
||||||
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
|
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
|
||||||
|
|
||||||
@ -145,7 +154,14 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
#
|
#
|
||||||
# \param extruder_stack_id The new extruder stack to print with.
|
# \param extruder_stack_id The new extruder stack to print with.
|
||||||
def setActiveExtruder(self, extruder_stack_id):
|
def setActiveExtruder(self, extruder_stack_id):
|
||||||
self._extruder_stack = extruder_stack_id
|
if self._extruder_stack.getId() == extruder_stack_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
global_stack = Application.getInstance().getMachineManager().activeMachine
|
||||||
|
for extruder in global_stack.extruders.values():
|
||||||
|
if extruder.getId() == extruder_stack_id:
|
||||||
|
self._extruder_stack = extruder
|
||||||
|
break
|
||||||
self._updateNextStack()
|
self._updateNextStack()
|
||||||
ExtruderManager.getInstance().resetSelectedObjectExtruders()
|
ExtruderManager.getInstance().resetSelectedObjectExtruders()
|
||||||
self.activeExtruderChanged.emit()
|
self.activeExtruderChanged.emit()
|
||||||
|
@ -158,4 +158,4 @@ class MachineSettingsAction(MachineAction):
|
|||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def updateMaterialForDiameter(self, extruder_position: int):
|
def updateMaterialForDiameter(self, extruder_position: int):
|
||||||
# Updates the material container to a material that matches the material diameter set for the printer
|
# Updates the material container to a material that matches the material diameter set for the printer
|
||||||
self._application.getMachineManager()._updateMaterialWithVariant(extruder_position)
|
self._application.getMachineManager().updateMaterialWithVariant(extruder_position)
|
||||||
|
@ -170,7 +170,7 @@ UM.PreferencesPage
|
|||||||
append({ text: "日本語", code: "ja_JP" })
|
append({ text: "日本語", code: "ja_JP" })
|
||||||
append({ text: "한국어", code: "ko_KR" })
|
append({ text: "한국어", code: "ko_KR" })
|
||||||
append({ text: "Nederlands", code: "nl_NL" })
|
append({ text: "Nederlands", code: "nl_NL" })
|
||||||
append({ text: "Polski", code: "pl_PL" })
|
//Polish is disabled for being incomplete: append({ text: "Polski", code: "pl_PL" })
|
||||||
append({ text: "Português do Brasil", code: "pt_BR" })
|
append({ text: "Português do Brasil", code: "pt_BR" })
|
||||||
append({ text: "Português", code: "pt_PT" })
|
append({ text: "Português", code: "pt_PT" })
|
||||||
append({ text: "Русский", code: "ru_RU" })
|
append({ text: "Русский", code: "ru_RU" })
|
||||||
|
Loading…
x
Reference in New Issue
Block a user