mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-07 22:13:58 -06:00
Merge branch 'master' into feature_intent
Conflicts: resources/bundled_packages/cura.json -> 4.1 to 4.2 upgrade package added simultaneously tests/TestMachineManager.py -> Due to changes in conftest.py tests/conftest.py -> Simultaneously expanding the magic mocks to have some correct properties
This commit is contained in:
commit
20201c65e6
1386 changed files with 33217 additions and 35219 deletions
|
@ -4,7 +4,7 @@
|
|||
import os
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from typing import Dict, Union, Any, TYPE_CHECKING, List
|
||||
from typing import Dict, Union, Any, TYPE_CHECKING, List, cast
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
@ -438,7 +438,7 @@ class ContainerManager(QObject):
|
|||
if not path:
|
||||
return
|
||||
|
||||
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
|
||||
container_list = [cast(InstanceContainer, n.getContainer()) for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
|
||||
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
||||
|
||||
__instance = None # type: ContainerManager
|
||||
|
|
|
@ -5,10 +5,11 @@ import os
|
|||
import re
|
||||
import configparser
|
||||
|
||||
from typing import Any, cast, Dict, Optional
|
||||
from typing import Any, cast, Dict, Optional, List, Union
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from UM.Decorators import override
|
||||
from UM.PluginObject import PluginObject
|
||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
@ -22,6 +23,7 @@ from UM.Platform import Platform
|
|||
from UM.PluginRegistry import PluginRegistry # For getting the possible profile writers to write with.
|
||||
from UM.Util import parseBool
|
||||
from UM.Resources import Resources
|
||||
from cura.ReaderWriters.ProfileWriter import ProfileWriter
|
||||
|
||||
from . import ExtruderStack
|
||||
from . import GlobalStack
|
||||
|
@ -50,10 +52,10 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# This will also try to convert a ContainerStack to either Extruder or
|
||||
# Global stack based on metadata information.
|
||||
@override(ContainerRegistry)
|
||||
def addContainer(self, container):
|
||||
def addContainer(self, container: ContainerInterface) -> None:
|
||||
# Note: Intentional check with type() because we want to ignore subclasses
|
||||
if type(container) == ContainerStack:
|
||||
container = self._convertContainerStack(container)
|
||||
container = self._convertContainerStack(cast(ContainerStack, container))
|
||||
|
||||
if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()):
|
||||
# Check against setting version of the definition.
|
||||
|
@ -61,7 +63,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0))
|
||||
if required_setting_version != actual_setting_version:
|
||||
Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version))
|
||||
return #Don't add.
|
||||
return # Don't add.
|
||||
|
||||
super().addContainer(container)
|
||||
|
||||
|
@ -71,9 +73,9 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# \param new_name \type{string} Base name, which may not be unique
|
||||
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
|
||||
# \return \type{string} Name that is unique for the specified type and name/id
|
||||
def createUniqueName(self, container_type, current_name, new_name, fallback_name):
|
||||
def createUniqueName(self, container_type: str, current_name: str, new_name: str, fallback_name: str) -> str:
|
||||
new_name = new_name.strip()
|
||||
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
|
||||
num_check = re.compile(r"(.*?)\s*#\d+$").match(new_name)
|
||||
if num_check:
|
||||
new_name = num_check.group(1)
|
||||
if new_name == "":
|
||||
|
@ -92,7 +94,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
# \param container_name \type{string} Name to check
|
||||
def _containerExists(self, container_type, container_name):
|
||||
def _containerExists(self, container_type: str, container_name: str):
|
||||
container_class = ContainerStack if container_type == "machine" else InstanceContainer
|
||||
|
||||
return self.findContainersMetadata(container_type = container_class, id = container_name, type = container_type, ignore_case = True) or \
|
||||
|
@ -100,11 +102,11 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
|
||||
## Exports an profile to a file
|
||||
#
|
||||
# \param instance_ids \type{list} the IDs of the profiles to export.
|
||||
# \param container_list \type{list} the containers to export
|
||||
# \param file_name \type{str} the full path and filename to export to.
|
||||
# \param file_type \type{str} the file type with the format "<description> (*.<extension>)"
|
||||
# \return True if the export succeeded, false otherwise.
|
||||
def exportQualityProfile(self, container_list, file_name, file_type) -> bool:
|
||||
def exportQualityProfile(self, container_list: List[InstanceContainer], file_name: str, file_type: str) -> bool:
|
||||
# Parse the fileType to deduce what plugin can save the file format.
|
||||
# fileType has the format "<description> (*.<extension>)"
|
||||
split = file_type.rfind(" (*.") # Find where the description ends and the extension starts.
|
||||
|
@ -126,6 +128,8 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
|
||||
profile_writer = self._findProfileWriter(extension, description)
|
||||
try:
|
||||
if profile_writer is None:
|
||||
raise Exception("Unable to find a profile writer")
|
||||
success = profile_writer.write(file_name, container_list)
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
|
||||
|
@ -150,7 +154,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# \param extension
|
||||
# \param description
|
||||
# \return The plugin object matching the given extension and description.
|
||||
def _findProfileWriter(self, extension, description):
|
||||
def _findProfileWriter(self, extension: str, description: str) -> Optional[ProfileWriter]:
|
||||
plugin_registry = PluginRegistry.getInstance()
|
||||
for plugin_id, meta_data in self._getIOPlugins("profile_writer"):
|
||||
for supported_type in meta_data["profile_writer"]: # All file types this plugin can supposedly write.
|
||||
|
@ -158,7 +162,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
if supported_extension == extension: # This plugin supports a file type with the same extension.
|
||||
supported_description = supported_type.get("description", None)
|
||||
if supported_description == description: # The description is also identical. Assume it's the same file type.
|
||||
return plugin_registry.getPluginObject(plugin_id)
|
||||
return cast(ProfileWriter, plugin_registry.getPluginObject(plugin_id))
|
||||
return None
|
||||
|
||||
## Imports a profile from a file
|
||||
|
@ -324,7 +328,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
|
||||
|
||||
@override(ContainerRegistry)
|
||||
def load(self):
|
||||
def load(self) -> None:
|
||||
super().load()
|
||||
self._registerSingleExtrusionMachinesExtruderStacks()
|
||||
self._connectUpgradedExtruderStacksToMachines()
|
||||
|
@ -406,7 +410,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
return result
|
||||
|
||||
## Convert an "old-style" pure ContainerStack to either an Extruder or Global stack.
|
||||
def _convertContainerStack(self, container):
|
||||
def _convertContainerStack(self, container: ContainerStack) -> Union[ExtruderStack.ExtruderStack, GlobalStack.GlobalStack]:
|
||||
assert type(container) == ContainerStack
|
||||
|
||||
container_type = container.getMetaDataEntry("type")
|
||||
|
@ -430,14 +434,14 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
|
||||
return new_stack
|
||||
|
||||
def _registerSingleExtrusionMachinesExtruderStacks(self):
|
||||
def _registerSingleExtrusionMachinesExtruderStacks(self) -> None:
|
||||
machines = self.findContainerStacks(type = "machine", machine_extruder_trains = {"0": "fdmextruder"})
|
||||
for machine in machines:
|
||||
extruder_stacks = self.findContainerStacks(type = "extruder_train", machine = machine.getId())
|
||||
if not extruder_stacks:
|
||||
self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder")
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
def _onContainerAdded(self, container: ContainerInterface) -> None:
|
||||
# We don't have all the machines loaded in the beginning, so in order to add the missing extruder stack
|
||||
# for single extrusion machines, we subscribe to the containerAdded signal, and whenever a global stack
|
||||
# is added, we check to see if an extruder stack needs to be added.
|
||||
|
@ -671,7 +675,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
|
||||
return extruder_stack
|
||||
|
||||
def _findQualityChangesContainerInCuraFolder(self, name):
|
||||
def _findQualityChangesContainerInCuraFolder(self, name: str) -> Optional[InstanceContainer]:
|
||||
quality_changes_dir = Resources.getPath(cura.CuraApplication.CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
|
||||
|
||||
instance_container = None
|
||||
|
@ -684,7 +688,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
parser = configparser.ConfigParser(interpolation = None)
|
||||
try:
|
||||
parser.read([file_path])
|
||||
except:
|
||||
except Exception:
|
||||
# Skip, it is not a valid stack file
|
||||
continue
|
||||
|
||||
|
@ -716,7 +720,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# due to problems with loading order, some stacks may not have the proper next stack
|
||||
# set after upgrading, because the proper global stack was not yet loaded. This method
|
||||
# makes sure those extruders also get the right stack set.
|
||||
def _connectUpgradedExtruderStacksToMachines(self):
|
||||
def _connectUpgradedExtruderStacksToMachines(self) -> None:
|
||||
extruder_stacks = self.findContainers(container_type = ExtruderStack.ExtruderStack)
|
||||
for extruder_stack in extruder_stacks:
|
||||
if extruder_stack.getNextStack():
|
||||
|
|
|
@ -129,8 +129,9 @@ class CuraStackBuilder:
|
|||
extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0]
|
||||
except IndexError as e:
|
||||
# It still needs to break, but we want to know what extruder ID made it break.
|
||||
Logger.log("e", "Unable to find extruder with the id %s", extruder_definition_id)
|
||||
raise e
|
||||
msg = "Unable to find extruder definition with the id [%s]" % extruder_definition_id
|
||||
Logger.logException("e", msg)
|
||||
raise IndexError(msg)
|
||||
|
||||
# get material container for extruders
|
||||
material_container = application.empty_material_container
|
||||
|
|
|
@ -12,7 +12,7 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Decorators import deprecated
|
||||
|
||||
from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING, Union
|
||||
|
||||
|
@ -95,6 +95,7 @@ class ExtruderManager(QObject):
|
|||
#
|
||||
# \param index The index of the extruder whose name to get.
|
||||
@pyqtSlot(int, result = str)
|
||||
@deprecated("Use Cura.MachineManager.activeMachine.extruders[index].name instead", "4.3")
|
||||
def getExtruderName(self, index: int) -> str:
|
||||
try:
|
||||
return self.getActiveExtruderStacks()[index].getName()
|
||||
|
@ -114,7 +115,7 @@ class ExtruderManager(QObject):
|
|||
selected_nodes = [] # type: List["SceneNode"]
|
||||
for node in Selection.getAllSelectedObjects():
|
||||
if node.callDecoration("isGroup"):
|
||||
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||
for grouped_node in BreadthFirstIterator(node):
|
||||
if grouped_node.callDecoration("isGroup"):
|
||||
continue
|
||||
|
||||
|
@ -131,7 +132,7 @@ class ExtruderManager(QObject):
|
|||
elif current_extruder_trains:
|
||||
object_extruders.add(current_extruder_trains[0].getId())
|
||||
|
||||
self._selected_object_extruders = list(object_extruders) # type: List[Union[str, "ExtruderStack"]]
|
||||
self._selected_object_extruders = list(object_extruders)
|
||||
|
||||
return self._selected_object_extruders
|
||||
|
||||
|
@ -140,7 +141,7 @@ class ExtruderManager(QObject):
|
|||
# This will trigger a recalculation of the extruders used for the
|
||||
# selection.
|
||||
def resetSelectedObjectExtruders(self) -> None:
|
||||
self._selected_object_extruders = [] # type: List[Union[str, "ExtruderStack"]]
|
||||
self._selected_object_extruders = []
|
||||
self.selectedObjectExtrudersChanged.emit()
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
|
@ -380,7 +381,13 @@ class ExtruderManager(QObject):
|
|||
elif extruder_stack_0.definition.getId() != expected_extruder_definition_0_id:
|
||||
Logger.log("e", "Single extruder printer [{printer}] expected extruder [{expected}], but got [{got}]. I'm making it [{expected}].".format(
|
||||
printer = global_stack.getId(), expected = expected_extruder_definition_0_id, got = extruder_stack_0.definition.getId()))
|
||||
extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0]
|
||||
try:
|
||||
extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0]
|
||||
except IndexError as e:
|
||||
# It still needs to break, but we want to know what extruder ID made it break.
|
||||
msg = "Unable to find extruder definition with the id [%s]" % expected_extruder_definition_0_id
|
||||
Logger.logException("e", msg)
|
||||
raise IndexError(msg)
|
||||
extruder_stack_0.definition = extruder_definition
|
||||
|
||||
## Get all extruder values for a certain setting.
|
||||
|
|
|
@ -118,7 +118,7 @@ class GlobalStack(CuraContainerStack):
|
|||
## \sa configuredConnectionTypes
|
||||
def removeConfiguredConnectionType(self, connection_type: int) -> None:
|
||||
configured_connection_types = self.configuredConnectionTypes
|
||||
if connection_type in self.configured_connection_types:
|
||||
if connection_type in configured_connection_types:
|
||||
# Store the values as a string.
|
||||
configured_connection_types.remove(connection_type)
|
||||
self.setMetaDataEntry("connection_type", ",".join([str(c_type) for c_type in configured_connection_types]))
|
||||
|
|
|
@ -38,11 +38,10 @@ from .CuraStackBuilder import CuraStackBuilder
|
|||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.CuraContainerStack import CuraContainerStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
|
@ -388,7 +387,7 @@ class MachineManager(QObject):
|
|||
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
|
||||
for machine in machines:
|
||||
if machine.definition.getId() == definition_id:
|
||||
return machine
|
||||
return cast(GlobalStack, machine)
|
||||
return None
|
||||
|
||||
@pyqtSlot(str)
|
||||
|
@ -599,7 +598,7 @@ class MachineManager(QObject):
|
|||
def activeStack(self) -> Optional["ExtruderStack"]:
|
||||
return self._active_container_stack
|
||||
|
||||
@pyqtProperty(str, notify=activeMaterialChanged)
|
||||
@pyqtProperty(str, notify = activeMaterialChanged)
|
||||
def activeMaterialId(self) -> str:
|
||||
if self._active_container_stack:
|
||||
material = self._active_container_stack.material
|
||||
|
@ -950,7 +949,7 @@ class MachineManager(QObject):
|
|||
|
||||
# Check to see if any objects are set to print with an extruder that will no longer exist
|
||||
root_node = self._application.getController().getScene().getRoot()
|
||||
for node in DepthFirstIterator(root_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||
for node in DepthFirstIterator(root_node):
|
||||
if node.getMeshData():
|
||||
extruder_nr = node.callDecoration("getActiveExtruderPosition")
|
||||
|
||||
|
@ -986,10 +985,13 @@ class MachineManager(QObject):
|
|||
self._application.globalContainerStackChanged.emit()
|
||||
self.forceUpdateAllSettings()
|
||||
|
||||
# Note that this function is deprecated, but the decorators for this don't play well together!
|
||||
# @deprecated("use Cura.MachineManager.activeMachine.extruders instead", "4.2")
|
||||
@pyqtSlot(int, result = QObject)
|
||||
def getExtruder(self, position: int) -> Optional[ExtruderStack]:
|
||||
return self._getExtruder(position)
|
||||
|
||||
# This is a workaround for the deprecated decorator and the pyqtSlot not playing well together.
|
||||
@deprecated("use Cura.MachineManager.activeMachine.extruders instead", "4.2")
|
||||
def _getExtruder(self, position) -> Optional[ExtruderStack]:
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.extruders.get(str(position))
|
||||
return None
|
||||
|
@ -1112,9 +1114,17 @@ class MachineManager(QObject):
|
|||
def _onRootMaterialChanged(self) -> None:
|
||||
self._current_root_material_id = {}
|
||||
|
||||
changed = False
|
||||
|
||||
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")
|
||||
material_id = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
|
||||
if position not in self._current_root_material_id or material_id != self._current_root_material_id[position]:
|
||||
changed = True
|
||||
self._current_root_material_id[position] = material_id
|
||||
|
||||
if changed:
|
||||
self.activeMaterialChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialId(self) -> Dict[str, str]:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue