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:
Ghostkeeper 2019-08-13 14:43:28 +02:00
commit 20201c65e6
No known key found for this signature in database
GPG key ID: 86BEF881AE2CF276
1386 changed files with 33217 additions and 35219 deletions

View file

@ -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

View file

@ -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():

View file

@ -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

View file

@ -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.

View file

@ -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]))

View file

@ -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]: