diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 1a51f9201f..2041c45ef5 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -1,21 +1,25 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import numpy from string import Formatter from enum import IntEnum import time +from typing import Any, Dict, List, Optional, Set import re +import Arcus #For typing. from UM.Job import Job -from UM.Application import Application from UM.Logger import Logger +from UM.Settings.ContainerStack import ContainerStack #For typing. +from UM.Settings.SettingRelation import SettingRelation #For typing. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator - +from UM.Scene.Scene import Scene #For typing. from UM.Settings.Validator import ValidatorState from UM.Settings.SettingRelation import RelationType +from cura.CuraApplication import CuraApplication from cura.Scene.CuraSceneNode import CuraSceneNode from cura.OneAtATimeIterator import OneAtATimeIterator from cura.Settings.ExtruderManager import ExtruderManager @@ -35,19 +39,19 @@ class StartJobResult(IntEnum): ObjectsWithDisabledExtruder = 8 -## Formatter class that handles token expansion in start/end gcod +## Formatter class that handles token expansion in start/end gcode class GcodeStartEndFormatter(Formatter): - def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class] + def get_value(self, key: str, *args: str, **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class] # The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key), # and a default_extruder_nr to use when no extruder_nr is specified if isinstance(key, str): try: - extruder_nr = kwargs["default_extruder_nr"] + extruder_nr = int(kwargs["default_extruder_nr"]) except ValueError: extruder_nr = -1 - key_fragments = [fragment.strip() for fragment in key.split(',')] + key_fragments = [fragment.strip() for fragment in key.split(",")] if len(key_fragments) == 2: try: extruder_nr = int(key_fragments[1]) @@ -74,25 +78,25 @@ class GcodeStartEndFormatter(Formatter): ## Job class that builds up the message of scene data to send to CuraEngine. class StartSliceJob(Job): - def __init__(self, slice_message): + def __init__(self, slice_message: Arcus.PythonMessage) -> None: super().__init__() - self._scene = Application.getInstance().getController().getScene() - self._slice_message = slice_message - self._is_cancelled = False - self._build_plate_number = None + self._scene = CuraApplication.getInstance().getController().getScene() #type: Scene + self._slice_message = slice_message #type: Arcus.PythonMessage + self._is_cancelled = False #type: bool + self._build_plate_number = None #type: Optional[int] - self._all_extruders_settings = None # cache for all setting values from all stacks (global & extruder) for the current machine + self._all_extruders_settings = None #type: Optional[Dict[str, Any]] # cache for all setting values from all stacks (global & extruder) for the current machine - def getSliceMessage(self): + def getSliceMessage(self) -> Arcus.PythonMessage: return self._slice_message - def setBuildPlate(self, build_plate_number): + def setBuildPlate(self, build_plate_number: int) -> None: self._build_plate_number = build_plate_number ## Check if a stack has any errors. ## returns true if it has errors, false otherwise. - def _checkStackForErrors(self, stack): + def _checkStackForErrors(self, stack: ContainerStack) -> bool: if stack is None: return False @@ -105,28 +109,28 @@ class StartSliceJob(Job): return False ## Runs the job that initiates the slicing. - def run(self): + def run(self) -> None: if self._build_plate_number is None: self.setResult(StartJobResult.Error) return - stack = Application.getInstance().getGlobalContainerStack() + stack = CuraApplication.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. - if Application.getInstance().getMachineManager().stacksHaveErrors: + if CuraApplication.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return - if Application.getInstance().getBuildVolume().hasErrors(): + if CuraApplication.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return # Don't slice if the buildplate or the nozzle type is incompatible with the materials - if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \ - not Application.getInstance().getMachineManager().variantBuildplateUsable: + if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \ + not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable: self.setResult(StartJobResult.MaterialIncompatible) return @@ -141,7 +145,7 @@ class StartSliceJob(Job): # Don't slice if there is a per object setting with an error value. - for node in DepthFirstIterator(self._scene.getRoot()): + for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue @@ -151,7 +155,7 @@ class StartSliceJob(Job): with self._scene.getSceneLock(): # Remove old layer data. - for node in DepthFirstIterator(self._scene.getRoot()): + for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number: node.getParent().removeChild(node) break @@ -159,7 +163,7 @@ class StartSliceJob(Job): # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": - for node in OneAtATimeIterator(self._scene.getRoot()): + for node in OneAtATimeIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. temp_list = [] # Node can't be printed, so don't bother sending it. @@ -185,7 +189,7 @@ class StartSliceJob(Job): else: temp_list = [] has_printing_mesh = False - for node in DepthFirstIterator(self._scene.getRoot()): + for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("isSliceable") and node.getMeshData() and node.getMeshData().getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False @@ -212,12 +216,12 @@ class StartSliceJob(Job): if temp_list: object_groups.append(temp_list) - extruders_enabled = {position: stack.isEnabled for position, stack in Application.getInstance().getGlobalContainerStack().extruders.items()} + extruders_enabled = {position: stack.isEnabled for position, stack in CuraApplication.getInstance().getGlobalContainerStack().extruders.items()} filtered_object_groups = [] has_model_with_disabled_extruders = False associated_siabled_extruders = set() for group in object_groups: - stack = Application.getInstance().getGlobalContainerStack() + stack = CuraApplication.getInstance().getGlobalContainerStack() skip_group = False for node in group: extruder_position = node.callDecoration("getActiveExtruderPosition") @@ -231,7 +235,7 @@ class StartSliceJob(Job): if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) - associated_siabled_extruders = [str(c) for c in sorted([int(p) + 1 for p in associated_siabled_extruders])] + associated_siabled_extruders = {str(c) for c in sorted([int(p) + 1 for p in associated_siabled_extruders])} self.setMessage(", ".join(associated_siabled_extruders)) return @@ -284,11 +288,11 @@ class StartSliceJob(Job): self.setResult(StartJobResult.Finished) - def cancel(self): + def cancel(self) -> None: super().cancel() self._is_cancelled = True - def isCancelled(self): + def isCancelled(self) -> bool: return self._is_cancelled ## Creates a dictionary of tokens to replace in g-code pieces. @@ -298,7 +302,7 @@ class StartSliceJob(Job): # with. # \return A dictionary of replacement tokens to the values they should be # replaced with. - def _buildReplacementTokens(self, stack) -> dict: + def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]: result = {} for key in stack.getAllKeys(): value = stack.getProperty(key, "value") @@ -311,7 +315,7 @@ class StartSliceJob(Job): result["date"] = time.strftime("%d-%m-%Y") result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))] - initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0] + initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0] initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value") result["initial_extruder_nr"] = initial_extruder_nr @@ -320,9 +324,9 @@ class StartSliceJob(Job): ## Replace setting tokens in a piece of g-code. # \param value A piece of g-code to replace tokens in. # \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack - def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1): + def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str: if not self._all_extruders_settings: - global_stack = Application.getInstance().getGlobalContainerStack() + global_stack = CuraApplication.getInstance().getGlobalContainerStack() # NB: keys must be strings for the string formatter self._all_extruders_settings = { @@ -344,7 +348,7 @@ class StartSliceJob(Job): return str(value) ## Create extruder message from stack - def _buildExtruderMessage(self, stack): + def _buildExtruderMessage(self, stack: ContainerStack) -> None: message = self._slice_message.addRepeatedMessage("extruders") message.id = int(stack.getMetaDataEntry("position")) @@ -371,7 +375,7 @@ class StartSliceJob(Job): # # The settings are taken from the global stack. This does not include any # per-extruder settings or per-object settings. - def _buildGlobalSettingsMessage(self, stack): + def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None: settings = self._buildReplacementTokens(stack) # Pre-compute material material_bed_temp_prepend and material_print_temp_prepend @@ -385,7 +389,7 @@ class StartSliceJob(Job): # Replace the setting tokens in start and end g-code. # Use values from the first used extruder by default so we get the expected temperatures - initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0] + initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0] initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value") settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr) @@ -406,7 +410,7 @@ class StartSliceJob(Job): # # \param stack The global stack with all settings, from which to read the # limit_to_extruder property. - def _buildGlobalInheritsStackMessage(self, stack): + def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None: for key in stack.getAllKeys(): extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder")))) if extruder_position >= 0: # Set to a specific extruder. @@ -416,9 +420,9 @@ class StartSliceJob(Job): Job.yieldThread() ## Check if a node has per object settings and ensure that they are set correctly in the message - # \param node \type{SceneNode} Node to check. + # \param node Node to check. # \param message object_lists message to put the per object settings in - def _handlePerObjectSettings(self, node, message): + def _handlePerObjectSettings(self, node: CuraSceneNode, message: Arcus.PythonMessage): stack = node.callDecoration("getStack") # Check if the node has a stack attached to it and the stack has any settings in the top container. @@ -456,9 +460,9 @@ class StartSliceJob(Job): Job.yieldThread() ## Recursive function to put all settings that require each other for value changes in a list - # \param relations_set \type{set} Set of keys (strings) of settings that are influenced + # \param relations_set Set of keys of settings that are influenced # \param relations list of relation objects that need to be checked. - def _addRelations(self, relations_set, relations): + def _addRelations(self, relations_set: Set[str], relations: List[SettingRelation]): for relation in filter(lambda r: r.role == "value" or r.role == "limit_to_extruder", relations): if relation.type == RelationType.RequiresTarget: continue