From 3e07105edfa12651980abe61e0141127d60598f0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 11:42:17 +0100 Subject: [PATCH 01/23] Fix incorrect typing --- plugins/Toolbox/src/Toolbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index f28178b99e..ee260f6808 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -797,11 +797,11 @@ class Toolbox(QObject, Extension): return cast(SubscribedPackagesModel, self._models["subscribed_packages"]) @pyqtProperty(bool, constant=True) - def has_compatible_packages(self) -> str: + def has_compatible_packages(self) -> bool: return self._models["subscribed_packages"].hasCompatiblePackages() @pyqtProperty(bool, constant=True) - def has_incompatible_packages(self) -> str: + def has_incompatible_packages(self) -> bool: return self._models["subscribed_packages"].hasIncompatiblePackages() @pyqtProperty(QObject, constant = True) From 81b33b864959610ea5a367d3149706d35ca499e2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 14:25:35 +0100 Subject: [PATCH 02/23] Add some missing typing --- cura/Arranging/Arrange.py | 5 +++-- cura/AutoSave.py | 14 +++++++++----- cura/CuraApplication.py | 10 +++++----- cura/CuraPackageManager.py | 10 +++++++--- cura/Layer.py | 4 ++-- cura/LayerDataDecorator.py | 2 +- cura/LayerPolygon.py | 18 +++++++++--------- cura/Machines/ContainerNode.py | 2 +- cura/Machines/ContainerTree.py | 6 +++--- cura/Machines/MachineErrorChecker.py | 15 ++++++++------- cura/Machines/MaterialNode.py | 1 + cura/OAuth2/Models.py | 7 ++++--- cura/Operations/PlatformPhysicsOperation.py | 13 +++++++------ .../Operations/SetBuildPlateNumberOperation.py | 6 +++--- cura/Operations/SetParentOperation.py | 13 +++++++------ .../Models/PrintJobOutputModel.py | 2 +- cura/Scene/BlockSlicingDecorator.py | 2 +- cura/UI/MachineActionManager.py | 2 +- 18 files changed, 73 insertions(+), 59 deletions(-) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index caa7aae910..8f7ba5ccfe 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List +from typing import List, Optional from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Logger import Logger @@ -8,6 +8,7 @@ from UM.Math.Polygon import Polygon from UM.Math.Vector import Vector from UM.Scene.SceneNode import SceneNode from cura.Arranging.ShapeArray import ShapeArray +from cura.BuildVolume import BuildVolume from cura.Scene import ZOffsetDecorator from collections import namedtuple @@ -27,7 +28,7 @@ LocationSuggestion = namedtuple("LocationSuggestion", ["x", "y", "penalty_points # # Note: Make sure the scale is the same between ShapeArray objects and the Arrange instance. class Arrange: - build_volume = None + build_volume = None # type: Optional[BuildVolume] def __init__(self, x, y, offset_x, offset_y, scale= 0.5): self._scale = scale # convert input coordinates to arrange coordinates diff --git a/cura/AutoSave.py b/cura/AutoSave.py index 3b42fdafdf..2c1dbe4a84 100644 --- a/cura/AutoSave.py +++ b/cura/AutoSave.py @@ -2,12 +2,16 @@ # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import QTimer +from typing import Any, TYPE_CHECKING from UM.Logger import Logger +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + class AutoSave: - def __init__(self, application): + def __init__(self, application: "CuraApplication") -> None: self._application = application self._application.getPreferences().preferenceChanged.connect(self._triggerTimer) @@ -22,14 +26,14 @@ class AutoSave: self._enabled = True self._saving = False - def initialize(self): + def initialize(self) -> None: # only initialise if the application is created and has started self._change_timer.timeout.connect(self._onTimeout) self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() self._triggerTimer() - def _triggerTimer(self, *args): + def _triggerTimer(self, *args: Any) -> None: if not self._saving: self._change_timer.start() @@ -40,7 +44,7 @@ class AutoSave: else: self._change_timer.stop() - def _onGlobalStackChanged(self): + def _onGlobalStackChanged(self) -> None: if self._global_stack: self._global_stack.propertyChanged.disconnect(self._triggerTimer) self._global_stack.containersChanged.disconnect(self._triggerTimer) @@ -51,7 +55,7 @@ class AutoSave: self._global_stack.propertyChanged.connect(self._triggerTimer) self._global_stack.containersChanged.connect(self._triggerTimer) - def _onTimeout(self): + def _onTimeout(self) -> None: self._saving = True # To prevent the save process from triggering another autosave. Logger.log("d", "Autosaving preferences, instances and profiles") diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 8dd49c74f1..c1c2448a4d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -4,7 +4,7 @@ import os import sys import time -from typing import cast, TYPE_CHECKING, Optional, Callable, List +from typing import cast, TYPE_CHECKING, Optional, Callable, List, Any import numpy @@ -193,7 +193,7 @@ class CuraApplication(QtApplication): self._cura_package_manager = None - self._machine_action_manager = None + self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager] self.empty_container = None # type: EmptyInstanceContainer self.empty_definition_changes_container = None # type: EmptyInstanceContainer @@ -699,7 +699,7 @@ class CuraApplication(QtApplication): self._message_box_callback_arguments = [] # Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. - def saveSettings(self): + def saveSettings(self) -> None: if not self.started: # Do not do saving during application start or when data should not be saved on quit. return @@ -989,8 +989,8 @@ class CuraApplication(QtApplication): ## Get the machine action manager # We ignore any *args given to this, as we also register the machine manager as qml singleton. # It wants to give this function an engine and script engine, but we don't care about that. - def getMachineActionManager(self, *args): - return self._machine_action_manager + def getMachineActionManager(self, *args: Any) -> MachineActionManager.MachineActionManager: + return cast(MachineActionManager.MachineActionManager, self._machine_action_manager) @pyqtSlot(result = QObject) def getMaterialManagementModel(self) -> MaterialManagementModel: diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 6422469bdf..99f2072644 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Tuple +from typing import List, Tuple, TYPE_CHECKING, Optional from cura.CuraApplication import CuraApplication #To find some resource types. from cura.Settings.GlobalStack import GlobalStack @@ -9,12 +9,16 @@ from cura.Settings.GlobalStack import GlobalStack from UM.PackageManager import PackageManager #The class we're extending. from UM.Resources import Resources #To find storage paths for some resource types. +if TYPE_CHECKING: + from UM.Qt.QtApplication import QtApplication + from PyQt5.QtCore import QObject + class CuraPackageManager(PackageManager): - def __init__(self, application, parent = None): + def __init__(self, application: "QtApplication", parent: Optional["QObject"] = None): super().__init__(application, parent) - def initialize(self): + def initialize(self) -> None: self._installation_dirs_dict["materials"] = Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer) self._installation_dirs_dict["qualities"] = Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer) diff --git a/cura/Layer.py b/cura/Layer.py index 73fda64a45..933d4436c9 100644 --- a/cura/Layer.py +++ b/cura/Layer.py @@ -33,10 +33,10 @@ class Layer: def elementCount(self): return self._element_count - def setHeight(self, height): + def setHeight(self, height: float) -> None: self._height = height - def setThickness(self, thickness): + def setThickness(self, thickness: float) -> None: self._thickness = thickness def lineMeshVertexCount(self) -> int: diff --git a/cura/LayerDataDecorator.py b/cura/LayerDataDecorator.py index ef82d8f5cc..36466cac72 100644 --- a/cura/LayerDataDecorator.py +++ b/cura/LayerDataDecorator.py @@ -9,7 +9,7 @@ from cura.LayerData import LayerData ## Simple decorator to indicate a scene node holds layer data. class LayerDataDecorator(SceneNodeDecorator): - def __init__(self): + def __init__(self) -> None: super().__init__() self._layer_data = None # type: Optional[LayerData] diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index 0d6489aaa2..ca752e35ee 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -149,17 +149,17 @@ class LayerPolygon: def getColors(self): return self._colors - def mapLineTypeToColor(self, line_types): + def mapLineTypeToColor(self, line_types: numpy.ndarray) -> numpy.ndarray: return self._color_map[line_types] - def isInfillOrSkinType(self, line_types): + def isInfillOrSkinType(self, line_types: numpy.ndarray) -> numpy.ndarray: return self._isInfillOrSkinTypeMap[line_types] - def lineMeshVertexCount(self): - return (self._vertex_end - self._vertex_begin) + def lineMeshVertexCount(self) -> int: + return self._vertex_end - self._vertex_begin - def lineMeshElementCount(self): - return (self._index_end - self._index_begin) + def lineMeshElementCount(self) -> int: + return self._index_end - self._index_begin @property def extruder(self): @@ -202,7 +202,7 @@ class LayerPolygon: return self._jump_count # Calculate normals for the entire polygon using numpy. - def getNormals(self): + def getNormals(self) -> numpy.ndarray: normals = numpy.copy(self._data) normals[:, 1] = 0.0 # We are only interested in 2D normals @@ -226,11 +226,11 @@ class LayerPolygon: return normals - __color_map = None # type: numpy.ndarray[Any] + __color_map = None # type: numpy.ndarray ## Gets the instance of the VersionUpgradeManager, or creates one. @classmethod - def getColorMap(cls): + def getColorMap(cls) -> numpy.ndarray: if cls.__color_map is None: theme = QtApplication.getInstance().getTheme() cls.__color_map = numpy.array([ diff --git a/cura/Machines/ContainerNode.py b/cura/Machines/ContainerNode.py index a8bbf0a537..8a9ddcc39b 100644 --- a/cura/Machines/ContainerNode.py +++ b/cura/Machines/ContainerNode.py @@ -26,7 +26,7 @@ class ContainerNode: ## Gets the metadata of the container that this node represents. # Getting the metadata from the container directly is about 10x as fast. # \return The metadata of the container in this node. - def getMetadata(self): + def getMetadata(self) -> Dict[str, Any]: return ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id)[0] ## Get an entry from the metadata of the container that this node contains. diff --git a/cura/Machines/ContainerTree.py b/cura/Machines/ContainerTree.py index c2bfabea2c..a7bb0610bd 100644 --- a/cura/Machines/ContainerTree.py +++ b/cura/Machines/ContainerTree.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: # nodes that have children) but that child node may be a node representing the # empty instance container. class ContainerTree: - __instance = None + __instance = None # type: Optional["ContainerTree"] @classmethod def getInstance(cls): @@ -75,7 +75,7 @@ class ContainerTree: return self.machines[global_stack.definition.getId()].getQualityChangesGroups(variant_names, material_bases, extruder_enabled) ## Ran after completely starting up the application. - def _onStartupFinished(self): + def _onStartupFinished(self) -> None: currently_added = ContainerRegistry.getInstance().findContainerStacks() # Find all currently added global stacks. JobQueue.getInstance().add(self._MachineNodeLoadJob(self, currently_added)) @@ -137,7 +137,7 @@ class ContainerTree: # \param container_stacks All of the stacks to pre-load the container # trees for. This needs to be provided from here because the stacks # need to be constructed on the main thread because they are QObject. - def __init__(self, tree_root: "ContainerTree", container_stacks: List["ContainerStack"]): + def __init__(self, tree_root: "ContainerTree", container_stacks: List["ContainerStack"]) -> None: self.tree_root = tree_root self.container_stacks = container_stacks super().__init__() diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 4c6ed891b1..9460578f45 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -6,6 +6,7 @@ import time from collections import deque from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty +from typing import Optional, Any, Set from UM.Application import Application from UM.Logger import Logger @@ -24,16 +25,16 @@ from UM.Settings.Validator import ValidatorState # class MachineErrorChecker(QObject): - def __init__(self, parent = None): + def __init__(self, parent: Optional[QObject] = None) -> None: super().__init__(parent) self._global_stack = None self._has_errors = True # Result of the error check, indicating whether there are errors in the stack - self._error_keys = set() # A set of settings keys that have errors - self._error_keys_in_progress = set() # The variable that stores the results of the currently in progress check + self._error_keys = set() # type: Set[str] # A set of settings keys that have errors + self._error_keys_in_progress = set() # type: Set[str] # The variable that stores the results of the currently in progress check - self._stacks_and_keys_to_check = None # a FIFO queue of tuples (stack, key) to check for errors + self._stacks_and_keys_to_check = None # type: Optional[deque] # a FIFO queue of tuples (stack, key) to check for errors self._need_to_check = False # Whether we need to schedule a new check or not. This flag is set when a new # error check needs to take place while there is already one running at the moment. @@ -42,7 +43,7 @@ class MachineErrorChecker(QObject): self._application = Application.getInstance() self._machine_manager = self._application.getMachineManager() - self._start_time = 0 # measure checking time + self._start_time = 0. # measure checking time # This timer delays the starting of error check so we can react less frequently if the user is frequently # changing settings. @@ -94,13 +95,13 @@ class MachineErrorChecker(QObject): # Start the error check for property changed # this is seperate from the startErrorCheck because it ignores a number property types - def startErrorCheckPropertyChanged(self, key, property_name): + def startErrorCheckPropertyChanged(self, key: str, property_name: str) -> None: if property_name != "value": return self.startErrorCheck() # Starts the error check timer to schedule a new error check. - def startErrorCheck(self, *args) -> None: + def startErrorCheck(self, *args: Any) -> None: if not self._check_in_progress: self._need_to_check = True self.needToWaitForResultChanged.emit() diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 5bcaf12bfb..dcd4adcfdb 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -14,6 +14,7 @@ if TYPE_CHECKING: from typing import Dict from cura.Machines.VariantNode import VariantNode + ## Represents a material in the container tree. # # Its subcontainers are quality profiles. diff --git a/cura/OAuth2/Models.py b/cura/OAuth2/Models.py index 468351c62b..dd935fef6e 100644 --- a/cura/OAuth2/Models.py +++ b/cura/OAuth2/Models.py @@ -1,10 +1,10 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional +from typing import Optional, Dict, Any class BaseModel: - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: self.__dict__.update(kwargs) @@ -53,9 +53,10 @@ class ResponseData(BaseModel): redirect_uri = None # type: Optional[str] content_type = "text/html" # type: str + ## Possible HTTP responses. HTTP_STATUS = { "OK": ResponseStatus(code = 200, message = "OK"), "NOT_FOUND": ResponseStatus(code = 404, message = "NOT FOUND"), "REDIRECT": ResponseStatus(code = 302, message = "REDIRECT") -} +} # type: Dict[str, ResponseStatus] diff --git a/cura/Operations/PlatformPhysicsOperation.py b/cura/Operations/PlatformPhysicsOperation.py index 9571679c3c..5aaa2ad94f 100644 --- a/cura/Operations/PlatformPhysicsOperation.py +++ b/cura/Operations/PlatformPhysicsOperation.py @@ -1,26 +1,27 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +from UM.Math.Vector import Vector from UM.Operations.Operation import Operation from UM.Operations.GroupedOperation import GroupedOperation from UM.Scene.SceneNode import SceneNode + ## A specialised operation designed specifically to modify the previous operation. class PlatformPhysicsOperation(Operation): - def __init__(self, node, translation): + def __init__(self, node: SceneNode, translation: Vector): super().__init__() self._node = node self._old_transformation = node.getLocalTransformation() self._translation = translation self._always_merge = True - def undo(self): + def undo(self) -> None: self._node.setTransformation(self._old_transformation) - def redo(self): + def redo(self) -> None: self._node.translate(self._translation, SceneNode.TransformSpace.World) - def mergeWith(self, other): + def mergeWith(self, other: Operation) -> GroupedOperation: group = GroupedOperation() group.addOperation(other) @@ -28,5 +29,5 @@ class PlatformPhysicsOperation(Operation): return group - def __repr__(self): + def __repr__(self) -> str: return "PlatformPhysicsOp.(trans.={0})".format(self._translation) diff --git a/cura/Operations/SetBuildPlateNumberOperation.py b/cura/Operations/SetBuildPlateNumberOperation.py index 96230639f9..fd48cf47d9 100644 --- a/cura/Operations/SetBuildPlateNumberOperation.py +++ b/cura/Operations/SetBuildPlateNumberOperation.py @@ -6,9 +6,9 @@ from UM.Operations.Operation import Operation from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator + ## Simple operation to set the buildplate number of a scenenode. class SetBuildPlateNumberOperation(Operation): - def __init__(self, node: SceneNode, build_plate_nr: int) -> None: super().__init__() self._node = node @@ -16,11 +16,11 @@ class SetBuildPlateNumberOperation(Operation): self._previous_build_plate_nr = None self._decorator_added = False - def undo(self): + def undo(self) -> None: if self._previous_build_plate_nr: self._node.callDecoration("setBuildPlateNumber", self._previous_build_plate_nr) - def redo(self): + def redo(self) -> None: stack = self._node.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. if not stack: self._node.addDecorator(SettingOverrideDecorator()) diff --git a/cura/Operations/SetParentOperation.py b/cura/Operations/SetParentOperation.py index 612c02f18d..7efe2618fd 100644 --- a/cura/Operations/SetParentOperation.py +++ b/cura/Operations/SetParentOperation.py @@ -1,36 +1,37 @@ # Copyright (c) 2016 Ultimaker B.V. # Uranium is released under the terms of the LGPLv3 or higher. +from typing import Optional from UM.Scene.SceneNode import SceneNode from UM.Operations import Operation from UM.Math.Vector import Vector -## An operation that parents a scene node to another scene node. +## An operation that parents a scene node to another scene node. class SetParentOperation(Operation.Operation): ## Initialises this SetParentOperation. # # \param node The node which will be reparented. # \param parent_node The node which will be the parent. - def __init__(self, node, parent_node): + def __init__(self, node: SceneNode, parent_node: Optional[SceneNode]): super().__init__() self._node = node self._parent = parent_node self._old_parent = node.getParent() # To restore the previous parent in case of an undo. ## Undoes the set-parent operation, restoring the old parent. - def undo(self): + def undo(self) -> None: self._set_parent(self._old_parent) ## Re-applies the set-parent operation. - def redo(self): + def redo(self) -> None: self._set_parent(self._parent) ## Sets the parent of the node while applying transformations to the world-transform of the node stays the same. # # \param new_parent The new parent. Note: this argument can be None, which would hide the node from the scene. - def _set_parent(self, new_parent): + def _set_parent(self, new_parent: Optional[SceneNode]) -> None: if new_parent: current_parent = self._node.getParent() if current_parent: @@ -59,5 +60,5 @@ class SetParentOperation(Operation.Operation): ## Returns a programmer-readable representation of this operation. # # \return A programmer-readable representation of this operation. - def __repr__(self): + def __repr__(self) -> str: return "SetParentOperation(node = {0}, parent_node={1})".format(self._node, self._parent) diff --git a/cura/PrinterOutput/Models/PrintJobOutputModel.py b/cura/PrinterOutput/Models/PrintJobOutputModel.py index b4296a5494..256999b96f 100644 --- a/cura/PrinterOutput/Models/PrintJobOutputModel.py +++ b/cura/PrinterOutput/Models/PrintJobOutputModel.py @@ -161,7 +161,7 @@ class PrintJobOutputModel(QObject): self._time_elapsed = new_time_elapsed self.timeElapsedChanged.emit() - def updateState(self, new_state): + def updateState(self, new_state: str) -> None: if self._state != new_state: self._state = new_state self.stateChanged.emit() diff --git a/cura/Scene/BlockSlicingDecorator.py b/cura/Scene/BlockSlicingDecorator.py index 0536e1635f..d9c9e0ac5e 100644 --- a/cura/Scene/BlockSlicingDecorator.py +++ b/cura/Scene/BlockSlicingDecorator.py @@ -9,4 +9,4 @@ class BlockSlicingDecorator(SceneNodeDecorator): super().__init__() def isBlockSlicing(self) -> bool: - return True + return True \ No newline at end of file diff --git a/cura/UI/MachineActionManager.py b/cura/UI/MachineActionManager.py index aa90e909e2..6efd3217a1 100644 --- a/cura/UI/MachineActionManager.py +++ b/cura/UI/MachineActionManager.py @@ -43,7 +43,7 @@ class MachineActionManager(QObject): # Dict of all actions that need to be done when first added by definition ID self._first_start_actions = {} # type: Dict[str, List[MachineAction]] - def initialize(self): + def initialize(self) -> None: # Add machine_action as plugin type PluginRegistry.addType("machine_action", self.addMachineAction) From 49211d323372ebbfcb6840e371d7f9fb95222ed1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 15:22:32 +0100 Subject: [PATCH 03/23] remove unused import --- cura/CuraApplication.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c1c2448a4d..e9ed1bf978 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -266,7 +266,6 @@ class CuraApplication(QtApplication): # Backups self._auto_save = None # type: Optional[AutoSave] - from cura.Settings.CuraContainerRegistry import CuraContainerRegistry self._container_registry_class = CuraContainerRegistry # Redefined here in order to please the typing. self._container_registry = None # type: CuraContainerRegistry From bb52ba6848dd8319a5cf10bd5e2eaba994f159a5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 15:32:53 +0100 Subject: [PATCH 04/23] Codestyle fixes --- cura/CuraApplication.py | 8 +++----- cura/LayerData.py | 3 +-- cura/LayerPolygon.py | 4 ++-- cura/PrintJobPreviewImageProvider.py | 9 +++++---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e9ed1bf978..d479b0fe18 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -15,7 +15,7 @@ from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qm from UM.i18n import i18nCatalog from UM.Application import Application -from UM.Decorators import override, deprecated +from UM.Decorators import override from UM.FlameProfiler import pyqtSlot from UM.Logger import Logger from UM.Message import Message @@ -1870,16 +1870,14 @@ class CuraApplication(QtApplication): main_window = QtApplication.getInstance().getMainWindow() if main_window: return main_window.width() - else: - return 0 + return 0 @pyqtSlot(result = int) def appHeight(self) -> int: main_window = QtApplication.getInstance().getMainWindow() if main_window: return main_window.height() - else: - return 0 + return 0 @pyqtSlot() def deleteAll(self, only_selectable: bool = True) -> None: diff --git a/cura/LayerData.py b/cura/LayerData.py index 796e71cbdc..72824591ab 100644 --- a/cura/LayerData.py +++ b/cura/LayerData.py @@ -16,8 +16,7 @@ class LayerData(MeshData): def getLayer(self, layer): if layer in self._layers: return self._layers[layer] - else: - return None + return None def getLayers(self): return self._layers diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index ca752e35ee..353d195100 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -61,7 +61,7 @@ class LayerPolygon: # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType # Should be generated in better way, not hardcoded. - self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype = numpy.bool) + self._is_infill_or_skin_type_map = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype = numpy.bool) self._build_cache_line_mesh_mask = None # type: Optional[numpy.ndarray] self._build_cache_needed_points = None # type: Optional[numpy.ndarray] @@ -153,7 +153,7 @@ class LayerPolygon: return self._color_map[line_types] def isInfillOrSkinType(self, line_types: numpy.ndarray) -> numpy.ndarray: - return self._isInfillOrSkinTypeMap[line_types] + return self._is_infill_or_skin_type_map[line_types] def lineMeshVertexCount(self) -> int: return self._vertex_end - self._vertex_begin diff --git a/cura/PrintJobPreviewImageProvider.py b/cura/PrintJobPreviewImageProvider.py index a8df5aa273..8b46c6db37 100644 --- a/cura/PrintJobPreviewImageProvider.py +++ b/cura/PrintJobPreviewImageProvider.py @@ -3,6 +3,7 @@ from PyQt5.QtQuick import QQuickImageProvider from PyQt5.QtCore import QSize from UM.Application import Application +from typing import Tuple class PrintJobPreviewImageProvider(QQuickImageProvider): @@ -10,7 +11,7 @@ class PrintJobPreviewImageProvider(QQuickImageProvider): super().__init__(QQuickImageProvider.Image) ## Request a new image. - def requestImage(self, id: str, size: QSize) -> QImage: + def requestImage(self, id: str, size: QSize) -> Tuple[QImage, QSize]: # The id will have an uuid and an increment separated by a slash. As we don't care about the value of the # increment, we need to strip that first. uuid = id[id.find("/") + 1:] @@ -22,6 +23,6 @@ class PrintJobPreviewImageProvider(QQuickImageProvider): if print_job.key == uuid: if print_job.getPreviewImage(): return print_job.getPreviewImage(), QSize(15, 15) - else: - return QImage(), QSize(15, 15) - return QImage(), QSize(15,15) \ No newline at end of file + + return QImage(), QSize(15, 15) + return QImage(), QSize(15, 15) \ No newline at end of file From e74f049142811d4ed5ce6406bc0075fd4f7c68ee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 16:37:46 +0100 Subject: [PATCH 05/23] Fix bunch of issues found by pylint --- cura/Arranging/Arrange.py | 4 +-- .../ArrangeObjectsAllBuildPlatesJob.py | 4 +-- cura/BuildVolume.py | 28 +++++++++++-------- cura/CrashHandler.py | 2 ++ cura/CuraActions.py | 4 +-- cura/CuraApplication.py | 10 +++---- cura/CuraView.py | 1 + cura/LayerPolygon.py | 6 ++-- cura/Machines/MachineNode.py | 4 +-- cura/Machines/QualityNode.py | 2 +- cura/MultiplyObjectsJob.py | 10 +++---- cura/PreviewPass.py | 3 -- cura/PrinterOutput/PrinterOutputDevice.py | 2 +- cura/Scene/BlockSlicingDecorator.py | 5 +++- cura/Scene/GCodeListDecorator.py | 4 +-- cura/Settings/CuraContainerRegistry.py | 5 ++-- 16 files changed, 50 insertions(+), 44 deletions(-) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index 8f7ba5ccfe..d6b8e44cea 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -69,7 +69,7 @@ class Arrange: points = copy.deepcopy(vertices._points) # After scaling (like up to 0.1 mm) the node might not have points - if len(points) == 0: + if not points: continue shape_arr = ShapeArray.fromPolygon(points, scale = scale) @@ -114,7 +114,7 @@ class Arrange: found_spot = True self.place(x, y, offset_shape_arr) # place the object in arranger else: - Logger.log("d", "Could not find spot!"), + Logger.log("d", "Could not find spot!") found_spot = False node.setPosition(Vector(200, center_y, 100)) return found_spot diff --git a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py index 89f613e180..7736efbeeb 100644 --- a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py +++ b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py @@ -29,7 +29,7 @@ class ArrangeArray: self._has_empty = False self._arrange = [] # type: List[Arrange] - def _update_first_empty(self): + def _updateFirstEmpty(self): for i, a in enumerate(self._arrange): if a.isEmpty: self._first_empty = i @@ -42,7 +42,7 @@ class ArrangeArray: new_arrange = Arrange.create(x = self._x, y = self._y, fixed_nodes = self._fixed_nodes) self._arrange.append(new_arrange) self._count += 1 - self._update_first_empty() + self._updateFirstEmpty() def count(self): return self._count diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index aba94e8c60..d7ab18b09e 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1,15 +1,21 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + +import numpy +import math + +from typing import List, Optional, TYPE_CHECKING, Any, Set, cast, Iterable, Dict + from UM.Mesh.MeshData import MeshData -from cura.Scene.CuraSceneNode import CuraSceneNode -from cura.Settings.ExtruderManager import ExtruderManager +from UM.Mesh.MeshBuilder import MeshBuilder + from UM.Application import Application #To modify the maximum zoom level. from UM.i18n import i18nCatalog from UM.Scene.Platform import Platform from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.Resources import Resources -from UM.Mesh.MeshBuilder import MeshBuilder + from UM.Math.Vector import Vector from UM.Math.Matrix import Matrix from UM.Math.Color import Color @@ -17,23 +23,23 @@ from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.Polygon import Polygon from UM.Message import Message from UM.Signal import Signal -from PyQt5.QtCore import QTimer from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL + from cura.Settings.GlobalStack import GlobalStack +from cura.Scene.CuraSceneNode import CuraSceneNode +from cura.Settings.ExtruderManager import ExtruderManager -catalog = i18nCatalog("cura") +from PyQt5.QtCore import QTimer -import numpy -import math - -from typing import List, Optional, TYPE_CHECKING, Any, Set, cast, Iterable, Dict if TYPE_CHECKING: from cura.CuraApplication import CuraApplication from cura.Settings.ExtruderStack import ExtruderStack from UM.Settings.ContainerStack import ContainerStack +catalog = i18nCatalog("cura") + # Radius of disallowed area in mm around prime. I.e. how much distance to keep from prime position. PRIME_CLEARANCE = 6.5 @@ -1012,13 +1018,13 @@ class BuildVolume(SceneNode): all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "value") all_types = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "type") for i, (setting_value, setting_type) in enumerate(zip(all_values, all_types)): - if not setting_value and (setting_type == "int" or setting_type == "float"): + if not setting_value and setting_type in ["int", "float"]: all_values[i] = 0 return all_values def _calculateBedAdhesionSize(self, used_extruders): if self._global_container_stack is None: - return + return None container_stack = self._global_container_stack adhesion_type = container_stack.getProperty("adhesion_type", "value") diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 6b33dc2d03..e72180887c 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -58,6 +58,8 @@ class CrashHandler: self.traceback = tb self.has_started = has_started self.dialog = None # Don't create a QDialog before there is a QApplication + self.cura_version = None + self.cura_locale = None Logger.log("c", "An uncaught error has occurred!") for line in traceback.format_exception(exception_type, value, tb): diff --git a/cura/CuraActions.py b/cura/CuraActions.py index b92abbe706..20c44c7916 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -3,17 +3,15 @@ from PyQt5.QtCore import QObject, QUrl from PyQt5.QtGui import QDesktopServices -from typing import List, Optional, cast +from typing import List, cast from UM.Event import CallFunctionEvent from UM.FlameProfiler import pyqtSlot -from UM.Math.Quaternion import Quaternion from UM.Math.Vector import Vector from UM.Scene.Selection import Selection from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation -from UM.Operations.RotateOperation import RotateOperation from UM.Operations.TranslateOperation import TranslateOperation import cura.CuraApplication diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d479b0fe18..f778cb0fab 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1442,7 +1442,7 @@ class CuraApplication(QtApplication): if center is not None: object_centers.append(center) - if object_centers and len(object_centers) > 0: + if object_centers: middle_x = sum([v.x for v in object_centers]) / len(object_centers) middle_y = sum([v.y for v in object_centers]) / len(object_centers) middle_z = sum([v.z for v in object_centers]) / len(object_centers) @@ -1492,7 +1492,7 @@ class CuraApplication(QtApplication): if center is not None: object_centers.append(center) - if object_centers and len(object_centers) > 0: + if object_centers: middle_x = sum([v.x for v in object_centers]) / len(object_centers) middle_y = sum([v.y for v in object_centers]) / len(object_centers) middle_z = sum([v.z for v in object_centers]) / len(object_centers) @@ -1674,7 +1674,7 @@ class CuraApplication(QtApplication): extension = os.path.splitext(f)[1] extension = extension.lower() filename = os.path.basename(f) - if len(self._currently_loading_files) > 0: + if self._currently_loading_files: # If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files if extension in self._non_sliceable_extensions: message = Message( @@ -1795,8 +1795,8 @@ class CuraApplication(QtApplication): node.addDecorator(build_plate_decorator) build_plate_decorator.setBuildPlateNumber(target_build_plate) - op = AddSceneNodeOperation(node, scene.getRoot()) - op.push() + operation = AddSceneNodeOperation(node, scene.getRoot()) + operation.push() node.callDecoration("setActiveExtruder", default_extruder_id) scene.sceneChanged.emit(node) diff --git a/cura/CuraView.py b/cura/CuraView.py index b358558dff..d594ea9571 100644 --- a/cura/CuraView.py +++ b/cura/CuraView.py @@ -26,6 +26,7 @@ class CuraView(View): def mainComponent(self) -> QUrl: return self.getDisplayComponent("main") + @pyqtProperty(QUrl, constant = True) def stageMenuComponent(self) -> QUrl: url = self.getDisplayComponent("menu") diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index 353d195100..6bdd0d53d1 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -1,10 +1,10 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - -from UM.Qt.QtApplication import QtApplication -from typing import Any, Optional import numpy +from typing import Optional + +from UM.Qt.QtApplication import QtApplication from UM.Logger import Logger diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index cee71160d2..0974c3dca7 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -176,9 +176,9 @@ class MachineNode(ContainerNode): # Find the global qualities for this printer. global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.quality_definition, global_quality = "True") # First try specific to this printer. - if len(global_qualities) == 0: # This printer doesn't override the global qualities. + if not global_qualities: # This printer doesn't override the global qualities. global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = "True") # Otherwise pick the global global qualities. - if len(global_qualities) == 0: # There are no global qualities either?! Something went very wrong, but we'll not crash and properly fill the tree. + if not global_qualities: # There are no global qualities either?! Something went very wrong, but we'll not crash and properly fill the tree. global_qualities = [cura.CuraApplication.CuraApplication.getInstance().empty_quality_container.getMetaData()] for global_quality in global_qualities: self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self) diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py index 45cd898db5..7696dfb117 100644 --- a/cura/Machines/QualityNode.py +++ b/cura/Machines/QualityNode.py @@ -41,4 +41,4 @@ class QualityNode(ContainerNode): self.intents[intent["id"]] = IntentNode(intent["id"], quality = self) self.intents["empty_intent"] = IntentNode("empty_intent", quality = self) - # Otherwise, there are no intents for global profiles. \ No newline at end of file + # Otherwise, there are no intents for global profiles. diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 5c25f70336..134e579746 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -47,7 +47,7 @@ class MultiplyObjectsJob(Job): nodes = [] not_fit_count = 0 - + found_solution_for_all = False for node in self._objects: # If object is part of a group, multiply group current_node = node @@ -66,7 +66,7 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True arranger.resetLastPriority() - for i in range(self._count): + for _ in range(self._count): # We do place the nodes one by one, as we want to yield in between. new_node = copy.deepcopy(node) solution_found = False @@ -98,10 +98,10 @@ class MultiplyObjectsJob(Job): Job.yieldThread() if nodes: - op = GroupedOperation() + operation = GroupedOperation() for new_node in nodes: - op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent())) - op.push() + operation.addOperation(AddSceneNodeOperation(new_node, current_node.getParent())) + operation.push() status_message.hide() if not found_solution_for_all: diff --git a/cura/PreviewPass.py b/cura/PreviewPass.py index 58205ba708..da60db2d99 100644 --- a/cura/PreviewPass.py +++ b/cura/PreviewPass.py @@ -17,9 +17,6 @@ from cura.Scene.CuraSceneNode import CuraSceneNode if TYPE_CHECKING: from UM.View.GL.ShaderProgram import ShaderProgram - -MYPY = False -if MYPY: from UM.Scene.Camera import Camera diff --git a/cura/PrinterOutput/PrinterOutputDevice.py b/cura/PrinterOutput/PrinterOutputDevice.py index b05e76ad2e..0e0ad488b1 100644 --- a/cura/PrinterOutput/PrinterOutputDevice.py +++ b/cura/PrinterOutput/PrinterOutputDevice.py @@ -148,7 +148,7 @@ class PrinterOutputDevice(QObject, OutputDevice): @pyqtProperty(QObject, notify = printersChanged) def activePrinter(self) -> Optional["PrinterOutputModel"]: - if len(self._printers): + if self._printers: return self._printers[0] return None diff --git a/cura/Scene/BlockSlicingDecorator.py b/cura/Scene/BlockSlicingDecorator.py index d9c9e0ac5e..3f0d57a83f 100644 --- a/cura/Scene/BlockSlicingDecorator.py +++ b/cura/Scene/BlockSlicingDecorator.py @@ -9,4 +9,7 @@ class BlockSlicingDecorator(SceneNodeDecorator): super().__init__() def isBlockSlicing(self) -> bool: - return True \ No newline at end of file + return True + + def __deepcopy__(self, memo): + return BlockSlicingDecorator() \ No newline at end of file diff --git a/cura/Scene/GCodeListDecorator.py b/cura/Scene/GCodeListDecorator.py index 6c52fb89bf..b8db706db3 100644 --- a/cura/Scene/GCodeListDecorator.py +++ b/cura/Scene/GCodeListDecorator.py @@ -17,8 +17,8 @@ class GCodeListDecorator(SceneNodeDecorator): def getGCodeList(self) -> List[str]: return self._gcode_list - def setGCodeList(self, list: List[str]) -> None: - self._gcode_list = list + def setGCodeList(self, gcode_list: List[str]) -> None: + self._gcode_list = gcode_list def __deepcopy__(self, memo) -> "GCodeListDecorator": copied_decorator = GCodeListDecorator() diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index f6028e9d4d..0ef09a1fac 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -15,7 +15,6 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerStack import ContainerStack from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.SettingInstance import SettingInstance -from UM.Application import Application from UM.Logger import Logger from UM.Message import Message from UM.Platform import Platform @@ -176,7 +175,7 @@ class CuraContainerRegistry(ContainerRegistry): if not file_name: return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags !", "Failed to import profile from {0}: {1}", file_name, "Invalid path")} - global_stack = Application.getInstance().getGlobalContainerStack() + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() if not global_stack: return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags !", "Can't import profile from {0} before a printer is added.", file_name)} container_tree = ContainerTree.getInstance() @@ -384,7 +383,7 @@ class CuraContainerRegistry(ContainerRegistry): if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") - global_stack = Application.getInstance().getGlobalContainerStack() + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() if global_stack is None: return None definition_id = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition From c86cc3ae5a08df085d59caba949efce414b1c92d Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 10 Jan 2020 16:48:45 +0100 Subject: [PATCH 06/23] Added 'dismiss' link and logic for removing the item from the dialog CURA-7090 --- .../qml/dialogs/CompatibilityDialog.qml | 19 +++++++++++++- .../Toolbox/src/SubscribedPackagesModel.py | 26 +++++++++++++++++-- plugins/Toolbox/src/Toolbox.py | 5 ++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index a6ce7fc865..93945871a3 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -104,7 +104,7 @@ UM.Dialog{ { width: parent.width property int lineHeight: 60 - visible: !model.is_compatible + visible: !model.is_compatible && !model.is_dismissed height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the incompatible packages here Image { @@ -117,6 +117,7 @@ UM.Dialog{ } Label { + id: packageName text: model.name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right @@ -125,6 +126,22 @@ UM.Dialog{ color: UM.Theme.getColor("text") elide: Text.ElideRight } + + Label + { + id: dismissLabel + text: "(Dismiss)" + font: UM.Theme.getFont("small") + anchors.right: parent.right + anchors.verticalCenter: packageIcon.verticalCenter + color: UM.Theme.getColor("text") + + MouseArea + { + anchors.fill: parent + onClicked: toolbox.dismissIncompatiblePackage(model.package_id) + } + } } } } diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index cf0d07c153..118048eec2 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -5,6 +5,10 @@ from PyQt5.QtCore import Qt from UM.Qt.ListModel import ListModel from cura import ApplicationMetadata +from PyQt5.QtCore import pyqtSlot + +from UM.Logger import Logger + class SubscribedPackagesModel(ListModel): def __init__(self, parent = None): @@ -18,6 +22,9 @@ class SubscribedPackagesModel(ListModel): self.addRoleName(Qt.UserRole + 1, "name") self.addRoleName(Qt.UserRole + 2, "icon_url") self.addRoleName(Qt.UserRole + 3, "is_compatible") + self.addRoleName(Qt.UserRole + 4, "is_dismissed") + self.addRoleName(Qt.UserRole + 5, "package_id") + def setMetadata(self, data): if self._metadata != data: @@ -33,7 +40,11 @@ class SubscribedPackagesModel(ListModel): for item in self._metadata: if item["package_id"] not in self._discrepancies: continue - package = {"name": item["display_name"], "sdk_versions": item["sdk_versions"]} + package = {"package_id": item["package_id"], + "name": item["display_name"], + "sdk_versions": item["sdk_versions"], + "is_dismissed": False + } if self._sdk_version not in item["sdk_versions"]: package.update({"is_compatible": False}) else: @@ -44,8 +55,10 @@ class SubscribedPackagesModel(ListModel): package.update({"icon_url": ""}) self._items.append(package) + print("All items:: %s" % self._items) self.setItems(self._items) + def hasCompatiblePackages(self) -> bool: has_compatible_items = False for item in self._items: @@ -58,4 +71,13 @@ class SubscribedPackagesModel(ListModel): for item in self._items: if item['is_compatible'] == False: has_incompatible_items = True - return has_incompatible_items \ No newline at end of file + return has_incompatible_items + + @pyqtSlot(str) + def setDismiss(self, package_id) -> None: + package_id_in_list_of_items = self.find(key="package_id", value=package_id) + if package_id_in_list_of_items != -1: + self.setProperty(package_id_in_list_of_items, property="is_dismissed", value=True) + Logger.debug("Package {} has been dismissed".format(package_id)) + + # Now store this package_id as DISMISSED somewhere in local files \ No newline at end of file diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index f28178b99e..c77e148140 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -556,6 +556,11 @@ class Toolbox(QObject, Extension): populated += 1 return populated == len(self._server_response_data.items()) + @pyqtSlot(str) + def dismissIncompatiblePackage(self, package_id): + print("---in toolbox: %s" % package_id) + self._models["subscribed_packages"].setDismiss(package_id) + # Make API Calls # -------------------------------------------------------------------------- def _makeRequestByType(self, request_type: str) -> None: From 6e133448099a33a52f6f9afdf49f50a57ec0686b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 16:42:01 +0100 Subject: [PATCH 07/23] Add WIP pylint configuration Note that it still needs a bit of tweaking --- .pylintrc | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000000..8a4200491a --- /dev/null +++ b/.pylintrc @@ -0,0 +1,108 @@ +# Copyright (c) 2019 Ultimaker B.V. +# This file contains the Pylint rules used in the stardust projects. + +# To configure PyLint as an external tool in PyCharm, create a new External Tool with the settings: +# +# Name: PyLint +# Program: Check with 'which pylint'. For example: ~/.local/bin/pylint +# Arguments: $FileDirName$ --rcfile=.pylintrc --msg-template='{abspath}:{line}:{column}:({symbol}):{msg_id}:{msg}' +# Working directory: $ContentRoot$ +# Output filters: $FILE_PATH$:$LINE$:$COLUMN$:.* +# +# You can add a keyboard shortcut in the keymap settings. To run Pylint to a project, select the module +# you want to check (e.g. cura folder) before running the external tool. +# +# If you find a better way to configure the external tool please edit this file. + +[MASTER] +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins=pylint_quotes + +# We expect double string quotes +string-quote=double-avoid-escape + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Add files or directories to the blacklist. They should be base names, not paths. +ignore=tests + +[REFACTORING] +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +[MESSAGES CONTROL] +# C0326: No space allowed around keyword argument assignment +# C0411: Ignore import order because the rules are different than in PyCharm, so automatic imports break lots of builds +# C0412: Ignore import order because the rules are different than in PyCharm, so automatic imports break lots of builds +# C0413: Ignore import order because the rules are different than in PyCharm, so automatic imports break lots of builds +# R0201: Method could be a function (no-self-use) +# R0401: Cyclic imports (cyclic-import) are used for typing +# R0801: Unfortunately the error is triggered for a lot of similar models (duplicate-code) +# R1710: Either all return statements in a function should return an expression, or none of them should. +# W0221: Parameters differ from overridden method (tornado http methods have a flexible number of parameters) +# W0511: Ignore warnings generated for TODOs in the code +# C0111: We don't use docstring +# C0303: Trailing whitespace isn't something we care about +# C4001: You can put " in a string if you escape it first... +disable=C0326,C0411,C0412,C0413,R0201,R0401,R0801,R1710,W0221,W0511, C0111, C0303,C4001 + +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=120 + +# Maximum number of lines in a module. +max-module-lines=500 + +good-names=os + +[BASIC] +# allow modules and functions to use PascalCase +module-rgx=[a-zA-Z0-9_]+$ +function-rgx= +method-rgx=([a-z_][a-z0-9_]{2,30}|([a-z_][A-Za-z0-9]{2,30}))$ + +[DESIGN] +# Maximum number of arguments for function / method. +max-args=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=8 + +# Maximum number of boolean expressions in an if statement. +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (R0903). +# We set this to 0 because our models and fields do not have methods. +min-public-methods=0 + +ignored-argument-names=arg|args|kwargs|_ + +[CLASSES] +defining-attr-methods=__init__,__new__,setUp,initialize + +[TYPECHECK] +ignored-classes=NotImplemented + +[VARIABLES] +dummy-variables-rgx=_+[a-z0-9_]{2,30} From bbee2e0a14fbf66dbbd0f3a1b5ffdd3a5badb245 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 17:43:40 +0100 Subject: [PATCH 08/23] Fix regex for private & long functions --- .pylintrc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 8a4200491a..07a53c47c7 100644 --- a/.pylintrc +++ b/.pylintrc @@ -62,7 +62,15 @@ good-names=os # allow modules and functions to use PascalCase module-rgx=[a-zA-Z0-9_]+$ function-rgx= -method-rgx=([a-z_][a-z0-9_]{2,30}|([a-z_][A-Za-z0-9]{2,30}))$ +## Allowed methods: +# getSomething +# _getSomething +# __getSomething +# __new__ +## Disallowed: +# _GET +# GetSomething +method-rgx=(_{,2}[a-z][A-Za-z0-9]*_{,2})$ [DESIGN] # Maximum number of arguments for function / method. From eaf8b3491e116239924de6f4d166755597af87a8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 17:49:47 +0100 Subject: [PATCH 09/23] Fix typing issue --- cura/Machines/MachineErrorChecker.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 9460578f45..7a5291dac5 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -8,12 +8,11 @@ from collections import deque from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty from typing import Optional, Any, Set -from UM.Application import Application from UM.Logger import Logger from UM.Settings.SettingDefinition import SettingDefinition from UM.Settings.Validator import ValidatorState - +import cura.CuraApplication # # This class performs setting error checks for the currently active machine. # @@ -40,7 +39,7 @@ class MachineErrorChecker(QObject): # error check needs to take place while there is already one running at the moment. self._check_in_progress = False # Whether there is an error check running in progress at the moment. - self._application = Application.getInstance() + self._application = cura.CuraApplication.CuraApplication.getInstance() self._machine_manager = self._application.getMachineManager() self._start_time = 0. # measure checking time From 2a28321588a9e7f570021c4a3bd33eede0208caf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 10 Jan 2020 18:03:20 +0100 Subject: [PATCH 10/23] Fix some more typing For some reaso the server is giving me different reuslts... --- cura/LayerPolygon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index 6bdd0d53d1..70d818f1ca 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -2,8 +2,9 @@ # Cura is released under the terms of the LGPLv3 or higher. import numpy -from typing import Optional +from typing import Optional, cast +from UM.Qt.Bindings.Theme import Theme from UM.Qt.QtApplication import QtApplication from UM.Logger import Logger @@ -232,7 +233,7 @@ class LayerPolygon: @classmethod def getColorMap(cls) -> numpy.ndarray: if cls.__color_map is None: - theme = QtApplication.getInstance().getTheme() + theme = cast(Theme, QtApplication.getInstance().getTheme()) cls.__color_map = numpy.array([ theme.getColor("layerview_none").getRgbF(), # NoneType theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type From 071326b890f92d8b16fb094a310f29ea03df9764 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Sat, 11 Jan 2020 01:11:49 +0100 Subject: [PATCH 11/23] Added logic for dismissing packages from the Incompatible list CURA-7090 --- plugins/Toolbox/src/SubscribedPackagesModel.py | 8 +++++--- plugins/Toolbox/src/Toolbox.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index 118048eec2..d2ed486de2 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -55,7 +55,6 @@ class SubscribedPackagesModel(ListModel): package.update({"icon_url": ""}) self._items.append(package) - print("All items:: %s" % self._items) self.setItems(self._items) @@ -73,11 +72,14 @@ class SubscribedPackagesModel(ListModel): has_incompatible_items = True return has_incompatible_items - @pyqtSlot(str) def setDismiss(self, package_id) -> None: package_id_in_list_of_items = self.find(key="package_id", value=package_id) if package_id_in_list_of_items != -1: self.setProperty(package_id_in_list_of_items, property="is_dismissed", value=True) Logger.debug("Package {} has been dismissed".format(package_id)) - # Now store this package_id as DISMISSED somewhere in local files \ No newline at end of file + def addDismissed(self, list_of_dismissed) -> None: + for package in list_of_dismissed: + item = self.find(key="package_id", value=package) + if item != -1: + self.setProperty(item, property="is_dismissed", value=True) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index c77e148140..f12cc3ba3d 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -558,8 +558,8 @@ class Toolbox(QObject, Extension): @pyqtSlot(str) def dismissIncompatiblePackage(self, package_id): - print("---in toolbox: %s" % package_id) self._models["subscribed_packages"].setDismiss(package_id) + self._package_manager.setAsDismissed(package_id) # Make API Calls # -------------------------------------------------------------------------- @@ -668,12 +668,17 @@ class Toolbox(QObject, Extension): def _checkCompatibilities(self, json_data) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() + user_dismissed_packages = list(self._package_manager.getDismissedPackages()) + + user_installed_packages += user_dismissed_packages # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace (discrepancy) package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) + if package_discrepancy: self._models["subscribed_packages"].addValue(package_discrepancy) self._models["subscribed_packages"].update() + self._models["subscribed_packages"].addDismissed(user_dismissed_packages) Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", @@ -802,11 +807,11 @@ class Toolbox(QObject, Extension): return cast(SubscribedPackagesModel, self._models["subscribed_packages"]) @pyqtProperty(bool, constant=True) - def has_compatible_packages(self) -> str: + def has_compatible_packages(self) -> bool: return self._models["subscribed_packages"].hasCompatiblePackages() @pyqtProperty(bool, constant=True) - def has_incompatible_packages(self) -> str: + def has_incompatible_packages(self) -> bool: return self._models["subscribed_packages"].hasIncompatiblePackages() @pyqtProperty(QObject, constant = True) From fbe38dc6587053f6cf45f8b0aaac1c234c3dcf71 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 13 Jan 2020 09:55:18 +0100 Subject: [PATCH 12/23] added typing, refactored some functions CURA-7090 --- .../qml/dialogs/CompatibilityDialog.qml | 6 +-- .../Toolbox/src/SubscribedPackagesModel.py | 51 +++++++++---------- plugins/Toolbox/src/Toolbox.py | 22 ++++---- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 93945871a3..c2cc1ce4d6 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -74,7 +74,7 @@ UM.Dialog{ } Label { - text: model.name + text: model.display_name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right anchors.leftMargin: UM.Theme.getSize("default_margin").width @@ -117,8 +117,7 @@ UM.Dialog{ } Label { - id: packageName - text: model.name + text: model.display_name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right anchors.leftMargin: UM.Theme.getSize("default_margin").width @@ -129,7 +128,6 @@ UM.Dialog{ Label { - id: dismissLabel text: "(Dismiss)" font: UM.Theme.getFont("small") anchors.right: parent.right diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index d2ed486de2..acbf4c13f7 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -4,10 +4,8 @@ from PyQt5.QtCore import Qt from UM.Qt.ListModel import ListModel from cura import ApplicationMetadata - -from PyQt5.QtCore import pyqtSlot - from UM.Logger import Logger +from typing import List class SubscribedPackagesModel(ListModel): @@ -19,32 +17,30 @@ class SubscribedPackagesModel(ListModel): self._discrepancies = None self._sdk_version = ApplicationMetadata.CuraSDKVersion - self.addRoleName(Qt.UserRole + 1, "name") - self.addRoleName(Qt.UserRole + 2, "icon_url") - self.addRoleName(Qt.UserRole + 3, "is_compatible") - self.addRoleName(Qt.UserRole + 4, "is_dismissed") - self.addRoleName(Qt.UserRole + 5, "package_id") + self.addRoleName(Qt.UserRole + 1, "package_id") + self.addRoleName(Qt.UserRole + 2, "display_name") + self.addRoleName(Qt.UserRole + 3, "icon_url") + self.addRoleName(Qt.UserRole + 4, "is_compatible") + self.addRoleName(Qt.UserRole + 5, "is_dismissed") - - def setMetadata(self, data): + def setMetadata(self, data) -> None: if self._metadata != data: self._metadata = data - def addValue(self, discrepancy): + def addDiscrepancies(self, discrepancy: List[str]) -> None: if self._discrepancies != discrepancy: self._discrepancies = discrepancy - def update(self): + def initialize(self) -> None: self._items.clear() for item in self._metadata: if item["package_id"] not in self._discrepancies: continue - package = {"package_id": item["package_id"], - "name": item["display_name"], - "sdk_versions": item["sdk_versions"], - "is_dismissed": False - } + package = {"package_id": item["package_id"], + "display_name": item["display_name"], + "sdk_versions": item["sdk_versions"], + "is_dismissed": False} if self._sdk_version not in item["sdk_versions"]: package.update({"is_compatible": False}) else: @@ -57,7 +53,6 @@ class SubscribedPackagesModel(ListModel): self._items.append(package) self.setItems(self._items) - def hasCompatiblePackages(self) -> bool: has_compatible_items = False for item in self._items: @@ -72,14 +67,16 @@ class SubscribedPackagesModel(ListModel): has_incompatible_items = True return has_incompatible_items - def setDismiss(self, package_id) -> None: - package_id_in_list_of_items = self.find(key="package_id", value=package_id) - if package_id_in_list_of_items != -1: - self.setProperty(package_id_in_list_of_items, property="is_dismissed", value=True) + # Sets the "is_compatible" to True for the given package, in memory + def dismissPackage(self, package_id: str) -> None: + package = self.find(key="package_id", value=package_id) + if package != -1: + self.setProperty(package, property="is_dismissed", value=True) Logger.debug("Package {} has been dismissed".format(package_id)) - def addDismissed(self, list_of_dismissed) -> None: - for package in list_of_dismissed: - item = self.find(key="package_id", value=package) - if item != -1: - self.setProperty(item, property="is_dismissed", value=True) + # Reads the dismissed_packages from user config file and applies them so they won't be shown in the Compatibility Dialog + def applyDismissedPackages(self, dismissed_packages: List[str]) -> None: + for package in dismissed_packages: + exists = self.find(key="package_id", value=package) + if exists != -1: + self.setProperty(exists, property="is_dismissed", value=True) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index f12cc3ba3d..eaaf8d94e9 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -557,9 +557,9 @@ class Toolbox(QObject, Extension): return populated == len(self._server_response_data.items()) @pyqtSlot(str) - def dismissIncompatiblePackage(self, package_id): - self._models["subscribed_packages"].setDismiss(package_id) - self._package_manager.setAsDismissed(package_id) + def dismissIncompatiblePackage(self, package_id: str): + self._models["subscribed_packages"].dismissPackage(package_id) # sets "is_compatible" to True, in-memory + self._package_manager.dismissPackage(package_id) # adds this package_id as dismissed in the user config file # Make API Calls # -------------------------------------------------------------------------- @@ -668,17 +668,15 @@ class Toolbox(QObject, Extension): def _checkCompatibilities(self, json_data) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() - user_dismissed_packages = list(self._package_manager.getDismissedPackages()) - - user_installed_packages += user_dismissed_packages - - # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace (discrepancy) + user_dismissed_packages = self._package_manager.getDismissedPackages() + if user_dismissed_packages: + user_installed_packages += user_dismissed_packages + # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) - if package_discrepancy: - self._models["subscribed_packages"].addValue(package_discrepancy) - self._models["subscribed_packages"].update() - self._models["subscribed_packages"].addDismissed(user_dismissed_packages) + self._models["subscribed_packages"].addDiscrepancies(package_discrepancy) + self._models["subscribed_packages"].initialize() + self._models["subscribed_packages"].applyDismissedPackages(user_dismissed_packages) Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", From 071cf5517e759cdd41583587323065b05f64d9eb Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 13 Jan 2020 10:37:40 +0100 Subject: [PATCH 13/23] linting CURA-7090 --- plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index c2cc1ce4d6..bd858b4fd9 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -125,7 +125,6 @@ UM.Dialog{ color: UM.Theme.getColor("text") elide: Text.ElideRight } - Label { text: "(Dismiss)" @@ -133,7 +132,6 @@ UM.Dialog{ anchors.right: parent.right anchors.verticalCenter: packageIcon.verticalCenter color: UM.Theme.getColor("text") - MouseArea { anchors.fill: parent From 7359492e115855a4fa3e943d91fe888c06a7727e Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 13 Jan 2020 11:44:23 +0100 Subject: [PATCH 14/23] Added a Tooltip over the Dismiss button. Changed the cursor on hover. --- .../qml/dialogs/CompatibilityDialog.qml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index bd858b4fd9..41020f2415 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -125,17 +125,24 @@ UM.Dialog{ color: UM.Theme.getColor("text") elide: Text.ElideRight } - Label + UM.TooltipArea { - text: "(Dismiss)" - font: UM.Theme.getFont("small") + width: childrenRect.width; + height: childrenRect.height; + text: catalog.i18nc("@info:tooltip", "Dismisses the package and won't be shown in this dialog anymore") anchors.right: parent.right anchors.verticalCenter: packageIcon.verticalCenter - color: UM.Theme.getColor("text") - MouseArea + Label { - anchors.fill: parent - onClicked: toolbox.dismissIncompatiblePackage(model.package_id) + text: "(Dismiss)" + font: UM.Theme.getFont("small") + color: UM.Theme.getColor("text") + MouseArea + { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: toolbox.dismissIncompatiblePackage(model.package_id) + } } } } From 122b6318feb590ee95a6da9f596ff38ab540fd86 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 13 Jan 2020 12:32:46 +0100 Subject: [PATCH 15/23] Remove dash if print job name is empty Otherwise it shows ' - Ultimaker Cura' here which looks a bit weird. --- resources/qml/Cura.qml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8dcf60018f..326edb6e97 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Ultimaker B.V. +// Copyright (c) 2020 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 @@ -21,7 +21,16 @@ UM.MainWindow id: base // Cura application window title - title: PrintInformation.jobName + " - " + catalog.i18nc("@title:window", CuraApplication.applicationDisplayName) + title: + { + let result = ""; + if(PrintInformation.jobName != "") + { + result += PrintInformation.jobName + " - "; + } + result += CuraApplication.applicationDisplayName; + return result; + } backgroundColor: UM.Theme.getColor("viewport_background") From 601a765f739341696f7c23b547da2a690aa87ee4 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 13 Jan 2020 13:44:36 +0100 Subject: [PATCH 16/23] Fix typo in comment --- cura/Machines/Models/MaterialBrandsModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py index 184d27f390..b0594cb286 100644 --- a/cura/Machines/Models/MaterialBrandsModel.py +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -34,7 +34,7 @@ class MaterialBrandsModel(BaseMaterialsModel): brand_item_list = [] brand_group_dict = {} - # Part 1: Generate the entire tree of brands -> material types -> spcific materials + # Part 1: Generate the entire tree of brands -> material types -> specific materials for root_material_id, container_node in self._available_materials.items(): # Do not include the materials from a to-be-removed package if bool(container_node.getMetaDataEntry("removed", False)): From d5cfca4df00f71269d1b6107050f0c763134e207 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 13 Jan 2020 13:44:51 +0100 Subject: [PATCH 17/23] Fix loading machine specific materials The container registry was incorrectly being searched with a variant_name == None, which always returned an empty printer-specific materials list. As a result, the generic material settings were always being loaded if there was no variant specifically indicated inside the fdm_material file. The printer specific values were consistently being ignored. This commit fixes that by removing the search with a variant_name==None which correctly returns the printer-specific materials list while loading the materials from the variant nodes. CURA-7087 --- cura/Machines/VariantNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/VariantNode.py b/cura/Machines/VariantNode.py index 550b5881a3..0f30782a91 100644 --- a/cura/Machines/VariantNode.py +++ b/cura/Machines/VariantNode.py @@ -51,7 +51,7 @@ class VariantNode(ContainerNode): # Find all the materials for this variant's name. else: # Printer has its own material profiles. Look for material profiles with this printer's definition. base_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = "fdmprinter") - printer_specific_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = self.machine.container_id, variant_name = None) + printer_specific_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = self.machine.container_id) variant_specific_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = self.machine.container_id, variant_name = self.variant_name) # If empty_variant, this won't return anything. materials_per_base_file = {material["base_file"]: material for material in base_materials} materials_per_base_file.update({material["base_file"]: material for material in printer_specific_materials}) # Printer-specific profiles override global ones. From b6216896cbeee47799671418ccc6fb0c5753d9a7 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 13 Jan 2020 14:43:11 +0100 Subject: [PATCH 18/23] Add a script to execute the CI scripts on a local Docker instance --- test-in-docker.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 test-in-docker.sh diff --git a/test-in-docker.sh b/test-in-docker.sh new file mode 100755 index 0000000000..e5a1116646 --- /dev/null +++ b/test-in-docker.sh @@ -0,0 +1,5 @@ +sudo rm -rf ./build ./Uranium +sudo docker run -it --rm \ + -v "$(pwd):/srv/cura" ultimaker/cura-build-environment \ + /srv/cura/docker/build.sh +sudo rm -rf ./build ./Uranium From e8f22beb276078a8abbae60a12583d7bacea699b Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 14 Jan 2020 11:50:47 +0100 Subject: [PATCH 19/23] Removed unnecessary function and a call to it. Added some typing. CURA-7090 --- plugins/Toolbox/src/SubscribedPackagesModel.py | 13 +++---------- plugins/Toolbox/src/Toolbox.py | 4 +--- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index acbf4c13f7..53d6eba932 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import Qt from UM.Qt.ListModel import ListModel from cura import ApplicationMetadata from UM.Logger import Logger -from typing import List +from typing import List, Dict, Any class SubscribedPackagesModel(ListModel): @@ -23,12 +23,12 @@ class SubscribedPackagesModel(ListModel): self.addRoleName(Qt.UserRole + 4, "is_compatible") self.addRoleName(Qt.UserRole + 5, "is_dismissed") - def setMetadata(self, data) -> None: + def setMetadata(self, data: List[Dict[str, List[Any]]]) -> None: if self._metadata != data: self._metadata = data def addDiscrepancies(self, discrepancy: List[str]) -> None: - if self._discrepancies != discrepancy: + if set(self._discrepancies) != set(discrepancy): # convert to set() to check if they are same list, regardless of list order self._discrepancies = discrepancy def initialize(self) -> None: @@ -73,10 +73,3 @@ class SubscribedPackagesModel(ListModel): if package != -1: self.setProperty(package, property="is_dismissed", value=True) Logger.debug("Package {} has been dismissed".format(package_id)) - - # Reads the dismissed_packages from user config file and applies them so they won't be shown in the Compatibility Dialog - def applyDismissedPackages(self, dismissed_packages: List[str]) -> None: - for package in dismissed_packages: - exists = self.find(key="package_id", value=package) - if exists != -1: - self.setProperty(exists, property="is_dismissed", value=True) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index eaaf8d94e9..dd01458a32 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -676,8 +676,7 @@ class Toolbox(QObject, Extension): if package_discrepancy: self._models["subscribed_packages"].addDiscrepancies(package_discrepancy) self._models["subscribed_packages"].initialize() - self._models["subscribed_packages"].applyDismissedPackages(user_dismissed_packages) - Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") + Logger.debug("Discrepancy found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", "\nDo you want to sync material and software packages with your account?"), @@ -688,7 +687,6 @@ class Toolbox(QObject, Extension): icon="", description="Sync your Cloud subscribed packages to your local environment.", button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) - sync_message.actionTriggered.connect(self._onSyncButtonClicked) sync_message.show() From 3872e8ffa38622792285f47f38b20bc9a3e94e21 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 14 Jan 2020 12:45:11 +0100 Subject: [PATCH 20/23] Move 'Toolbar' lower: Toolpanels could be obscured by objects-info. --- resources/qml/Cura.qml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 326edb6e97..d6f50f939b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -253,23 +253,6 @@ UM.MainWindow } } - Toolbar - { - // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by - // plugins) - id: toolbar - - property int mouseX: base.mouseX - property int mouseY: base.mouseY - - anchors - { - verticalCenter: parent.verticalCenter - left: parent.left - } - visible: CuraApplication.platformActivity && !PrintInformation.preSliced - } - ObjectSelector { id: objectSelector @@ -311,6 +294,23 @@ UM.MainWindow } } + Toolbar + { + // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by + // plugins) + id: toolbar + + property int mouseX: base.mouseX + property int mouseY: base.mouseY + + anchors + { + verticalCenter: parent.verticalCenter + left: parent.left + } + visible: CuraApplication.platformActivity && !PrintInformation.preSliced + } + // A hint for the loaded content view. Overlay items / controls can safely be placed in this area Item { id: mainSafeArea From 15a3922436e82eff3371a840b99b1e77549860b8 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 14 Jan 2020 14:07:55 +0100 Subject: [PATCH 21/23] Removed unnecessary checks CURA-7090 --- plugins/Toolbox/src/SubscribedPackagesModel.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index 53d6eba932..e6886f23ce 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -24,16 +24,13 @@ class SubscribedPackagesModel(ListModel): self.addRoleName(Qt.UserRole + 5, "is_dismissed") def setMetadata(self, data: List[Dict[str, List[Any]]]) -> None: - if self._metadata != data: - self._metadata = data + self._metadata = data def addDiscrepancies(self, discrepancy: List[str]) -> None: - if set(self._discrepancies) != set(discrepancy): # convert to set() to check if they are same list, regardless of list order - self._discrepancies = discrepancy + self._discrepancies = discrepancy def initialize(self) -> None: self._items.clear() - for item in self._metadata: if item["package_id"] not in self._discrepancies: continue @@ -49,7 +46,6 @@ class SubscribedPackagesModel(ListModel): package.update({"icon_url": item["icon_url"]}) except KeyError: # There is no 'icon_url" in the response payload for this package package.update({"icon_url": ""}) - self._items.append(package) self.setItems(self._items) From 6abf916ced39f122bbd2f17471f0a82809740c08 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 14 Jan 2020 21:56:06 +0100 Subject: [PATCH 22/23] Fix typing in __init__ methods to appease MYPY --- cura/CuraPackageManager.py | 2 +- cura/OneAtATimeIterator.py | 2 +- cura/Operations/PlatformPhysicsOperation.py | 2 +- cura/Operations/SetParentOperation.py | 2 +- plugins/PostProcessingPlugin/scripts/Stretch.py | 2 +- plugins/SimulationView/SimulationViewProxy.py | 2 +- .../src/Models/Http/ClusterPrinterMaterialStationSlot.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 99f2072644..a0d3a8d44a 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class CuraPackageManager(PackageManager): - def __init__(self, application: "QtApplication", parent: Optional["QObject"] = None): + def __init__(self, application: "QtApplication", parent: Optional["QObject"] = None) -> None: super().__init__(application, parent) def initialize(self) -> None: diff --git a/cura/OneAtATimeIterator.py b/cura/OneAtATimeIterator.py index b77e1f3982..3373f2104f 100644 --- a/cura/OneAtATimeIterator.py +++ b/cura/OneAtATimeIterator.py @@ -122,6 +122,6 @@ class _ObjectOrder: # \param order List of indices in which to print objects, ordered by printing # order. # \param todo: List of indices which are not yet inserted into the order list. - def __init__(self, order: List[SceneNode], todo: List[SceneNode]): + def __init__(self, order: List[SceneNode], todo: List[SceneNode]) -> None: self.order = order self.todo = todo diff --git a/cura/Operations/PlatformPhysicsOperation.py b/cura/Operations/PlatformPhysicsOperation.py index 5aaa2ad94f..0d69320eec 100644 --- a/cura/Operations/PlatformPhysicsOperation.py +++ b/cura/Operations/PlatformPhysicsOperation.py @@ -8,7 +8,7 @@ from UM.Scene.SceneNode import SceneNode ## A specialised operation designed specifically to modify the previous operation. class PlatformPhysicsOperation(Operation): - def __init__(self, node: SceneNode, translation: Vector): + def __init__(self, node: SceneNode, translation: Vector) -> None: super().__init__() self._node = node self._old_transformation = node.getLocalTransformation() diff --git a/cura/Operations/SetParentOperation.py b/cura/Operations/SetParentOperation.py index 7efe2618fd..7d71572a93 100644 --- a/cura/Operations/SetParentOperation.py +++ b/cura/Operations/SetParentOperation.py @@ -14,7 +14,7 @@ class SetParentOperation(Operation.Operation): # # \param node The node which will be reparented. # \param parent_node The node which will be the parent. - def __init__(self, node: SceneNode, parent_node: Optional[SceneNode]): + def __init__(self, node: SceneNode, parent_node: Optional[SceneNode]) -> None: super().__init__() self._node = node self._parent = parent_node diff --git a/plugins/PostProcessingPlugin/scripts/Stretch.py b/plugins/PostProcessingPlugin/scripts/Stretch.py index 20eef60ef2..3899bd2c50 100644 --- a/plugins/PostProcessingPlugin/scripts/Stretch.py +++ b/plugins/PostProcessingPlugin/scripts/Stretch.py @@ -35,7 +35,7 @@ class GCodeStep(): Class to store the current value of each G_Code parameter for any G-Code step """ - def __init__(self, step, in_relative_movement: bool = False): + def __init__(self, step, in_relative_movement: bool = False) -> None: self.step = step self.step_x = 0 self.step_y = 0 diff --git a/plugins/SimulationView/SimulationViewProxy.py b/plugins/SimulationView/SimulationViewProxy.py index 1183244ab3..ce2c336257 100644 --- a/plugins/SimulationView/SimulationViewProxy.py +++ b/plugins/SimulationView/SimulationViewProxy.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: class SimulationViewProxy(QObject): - def __init__(self, simulation_view: "SimulationView", parent=None): + def __init__(self, simulation_view: "SimulationView", parent=None) -> None: super().__init__(parent) self._simulation_view = simulation_view self._current_layer = 0 diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterMaterialStationSlot.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterMaterialStationSlot.py index 80deb1c9a8..b9c40592e5 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterMaterialStationSlot.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterMaterialStationSlot.py @@ -14,7 +14,7 @@ class ClusterPrinterMaterialStationSlot(ClusterPrintCoreConfiguration): # \param material_remaining: How much material is remaining on the spool (between 0 and 1, or -1 for missing data). # \param material_empty: Whether the material spool is too empty to be used. def __init__(self, slot_index: int, compatible: bool, material_remaining: float, - material_empty: Optional[bool] = False, **kwargs): + material_empty: Optional[bool] = False, **kwargs) -> None: self.slot_index = slot_index self.compatible = compatible self.material_remaining = material_remaining From 290761fccd6cf8f282c110def1de190d5544a223 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 15 Jan 2020 11:29:27 +0100 Subject: [PATCH 23/23] Fix arranger crash --- cura/Arranging/Arrange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index d6b8e44cea..c0aca9a893 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -69,7 +69,7 @@ class Arrange: points = copy.deepcopy(vertices._points) # After scaling (like up to 0.1 mm) the node might not have points - if not points: + if not points.size: continue shape_arr = ShapeArray.fromPolygon(points, scale = scale)