From 88c2e37ce45f97402372762296b3582cbcd1807e Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Mon, 12 Mar 2018 10:25:39 +0000 Subject: [PATCH 001/109] Added wall_min_flow and wall_min_flow_retract settings. Wall lines whose flow is < wall_min_flow are converted into travel moves. --- resources/definitions/fdmprinter.def.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index aef5533ead..d350024d17 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1181,6 +1181,28 @@ "value": "travel_compensate_overlapping_walls_enabled", "limit_to_extruder": "wall_x_extruder_nr", "settable_per_mesh": true + }, + "wall_min_flow": + { + "label": "Minimum Wall Flow", + "description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting you must have the wall overlap compensation enabled and also print the outer before inner walls.", + "unit": "%", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "type": "float", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "wall_min_flow_retract": + { + "label": "Prefer Retract", + "description": "If enabled, retraction is used rather than combing for travel moves that replace walls whose flow is below the minimum flow threshold.", + "type": "bool", + "default_value": false, + "enabled": "wall_min_flow > 0", + "settable_per_mesh": false, + "settable_per_extruder": true } } }, From 4aa89068d71933b6082197adb03025cef4b64185 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Tue, 13 Mar 2018 19:00:27 +0000 Subject: [PATCH 002/109] Now, wall_min_flow and wall_min_flow_retract are hidden if overlap compensation is disabled. --- resources/definitions/fdmprinter.def.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d350024d17..9ed52c81b5 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1185,12 +1185,13 @@ "wall_min_flow": { "label": "Minimum Wall Flow", - "description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting you must have the wall overlap compensation enabled and also print the outer before inner walls.", + "description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting, you must enable the wall overlap compensation and print the outer wall before inner walls.", "unit": "%", "minimum_value": "0", "maximum_value": "100", "default_value": 0, "type": "float", + "enabled": "travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1200,7 +1201,7 @@ "description": "If enabled, retraction is used rather than combing for travel moves that replace walls whose flow is below the minimum flow threshold.", "type": "bool", "default_value": false, - "enabled": "wall_min_flow > 0", + "enabled": "(travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled) and wall_min_flow > 0", "settable_per_mesh": false, "settable_per_extruder": true } From 051dd7a6e94c2dc1af7b1ccd9c04b8407be010e4 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 30 Apr 2018 16:47:14 +0200 Subject: [PATCH 003/109] WIP: Make application initialization and start up more clear - Create SingleInstance class to handling single instance stuff. - Instead of calling getInstance() everywhere, initialize each object explicitly in order when application starts and getInstance()s do not create instances any more and they merely return the created instances. - Only set initial values in construtor functions __init__(). Move the initialization of context-aware (i.e. things that depend on other things) to separate functions. - Split application creation and initialziation into several steps and them should be called explicitly in the correct order. --- cura/BuildVolume.py | 52 +- cura/CuraActions.py | 1 - cura/CuraApplication.py | 527 ++++++++---------- cura/MachineActionManager.py | 22 +- cura/PrintInformation.py | 30 +- cura/Scene/ConvexHullNode.py | 2 +- cura/Settings/ContainerManager.py | 72 ++- cura/Settings/CuraStackBuilder.py | 19 +- cura/Settings/ExtruderManager.py | 33 +- cura/SingleInstance.py | 103 ++++ cura_app.py | 76 ++- .../CuraEngineBackend/CuraEngineBackend.py | 2 +- .../USBPrinterOutputDeviceManager.py | 51 +- plugins/USBPrinting/__init__.py | 2 +- 14 files changed, 521 insertions(+), 471 deletions(-) create mode 100644 cura/SingleInstance.py diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 98e087707a..f249c3513d 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1,14 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import math +from typing import List, Optional + +import numpy + +from PyQt5.QtCore import QTimer -from cura.Scene.CuraSceneNode import CuraSceneNode -from cura.Settings.ExtruderManager import ExtruderManager -from UM.Settings.ContainerRegistry import ContainerRegistry 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.Application import Application from UM.Resources import Resources from UM.Mesh.MeshBuilder import MeshBuilder from UM.Math.Vector import Vector @@ -18,16 +20,14 @@ 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.Scene.CuraSceneNode import CuraSceneNode +from cura.Settings.ExtruderManager import ExtruderManager + catalog = i18nCatalog("cura") -import numpy -import math - -from typing import List, Optional - # Setting for clearance around the prime PRIME_CLEARANCE = 6.5 @@ -36,8 +36,10 @@ PRIME_CLEARANCE = 6.5 class BuildVolume(SceneNode): raftThicknessChanged = Signal() - def __init__(self, parent = None): + def __init__(self, application, parent = None): super().__init__(parent) + self._application = application + self._machine_manager = self._application.getMachineManager() self._volume_outline_color = None self._x_axis_color = None @@ -80,14 +82,14 @@ class BuildVolume(SceneNode): " with printed models."), title = catalog.i18nc("@info:title", "Build Volume")) self._global_container_stack = None - Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged) + self._application.globalContainerStackChanged.connect(self._onStackChanged) self._onStackChanged() self._engine_ready = False - Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + self._application.engineCreatedSignal.connect(self._onEngineCreated) self._has_errors = False - Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) + self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged) #Objects loaded at the moment. We are connected to the property changed events of these objects. self._scene_objects = set() @@ -105,14 +107,14 @@ class BuildVolume(SceneNode): # Must be after setting _build_volume_message, apparently that is used in getMachineManager. # activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality. # Therefore this works. - Application.getInstance().getMachineManager().activeQualityChanged.connect(self._onStackChanged) + self._machine_manager.activeQualityChanged.connect(self._onStackChanged) # This should also ways work, and it is semantically more correct, # but it does not update the disallowed areas after material change - Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged) + self._machine_manager.activeStackChanged.connect(self._onStackChanged) # Enable and disable extruder - Application.getInstance().getMachineManager().extruderChanged.connect(self.updateNodeBoundaryCheck) + self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck) # list of settings which were updated self._changed_settings_since_last_rebuild = [] @@ -122,7 +124,7 @@ class BuildVolume(SceneNode): self._scene_change_timer.start() def _onSceneChangeTimerFinished(self): - root = Application.getInstance().getController().getScene().getRoot() + root = self._application.getController().getScene().getRoot() new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.callDecoration("isSliceable")) if new_scene_objects != self._scene_objects: for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene. @@ -181,7 +183,7 @@ class BuildVolume(SceneNode): if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader")) - theme = Application.getInstance().getTheme() + theme = self._application.getTheme() self._grid_shader.setUniformValue("u_plateColor", Color(*theme.getColor("buildplate").getRgb())) self._grid_shader.setUniformValue("u_gridColor0", Color(*theme.getColor("buildplate_grid").getRgb())) self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb())) @@ -201,7 +203,7 @@ class BuildVolume(SceneNode): ## For every sliceable node, update node._outside_buildarea # def updateNodeBoundaryCheck(self): - root = Application.getInstance().getController().getScene().getRoot() + root = self._application.getController().getScene().getRoot() nodes = list(BreadthFirstIterator(root)) group_nodes = [] @@ -289,11 +291,11 @@ class BuildVolume(SceneNode): if not self._width or not self._height or not self._depth: return - if not Application.getInstance()._engine: + if not self._application._qml_engine: return if not self._volume_outline_color: - theme = Application.getInstance().getTheme() + theme = self._application.getTheme() self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb()) self._x_axis_color = Color(*theme.getColor("x_axis").getRgb()) self._y_axis_color = Color(*theme.getColor("y_axis").getRgb()) @@ -465,7 +467,7 @@ class BuildVolume(SceneNode): maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1) ) - Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds + self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds self.updateNodeBoundaryCheck() @@ -518,7 +520,7 @@ class BuildVolume(SceneNode): for extruder in extruders: extruder.propertyChanged.disconnect(self._onSettingPropertyChanged) - self._global_container_stack = Application.getInstance().getGlobalContainerStack() + self._global_container_stack = self._application.getGlobalContainerStack() if self._global_container_stack: self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) @@ -561,7 +563,7 @@ class BuildVolume(SceneNode): if setting_key == "print_sequence": machine_height = self._global_container_stack.getProperty("machine_height", "value") - if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: + if self._application.getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) if self._height < machine_height: self._build_volume_message.show() diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 75338f17b6..019893957f 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -12,7 +12,6 @@ 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.SetTransformOperation import SetTransformOperation from UM.Operations.TranslateOperation import TranslateOperation from cura.Operations.SetParentOperation import SetParentOperation diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d490b1cfbf..47b2dd25bf 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,9 +1,19 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, QTimer +import copy +import json +import os +import sys +import time + +import numpy + +from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtNetwork import QLocalServer -from PyQt5.QtNetwork import QLocalSocket +from PyQt5.QtGui import QColor, QIcon +from PyQt5.QtWidgets import QMessageBox +from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType from UM.Qt.QtApplication import QtApplication from UM.Scene.SceneNode import SceneNode @@ -74,6 +84,7 @@ from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager from cura.Machines.VariantManager import VariantManager +from .SingleInstance import SingleInstance from . import PlatformPhysics from . import BuildVolume from . import CameraAnimation @@ -93,22 +104,10 @@ from cura.Settings.ContainerManager import ContainerManager from cura.ObjectsModel import ObjectsModel -from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot -from PyQt5.QtGui import QColor, QIcon -from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType - -import sys -import numpy -import copy -import os -import argparse -import json -import time -numpy.seterr(all="ignore") +numpy.seterr(all = "ignore") MYPY = False if not MYPY: @@ -143,19 +142,164 @@ class CuraApplication(QtApplication): Q_ENUMS(ResourceTypes) - def __init__(self, **kwargs): + def __init__(self, *args, **kwargs): + super().__init__(name = "cura", + version = CuraVersion, + buildtype = CuraBuildType, + is_debug_mode = CuraDebugMode, + tray_icon_name = "cura-icon-32.png", + **kwargs) + + self.default_theme = "cura-light" + self._boot_loading_time = time.time() + + self._currently_loading_files = [] + self._non_sliceable_extensions = [] + + # Variables set from CLI + self._files_to_open = [] + self._use_single_instance = False + self._trigger_early_crash = False # For debug only + + self._single_instance = None + + self._cura_package_manager = None + + self._machine_action_manager = None + + self.empty_container = None + self.empty_definition_changes_container = None + self.empty_variant_container = None + self.empty_material_container = None + self.empty_quality_container = None + self.empty_quality_changes_container = None + + self._variant_manager = None + self._material_manager = None + self._quality_manager = None + self._machine_manager = None + self._extruder_manager = None + self._container_manager = None + + self._object_manager = None + self._build_plate_model = None + self._multi_build_plate_model = None + self._setting_visibility_presets_model = None + self._setting_inheritance_manager = None + self._simple_mode_settings_manager = None + self._cura_scene_controller = None + self._machine_error_checker = None + + self._quality_profile_drop_down_menu_model = None + self._custom_quality_profile_drop_down_menu_model = None + + self._physics = None + self._volume = None + self._output_devices = {} + self._print_information = None + self._previous_active_tool = None + self._platform_activity = False + self._scene_bounding_box = AxisAlignedBox.Null + + self._job_name = None + self._center_after_select = False + self._camera_animation = None + self._cura_actions = None + self.started = False + + self._message_box_callback = None + self._message_box_callback_arguments = [] + self._preferred_mimetype = "" + self._i18n_catalog = None + + self._currently_loading_files = [] + self._non_sliceable_extensions = [] + self._additional_components = {} # Components to add to certain areas in the interface + + self._open_file_queue = [] # A list of files to open (after the application has started) + + self._update_platform_activity_timer = None + + self._need_to_show_user_agreement = True + + from cura.Settings.CuraContainerRegistry import CuraContainerRegistry + self._container_registry_class = CuraContainerRegistry + + # Adds command line options to the command line parser. This should be called after the application is created and + # before the pre-start. + def addCommandLineOptions(self): + super().addCommandLineOptions() + self._cli_parser.add_argument("--help", "-h", + action = "store_true", + default = False, + help = "Show this help message and exit.") + self._cli_parser.add_argument("--single-instance", + dest = "single_instance", + action = "store_true", + default = False) + # >> For debugging + # Trigger an early crash, i.e. a crash that happens before the application enters its event loop. + self._cli_parser.add_argument("--trigger-early-crash", + dest = "trigger_early_crash", + action = "store_true", + default = False, + help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog.") + self._cli_parser.add_argument("file", nargs = "*", help = "Files to load after starting the application.") + + def parseCliOptions(self): + super().parseCliOptions() + + if self._cli_args.help: + self._cli_parser.print_help() + sys.exit(0) + + self._use_single_instance = self._cli_args.single_instance + self._trigger_early_crash = self._cli_args.trigger_early_crash + for filename in self._cli_args.file: + self._files_to_open.append(os.path.abspath(filename)) + + def initialize(self) -> None: + super().initialize() + + # Initialize the package manager to remove and install scheduled packages. + from cura.CuraPackageManager import CuraPackageManager + self._cura_package_manager = CuraPackageManager(self) + self._cura_package_manager.initialize() + + self.__sendCommandToSingleInstance() + self.__addExpectedResourceDirsAndSearchPaths() + self.__initializeSettingDefinitionsAndFunctions() + self.__addAllResourcesAndContainerResources() + self.__addAllEmptyContainers() + self.__setLatestResouceVersionsForVersionUpgrade() + + self._machine_action_manager = MachineActionManager.MachineActionManager(self) + self._machine_action_manager.initialize() + + def __sendCommandToSingleInstance(self): + self._single_instance = SingleInstance(self, self._files_to_open) + + # If we use single instance, try to connect to the single instance server, send commands, and then exit. + # If we cannot find an existing single instance server, this is the only instance, so just keep going. + if self._use_single_instance: + if self._single_instance.startClient(): + Logger.log("i", "Single instance commands were sent, exiting") + sys.exit(0) + + # Adds expected directory names and search paths for Resources. + def __addExpectedResourceDirsAndSearchPaths(self): # this list of dir names will be used by UM to detect an old cura directory for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants"]: Resources.addExpectedDirNameInData(dir_name) - Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources")) + Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources")) if not hasattr(sys, "frozen"): Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) - self._use_gui = True - self._open_file_queue = [] # Files to open when plug-ins are loaded. - + # Adds custom property types, settings types, and extra operators (functions) that need to be registered in + # SettingDefinition and SettingFunction. + def __initializeSettingDefinitionsAndFunctions(self): # Need to do this before ContainerRegistry tries to load the machines SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True, read_only = True) SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True, read_only = True) @@ -180,7 +324,8 @@ class CuraApplication(QtApplication): SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue) SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue) - ## Add the 4 types of profiles to storage. + # Adds all resources and container related resources. + def __addAllResourcesAndContainerResources(self) -> None: Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") Resources.addStorageType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants") @@ -191,20 +336,64 @@ class CuraApplication(QtApplication): Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") + self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") + self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") + self._container_registry.addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant") + self._container_registry.addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material") + self._container_registry.addResourceType(self.ResourceTypes.UserInstanceContainer, "user") + self._container_registry.addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train") + self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine") + self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") - ## Initialise the version upgrade manager with Cura's storage paths. - # Needs to be here to prevent circular dependencies. - import UM.VersionUpgradeManager + Resources.addType(self.ResourceTypes.QmlFiles, "qml") + Resources.addType(self.ResourceTypes.Firmware, "firmware") - UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions( + # Adds all empty containers. + def __addAllEmptyContainers(self) -> None: + # Add empty variant, material and quality containers. + # Since they are empty, they should never be serialized and instead just programmatically created. + # We need them to simplify the switching between materials. + empty_container = self._container_registry.getEmptyInstanceContainer() + self.empty_container = empty_container + + empty_definition_changes_container = copy.deepcopy(empty_container) + empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes") + empty_definition_changes_container.addMetaDataEntry("type", "definition_changes") + self._container_registry.addContainer(empty_definition_changes_container) + self.empty_definition_changes_container = empty_definition_changes_container + + empty_variant_container = copy.deepcopy(empty_container) + empty_variant_container.setMetaDataEntry("id", "empty_variant") + empty_variant_container.addMetaDataEntry("type", "variant") + self._container_registry.addContainer(empty_variant_container) + self.empty_variant_container = empty_variant_container + + empty_material_container = copy.deepcopy(empty_container) + empty_material_container.setMetaDataEntry("id", "empty_material") + empty_material_container.addMetaDataEntry("type", "material") + self._container_registry.addContainer(empty_material_container) + self.empty_material_container = empty_material_container + + empty_quality_container = copy.deepcopy(empty_container) + empty_quality_container.setMetaDataEntry("id", "empty_quality") + empty_quality_container.setName("Not Supported") + empty_quality_container.addMetaDataEntry("quality_type", "not_supported") + empty_quality_container.addMetaDataEntry("type", "quality") + empty_quality_container.addMetaDataEntry("supported", False) + self._container_registry.addContainer(empty_quality_container) + self.empty_quality_container = empty_quality_container + + empty_quality_changes_container = copy.deepcopy(empty_container) + empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes") + empty_quality_changes_container.addMetaDataEntry("type", "quality_changes") + empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported") + self._container_registry.addContainer(empty_quality_changes_container) + self.empty_quality_changes_container = empty_quality_changes_container + + # Initializes the version upgrade manager with by providing the paths for each resource type and the latest + # versions. + def __setLatestResouceVersionsForVersionUpgrade(self): + self._version_upgrade_manager.setCurrentVersions( { ("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"), ("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"), @@ -216,46 +405,9 @@ class CuraApplication(QtApplication): } ) - self._currently_loading_files = [] - self._non_sliceable_extensions = [] - - self._machine_action_manager = MachineActionManager.MachineActionManager() - self._machine_manager = None # This is initialized on demand. - self._extruder_manager = None - self._material_manager = None - self._quality_manager = None - self._object_manager = None - self._build_plate_model = None - self._multi_build_plate_model = None - self._setting_visibility_presets_model = None - self._setting_inheritance_manager = None - self._simple_mode_settings_manager = None - self._cura_scene_controller = None - self._machine_error_checker = None - - self._additional_components = {} # Components to add to certain areas in the interface - - super().__init__(name = "cura", - version = CuraVersion, - buildtype = CuraBuildType, - is_debug_mode = CuraDebugMode, - tray_icon_name = "cura-icon-32.png", - **kwargs) - - # Initialize the package manager to remove and install scheduled packages. - from cura.CuraPackageManager import CuraPackageManager - self._cura_package_manager = CuraPackageManager(self) - self._cura_package_manager.initialize() - - self.initialize() - - # FOR TESTING ONLY - if kwargs["parsed_command_line"].get("trigger_early_crash", False): - assert not "This crash is triggered by the trigger_early_crash command line argument." - - self._variant_manager = None - - self.default_theme = "cura-light" + # Runs preparations that needs to be done before the starting process. + def startSlashWindowPhase(self): + super().startSlashWindowPhase() self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) @@ -289,23 +441,6 @@ class CuraApplication(QtApplication): "SelectionTool", "TranslateTool" ]) - self._physics = None - self._volume = None - self._output_devices = {} - self._print_information = None - self._previous_active_tool = None - self._platform_activity = False - self._scene_bounding_box = AxisAlignedBox.Null - - self._job_name = None - self._center_after_select = False - self._camera_animation = None - self._cura_actions = None - self.started = False - - self._message_box_callback = None - self._message_box_callback_arguments = [] - self._preferred_mimetype = "" self._i18n_catalog = i18nCatalog("cura") self._update_platform_activity_timer = QTimer() @@ -318,53 +453,10 @@ class CuraApplication(QtApplication): self.getController().contextMenuRequested.connect(self._onContextMenuRequested) self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed) - Resources.addType(self.ResourceTypes.QmlFiles, "qml") - Resources.addType(self.ResourceTypes.Firmware, "firmware") - self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines...")) - # Add empty variant, material and quality containers. - # Since they are empty, they should never be serialized and instead just programmatically created. - # We need them to simplify the switching between materials. - empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() - self.empty_container = empty_container - - empty_definition_changes_container = copy.deepcopy(empty_container) - empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes") - empty_definition_changes_container.addMetaDataEntry("type", "definition_changes") - ContainerRegistry.getInstance().addContainer(empty_definition_changes_container) - self.empty_definition_changes_container = empty_definition_changes_container - - empty_variant_container = copy.deepcopy(empty_container) - empty_variant_container.setMetaDataEntry("id", "empty_variant") - empty_variant_container.addMetaDataEntry("type", "variant") - ContainerRegistry.getInstance().addContainer(empty_variant_container) - self.empty_variant_container = empty_variant_container - - empty_material_container = copy.deepcopy(empty_container) - empty_material_container.setMetaDataEntry("id", "empty_material") - empty_material_container.addMetaDataEntry("type", "material") - ContainerRegistry.getInstance().addContainer(empty_material_container) - self.empty_material_container = empty_material_container - - empty_quality_container = copy.deepcopy(empty_container) - empty_quality_container.setMetaDataEntry("id", "empty_quality") - empty_quality_container.setName("Not Supported") - empty_quality_container.addMetaDataEntry("quality_type", "not_supported") - empty_quality_container.addMetaDataEntry("type", "quality") - empty_quality_container.addMetaDataEntry("supported", False) - ContainerRegistry.getInstance().addContainer(empty_quality_container) - self.empty_quality_container = empty_quality_container - - empty_quality_changes_container = copy.deepcopy(empty_container) - empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes") - empty_quality_changes_container.addMetaDataEntry("type", "quality_changes") - empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported") - ContainerRegistry.getInstance().addContainer(empty_quality_changes_container) - self.empty_quality_changes_container = empty_quality_changes_container - - with ContainerRegistry.getInstance().lockFile(): - ContainerRegistry.getInstance().loadAllMetadata() + with self._container_registry.lockFile(): + self._container_registry.loadAllMetadata() # set the setting version for Preferences preferences = Preferences.getInstance() @@ -411,13 +503,10 @@ class CuraApplication(QtApplication): self.getCuraSceneController().setActiveBuildPlate(0) # Initialize - self._quality_profile_drop_down_menu_model = None - self._custom_quality_profile_drop_down_menu_model = None - CuraApplication.Created = True def _onEngineCreated(self): - self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) + self._qml_engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) @pyqtProperty(bool) def needToShowUserAgreement(self): @@ -515,10 +604,6 @@ class CuraApplication(QtApplication): def setDefaultPath(self, key, default_path): Preferences.getInstance().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile()) - @classmethod - def getStaticVersion(cls): - return CuraVersion - ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistry def _loadPlugins(self): @@ -543,127 +628,8 @@ class CuraApplication(QtApplication): self._plugins_loaded = True - @classmethod - def addCommandLineOptions(cls, parser, parsed_command_line = None): - if parsed_command_line is None: - parsed_command_line = {} - super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line) - parser.add_argument("file", nargs="*", help="Files to load after starting the application.") - parser.add_argument("--single-instance", action="store_true", default=False) - - # Set up a local socket server which listener which coordinates single instances Curas and accepts commands. - def _setUpSingleInstanceServer(self): - if self.getCommandLineOption("single_instance", False): - self.__single_instance_server = QLocalServer() - self.__single_instance_server.newConnection.connect(self._singleInstanceServerNewConnection) - self.__single_instance_server.listen("ultimaker-cura") - - def _singleInstanceServerNewConnection(self): - Logger.log("i", "New connection recevied on our single-instance server") - remote_cura_connection = self.__single_instance_server.nextPendingConnection() - - if remote_cura_connection is not None: - def readCommands(): - line = remote_cura_connection.readLine() - while len(line) != 0: # There is also a .canReadLine() - try: - payload = json.loads(str(line, encoding="ASCII").strip()) - command = payload["command"] - - # Command: Remove all models from the build plate. - if command == "clear-all": - self.deleteAll() - - # Command: Load a model file - elif command == "open": - self._openFile(payload["filePath"]) - # WARNING ^ this method is async and we really should wait until - # the file load is complete before processing more commands. - - # Command: Activate the window and bring it to the top. - elif command == "focus": - # Operating systems these days prevent windows from moving around by themselves. - # 'alert' or flashing the icon in the taskbar is the best thing we do now. - self.getMainWindow().alert(0) - - # Command: Close the socket connection. We're done. - elif command == "close-connection": - remote_cura_connection.close() - - else: - Logger.log("w", "Received an unrecognized command " + str(command)) - except json.decoder.JSONDecodeError as ex: - Logger.log("w", "Unable to parse JSON command in _singleInstanceServerNewConnection(): " + repr(ex)) - line = remote_cura_connection.readLine() - - remote_cura_connection.readyRead.connect(readCommands) - - ## Perform any checks before creating the main application. - # - # This should be called directly before creating an instance of CuraApplication. - # \returns \type{bool} True if the whole Cura app should continue running. - @classmethod - def preStartUp(cls, parser = None, parsed_command_line = None): - if parsed_command_line is None: - parsed_command_line = {} - - # Peek the arguments and look for the 'single-instance' flag. - if not parser: - parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace - CuraApplication.addCommandLineOptions(parser, parsed_command_line = parsed_command_line) - # Important: It is important to keep this line here! - # In Uranium we allow to pass unknown arguments to the final executable or script. - parsed_command_line.update(vars(parser.parse_known_args()[0])) - - if parsed_command_line["single_instance"]: - Logger.log("i", "Checking for the presence of an ready running Cura instance.") - single_instance_socket = QLocalSocket() - Logger.log("d", "preStartUp(): full server name: " + single_instance_socket.fullServerName()) - single_instance_socket.connectToServer("ultimaker-cura") - single_instance_socket.waitForConnected() - if single_instance_socket.state() == QLocalSocket.ConnectedState: - Logger.log("i", "Connection has been made to the single-instance Cura socket.") - - # Protocol is one line of JSON terminated with a carriage return. - # "command" field is required and holds the name of the command to execute. - # Other fields depend on the command. - - payload = {"command": "clear-all"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII")) - - payload = {"command": "focus"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII")) - - if len(parsed_command_line["file"]) != 0: - for filename in parsed_command_line["file"]: - payload = {"command": "open", "filePath": filename} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII")) - - payload = {"command": "close-connection"} - single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII")) - - single_instance_socket.flush() - single_instance_socket.waitForDisconnected() - return False - return True - - def preRun(self): - # Last check for unknown commandline arguments - parser = self.getCommandlineParser() - parser.add_argument("--help", "-h", - action='store_true', - default = False, - help = "Show this help message and exit." - ) - parsed_args = vars(parser.parse_args()) # This won't allow unknown arguments - if parsed_args["help"]: - parser.print_help() - sys.exit(0) - def run(self): - self.preRun() - - container_registry = ContainerRegistry.getInstance() + container_registry = self._container_registry Logger.log("i", "Initializing variant manager") self._variant_manager = VariantManager(container_registry) @@ -682,20 +648,25 @@ class CuraApplication(QtApplication): Logger.log("i", "Initializing machine manager") self._machine_manager = MachineManager(self) + Logger.log("i", "Initializing container manager") + self._container_manager = ContainerManager(self) + Logger.log("i", "Initializing machine error checker") self._machine_error_checker = MachineErrorChecker(self) self._machine_error_checker.initialize() - # Check if we should run as single instance or not - self._setUpSingleInstanceServer() + # Check if we should run as single instance or not. If so, set up a local socket server which listener which + # coordinates multiple Cura instances and accepts commands. + if self._use_single_instance: + self.__setUpSingleInstanceServer() # Setup scene and build volume root = self.getController().getScene().getRoot() - self._volume = BuildVolume.BuildVolume(self.getController().getScene().getRoot()) + self._volume = BuildVolume.BuildVolume(self, root) Arrange.build_volume = self._volume # initialize info objects - self._print_information = PrintInformation.PrintInformation() + self._print_information = PrintInformation.PrintInformation(self) self._cura_actions = CuraActions.CuraActions(self) # Initialize setting visibility presets model @@ -704,7 +675,7 @@ class CuraApplication(QtApplication): Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) # Detect in which mode to run and execute that mode - if self.getCommandLineOption("headless", False): + if self._is_headless: self.runWithoutGUI() else: self.runWithGUI() @@ -713,7 +684,6 @@ class CuraApplication(QtApplication): self.initializationFinished.emit() Logger.log("d", "Booting Cura took %s seconds", time.time() - self._boot_loading_time) - # For now use a timer to postpone some things that need to be done after the application and GUI are # initialized, for example opening files because they may show dialogs which can be closed due to incomplete # GUI initialization. @@ -725,8 +695,12 @@ class CuraApplication(QtApplication): self.exec_() + def __setUpSingleInstanceServer(self): + if self._use_single_instance: + self._single_instance.startServer() + def _onPostStart(self): - for file_name in self.getCommandLineOption("file", []): + for file_name in self._files_to_open: self.callLater(self._openFile, file_name) for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading. self.callLater(self._openFile, file_name) @@ -735,13 +709,10 @@ class CuraApplication(QtApplication): ## Run Cura without GUI elements and interaction (server mode). def runWithoutGUI(self): - self._use_gui = False self.closeSplash() ## Run Cura with GUI (desktop mode). def runWithGUI(self): - self._use_gui = True - self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene...")) controller = self.getController() @@ -791,9 +762,6 @@ class CuraApplication(QtApplication): # Hide the splash screen self.closeSplash() - def hasGui(self): - return self._use_gui - @pyqtSlot(result = QObject) def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel: return self._setting_visibility_presets_model @@ -808,7 +776,7 @@ class CuraApplication(QtApplication): def getExtruderManager(self, *args): if self._extruder_manager is None: - self._extruder_manager = ExtruderManager.createExtruderManager() + self._extruder_manager = ExtruderManager() return self._extruder_manager @pyqtSlot(result = QObject) @@ -932,7 +900,7 @@ class CuraApplication(QtApplication): qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel") qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel") - qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) @@ -1520,8 +1488,7 @@ class CuraApplication(QtApplication): # see GroupDecorator._onChildrenChanged def _createSplashScreen(self): - run_headless = self.getCommandLineOption("headless", False) - if run_headless: + if self._is_headless: return None return CuraSplashScreen.CuraSplashScreen() diff --git a/cura/MachineActionManager.py b/cura/MachineActionManager.py index 27b08ba8a1..65eb33b54c 100644 --- a/cura/MachineActionManager.py +++ b/cura/MachineActionManager.py @@ -1,13 +1,13 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type - -from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.DefinitionContainer import DefinitionContainer from PyQt5.QtCore import QObject + from UM.FlameProfiler import pyqtSlot +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type +from UM.Settings.DefinitionContainer import DefinitionContainer + ## Raised when trying to add an unknown machine action as a required action class UnknownMachineActionError(Exception): @@ -20,23 +20,27 @@ class NotUniqueMachineActionError(Exception): class MachineActionManager(QObject): - def __init__(self, parent = None): + def __init__(self, application, parent = None): super().__init__(parent) + self._application = application self._machine_actions = {} # Dict of all known machine actions self._required_actions = {} # Dict of all required actions by definition ID self._supported_actions = {} # Dict of all supported actions by definition ID self._first_start_actions = {} # Dict of all actions that need to be done when first added by definition ID + def initialize(self): + container_registry = self._application.getContainerRegistry() + # Add machine_action as plugin type PluginRegistry.addType("machine_action", self.addMachineAction) # Ensure that all containers that were registered before creation of this registry are also handled. # This should not have any effect, but it makes it safer if we ever refactor the order of things. - for container in ContainerRegistry.getInstance().findDefinitionContainers(): + for container in container_registry.findDefinitionContainers(): self._onContainerAdded(container) - ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) + container_registry.containerAdded.connect(self._onContainerAdded) def _onContainerAdded(self, container): ## Ensure that the actions are added to this manager diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 22f7ea5407..8c897230d5 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -1,24 +1,24 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Dict -import math -import os.path -import unicodedata import json +import math +import os +import unicodedata import re # To create abbreviations for printer names. +from typing import Dict from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot -from UM.Application import Application -from UM.Logger import Logger -from UM.Qt.Duration import Duration -from UM.Preferences import Preferences -from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog +from UM.Logger import Logger +from UM.Preferences import Preferences +from UM.Qt.Duration import Duration +from UM.Scene.SceneNode import SceneNode catalog = i18nCatalog("cura") + ## A class for processing and calculating minimum, current and maximum print time as well as managing the job name # # This class contains all the logic relating to calculation and slicing for the @@ -47,8 +47,9 @@ class PrintInformation(QObject): ActiveMachineChanged = 3 Other = 4 - def __init__(self, parent = None): + def __init__(self, application, parent = None): super().__init__(parent) + self._application = application self.initializeCuraMessagePrintTimeProperties() @@ -59,10 +60,10 @@ class PrintInformation(QObject): self._pre_sliced = False - self._backend = Application.getInstance().getBackend() + self._backend = self._application.getBackend() if self._backend: self._backend.printDurationMessage.connect(self._onPrintDurationMessage) - Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) + self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._base_name = "" self._abbr_machine = "" @@ -71,7 +72,6 @@ class PrintInformation(QObject): self._active_build_plate = 0 self._initVariablesWithBuildPlate(self._active_build_plate) - self._application = Application.getInstance() self._multi_build_plate_model = self._application.getMultiBuildPlateModel() self._application.globalContainerStackChanged.connect(self._updateJobName) @@ -199,7 +199,7 @@ class PrintInformation(QObject): self._current_print_time[build_plate_number].setDuration(total_estimated_time) def _calculateInformation(self, build_plate_number): - global_stack = Application.getInstance().getGlobalContainerStack() + global_stack = self._application.getGlobalContainerStack() if global_stack is None: return @@ -358,7 +358,7 @@ class PrintInformation(QObject): ## Created an acronymn-like abbreviated machine name from the currently active machine name # Called each time the global stack is switched def _setAbbreviatedMachineName(self): - global_container_stack = Application.getInstance().getGlobalContainerStack() + global_container_stack = self._application.getGlobalContainerStack() if not global_container_stack: self._abbr_machine = "" return diff --git a/cura/Scene/ConvexHullNode.py b/cura/Scene/ConvexHullNode.py index 1131958627..4c79c7d5dc 100644 --- a/cura/Scene/ConvexHullNode.py +++ b/cura/Scene/ConvexHullNode.py @@ -24,7 +24,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - if Application.getInstance().hasGui(): + if not Application.getInstance().getIsHeadLess(): self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb()) else: self._color = Color(0, 0, 0) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 0dc26207df..ea2821ce25 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -1,32 +1,25 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import os.path +import os import urllib.parse import uuid from typing import Dict, Union from PyQt5.QtCore import QObject, QUrl, QVariant -from UM.FlameProfiler import pyqtSlot from PyQt5.QtWidgets import QMessageBox -from UM.PluginRegistry import PluginRegistry -from UM.SaveFile import SaveFile -from UM.Platform import Platform -from UM.MimeTypeDatabase import MimeTypeDatabase - +from UM.i18n import i18nCatalog +from UM.FlameProfiler import pyqtSlot from UM.Logger import Logger -from UM.Application import Application +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError +from UM.Platform import Platform +from UM.SaveFile import SaveFile +from UM.Settings.ContainerFormatError import ContainerFormatError from UM.Settings.ContainerStack import ContainerStack from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.InstanceContainer import InstanceContainer -from UM.MimeTypeDatabase import MimeTypeNotFoundError -from UM.Settings.ContainerFormatError import ContainerFormatError -from UM.Settings.ContainerRegistry import ContainerRegistry -from cura.Settings.ExtruderManager import ExtruderManager -from UM.i18n import i18nCatalog - catalog = i18nCatalog("cura") @@ -36,11 +29,17 @@ catalog = i18nCatalog("cura") # from within QML. We want to be able to trigger things like removing a container # when a certain action happens. This can be done through this class. class ContainerManager(QObject): - def __init__(self, parent = None): - super().__init__(parent) - self._application = Application.getInstance() - self._container_registry = ContainerRegistry.getInstance() + def __init__(self, application): + if ContainerManager.__instance is not None: + raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) + ContainerManager.__instance = self + + super().__init__(parent = application) + + self._application = application + self._plugin_registry = self._application.getPluginRegistry() + self._container_registry = self._application.getContainerRegistry() self._machine_manager = self._application.getMachineManager() self._material_manager = self._application.getMaterialManager() self._container_name_filters = {} @@ -129,7 +128,7 @@ class ContainerManager(QObject): container.setProperty(setting_key, property_name, property_value) basefile = container.getMetaDataEntry("base_file", container_id) - for sibbling_container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): + for sibbling_container in self._container_registry.findInstanceContainers(base_file = basefile): if sibbling_container != container: sibbling_container.setProperty(setting_key, property_name, property_value) @@ -307,13 +306,15 @@ class ContainerManager(QObject): # \return \type{bool} True if successful, False if not. @pyqtSlot(result = bool) def updateQualityChanges(self): - global_stack = Application.getInstance().getGlobalContainerStack() + global_stack = self._machine_manager.activeMachine if not global_stack: return False self._machine_manager.blurSettings.emit() - for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + global_stack = self._machine_manager.activeMachine + extruder_stacks = list(global_stack.extruders.values()) + for stack in [global_stack] + extruder_stacks: # Find the quality_changes container for this stack and merge the contents of the top container into it. quality_changes = stack.qualityChanges if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()): @@ -334,13 +335,15 @@ class ContainerManager(QObject): send_emits_containers = [] # Go through global and extruder stacks and clear their topmost container (the user settings). - for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + global_stack = self._machine_manager.activeMachine + extruder_stacks = list(global_stack.extruders.values()) + for stack in [global_stack] + extruder_stacks: container = stack.userChanges container.clear() send_emits_containers.append(container) # user changes are possibly added to make the current setup match the current enabled extruders - Application.getInstance().getMachineManager().correctExtruderSettings() + self._machine_manager.correctExtruderSettings() for container in send_emits_containers: container.sendPostponedEmits() @@ -381,21 +384,6 @@ class ContainerManager(QObject): if container is not None: container.setMetaDataEntry("GUID", new_guid) - ## Get the singleton instance for this class. - @classmethod - def getInstance(cls) -> "ContainerManager": - # Note: Explicit use of class name to prevent issues with inheritance. - if ContainerManager.__instance is None: - ContainerManager.__instance = cls() - return ContainerManager.__instance - - __instance = None # type: "ContainerManager" - - # Factory function, used by QML - @staticmethod - def createContainerManager(engine, js_engine): - return ContainerManager.getInstance() - def _performMerge(self, merge_into, merge, clear_settings = True): if merge == merge_into: return @@ -415,7 +403,7 @@ class ContainerManager(QObject): serialize_type = "" try: - plugin_metadata = PluginRegistry.getInstance().getMetaData(plugin_id) + plugin_metadata = self._plugin_registry.getMetaData(plugin_id) if plugin_metadata: serialize_type = plugin_metadata["settings_container"]["type"] else: @@ -470,3 +458,9 @@ class ContainerManager(QObject): container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None] self._container_registry.exportQualityProfile(container_list, path, file_type) + + __instance = None + + @classmethod + def getInstance(cls, *args, **kwargs) -> "ContainerManager": + return cls.__instance diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 640489adb3..e401cad1ae 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -7,7 +7,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.Logger import Logger from UM.Settings.Interfaces import DefinitionContainerInterface from UM.Settings.InstanceContainer import InstanceContainer -from UM.Settings.ContainerRegistry import ContainerRegistry from cura.Machines.VariantManager import VariantType from .GlobalStack import GlobalStack @@ -29,7 +28,7 @@ class CuraStackBuilder: variant_manager = application.getVariantManager() material_manager = application.getMaterialManager() quality_manager = application.getQualityManager() - registry = ContainerRegistry.getInstance() + registry = application.getContainerRegistry() definitions = registry.findDefinitionContainers(id = definition_id) if not definitions: @@ -142,6 +141,7 @@ class CuraStackBuilder: variant_container, material_container, quality_container, global_stack) -> ExtruderStack: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() + registry = application.getContainerRegistry() stack = ExtruderStack(new_stack_id, parent = global_stack) stack.setName(extruder_definition.getName()) @@ -162,7 +162,7 @@ class CuraStackBuilder: # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems # setting properties will not result in incomplete containers being added. - ContainerRegistry.getInstance().addContainer(user_container) + registry.addContainer(user_container) return stack @@ -178,6 +178,7 @@ class CuraStackBuilder: variant_container, material_container, quality_container) -> GlobalStack: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() + registry = application.getContainerRegistry() stack = GlobalStack(new_stack_id) stack.setDefinition(definition) @@ -193,7 +194,7 @@ class CuraStackBuilder: stack.qualityChanges = application.empty_quality_changes_container stack.userChanges = user_container - ContainerRegistry.getInstance().addContainer(user_container) + registry.addContainer(user_container) return stack @@ -201,8 +202,10 @@ class CuraStackBuilder: def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str, is_global_stack: bool) -> "InstanceContainer": from cura.CuraApplication import CuraApplication + application = CuraApplication.getInstance() + registry = application.getContainerRegistry() - unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) + unique_container_name = registry.uniqueName(container_name) container = InstanceContainer(unique_container_name) container.setDefinition(definition_id) @@ -217,15 +220,17 @@ class CuraStackBuilder: @classmethod def createDefinitionChangesContainer(cls, container_stack, container_name): from cura.CuraApplication import CuraApplication + application = CuraApplication.getInstance() + registry = application.getContainerRegistry() - unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) + unique_container_name = registry.uniqueName(container_name) definition_changes_container = InstanceContainer(unique_container_name) definition_changes_container.setDefinition(container_stack.getBottom().getId()) definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - ContainerRegistry.getInstance().addContainer(definition_changes_container) + registry.addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index c2fe929957..5fecb7aa17 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -15,6 +15,7 @@ from UM.Settings.SettingFunction import SettingFunction from UM.Settings.SettingInstance import SettingInstance from UM.Settings.ContainerStack import ContainerStack from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext + from typing import Optional, List, TYPE_CHECKING, Union if TYPE_CHECKING: @@ -29,6 +30,10 @@ class ExtruderManager(QObject): ## Registers listeners and such to listen to changes to the extruders. def __init__(self, parent = None): + if ExtruderManager.__instance is not None: + raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) + ExtruderManager.__instance = self + super().__init__(parent) self._application = Application.getInstance() @@ -92,28 +97,6 @@ class ExtruderManager(QObject): if extruder.getId() == extruder_stack_id: return extruder.qualityChanges.getId() - ## The instance of the singleton pattern. - # - # It's None if the extruder manager hasn't been created yet. - __instance = None - - @staticmethod - def createExtruderManager(): - return ExtruderManager().getInstance() - - ## Gets an instance of the extruder manager, or creates one if no instance - # exists yet. - # - # This is an implementation of singleton. If an extruder manager already - # exists, it is re-used. - # - # \return The extruder manager. - @classmethod - def getInstance(cls) -> "ExtruderManager": - if not cls.__instance: - cls.__instance = ExtruderManager() - return cls.__instance - ## Changes the active extruder by index. # # \param index The index of the new active extruder. @@ -746,3 +729,9 @@ class ExtruderManager(QObject): resolved_value = global_stack.getProperty(key, "value", context = context) return resolved_value + + __instance = None + + @classmethod + def getInstance(cls, *args, **kwargs) -> "ExtruderManager": + return cls.__instance diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py new file mode 100644 index 0000000000..a664204d79 --- /dev/null +++ b/cura/SingleInstance.py @@ -0,0 +1,103 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import json +import os +from typing import List, Optional + +from PyQt5.QtNetwork import QLocalServer, QLocalSocket + +from UM.Logger import Logger + + +class SingleInstance: + + def __init__(self, application, files_to_open: Optional[List[str]]): + self._application = application + self._files_to_open = files_to_open + + self._single_instance_server = None + + # Starts a client that checks for a single instance server and sends the files that need to opened if the server + # exists. Returns True if the single instance server is found, otherwise False. + def startClient(self) -> bool: + Logger.log("i", "Checking for the presence of an ready running Cura instance.") + single_instance_socket = QLocalSocket(self._application) + Logger.log("d", "Full single instance server name: %s", single_instance_socket.fullServerName()) + single_instance_socket.connectToServer("ultimaker-cura") + single_instance_socket.waitForConnected(msecs = 3000) # wait for 3 seconds + + if single_instance_socket.state() != QLocalSocket.ConnectedState: + return False + + # We only send the files that need to be opened. + if not self._files_to_open: + Logger.log("i", "No file need to be opened, do nothing.") + return True + + if single_instance_socket.state() == QLocalSocket.ConnectedState: + Logger.log("i", "Connection has been made to the single-instance Cura socket.") + + # Protocol is one line of JSON terminated with a carriage return. + # "command" field is required and holds the name of the command to execute. + # Other fields depend on the command. + + payload = {"command": "clear-all"} + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + + payload = {"command": "focus"} + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + + for filename in self._files_to_open: + payload = {"command": "open", "filePath": os.path.abspath(filename)} + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + + payload = {"command": "close-connection"} + single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii")) + + single_instance_socket.flush() + single_instance_socket.waitForDisconnected() + return True + + def startServer(self) -> None: + self._single_instance_server = QLocalServer() + self._single_instance_server.newConnection.connect(self._onClientConnected) + self._single_instance_server.listen("ultimaker-cura") + + def _onClientConnected(self): + Logger.log("i", "New connection recevied on our single-instance server") + connection = self._single_instance_server.nextPendingConnection() + + if connection is not None: + connection.readyRead.connect(lambda c = connection: self.__readCommands(c)) + + def __readCommands(self, connection): + line = connection.readLine() + while len(line) != 0: # There is also a .canReadLine() + try: + payload = json.loads(str(line, encoding = "ascii").strip()) + command = payload["command"] + + # Command: Remove all models from the build plate. + if command == "clear-all": + self._application.callLater(lambda: self._application.deleteAll()) + + # Command: Load a model file + elif command == "open": + self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f)) + + # Command: Activate the window and bring it to the top. + elif command == "focus": + # Operating systems these days prevent windows from moving around by themselves. + # 'alert' or flashing the icon in the taskbar is the best thing we do now. + self._application.callLater(lambda: self._application.getMainWindow().alert(0)) + + # Command: Close the socket connection. We're done. + elif command == "close-connection": + connection.close() + + else: + Logger.log("w", "Received an unrecognized command " + str(command)) + except json.decoder.JSONDecodeError as ex: + Logger.log("w", "Unable to parse JSON command '%s': %s", line, repr(ex)) + line = connection.readLine() diff --git a/cura_app.py b/cura_app.py index 8a04b8fe09..18f9b6365f 100755 --- a/cura_app.py +++ b/cura_app.py @@ -1,51 +1,36 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import argparse +import faulthandler import os import sys from UM.Platform import Platform -parser = argparse.ArgumentParser(prog = "cura", - add_help = False) -parser.add_argument('--debug', - action='store_true', - default = False, - help = "Turn on the debug mode by setting this option." - ) -parser.add_argument('--trigger-early-crash', - dest = 'trigger_early_crash', - action = 'store_true', - default = False, - help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog." - ) -known_args = vars(parser.parse_known_args()[0]) -if not known_args["debug"]: - def get_cura_dir_path(): - if Platform.isWindows(): - return os.path.expanduser("~/AppData/Roaming/cura/") - elif Platform.isLinux(): - return os.path.expanduser("~/.local/share/cura") - elif Platform.isOSX(): - return os.path.expanduser("~/Library/Logs/cura") +# Gets the directory for stdout and stderr +def get_cura_dir_for_stdoutputs() -> str: + if Platform.isWindows(): + return os.path.expanduser("~/AppData/Roaming/cura/") + elif Platform.isLinux(): + return os.path.expanduser("~/.local/share/cura") + elif Platform.isOSX(): + return os.path.expanduser("~/Library/Logs/cura") - if hasattr(sys, "frozen"): - dirpath = get_cura_dir_path() - os.makedirs(dirpath, exist_ok = True) - sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w", encoding = "utf-8") - sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w", encoding = "utf-8") -import platform -import faulthandler +# Change stdout and stderr to files if Cura is running as a packaged application. +if hasattr(sys, "frozen"): + dir_path = get_cura_dir_for_stdoutputs() + os.makedirs(dir_path, exist_ok = True) + sys.stdout = open(os.path.join(dir_path, "stdout.log"), "w", encoding = "utf-8") + sys.stderr = open(os.path.join(dir_path, "stderr.log"), "w", encoding = "utf-8") -#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 + +# WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - linux_distro_name = platform.linux_distribution()[0].lower() # The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. try: import ctypes @@ -79,6 +64,7 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u sys.path.remove(PATH_real) sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0. + def exceptHook(hook_type, value, traceback): from cura.CrashHandler import CrashHandler from cura.CuraApplication import CuraApplication @@ -121,25 +107,25 @@ def exceptHook(hook_type, value, traceback): _crash_handler.early_crash_dialog.show() sys.exit(application.exec_()) -if not known_args["debug"]: - sys.excepthook = exceptHook + +# Set exception hook to use the crash dialog handler +sys.excepthook = exceptHook +# Enable dumping traceback for all threads +faulthandler.enable(all_threads = True) # Workaround for a race condition on certain systems where there # is a race condition between Arcus and PyQt. Importing Arcus # first seems to prevent Sip from going into a state where it # tries to create PyQt objects on a non-main thread. import Arcus #@UnusedImport -import cura.CuraApplication -import cura.Settings.CuraContainerRegistry +from cura.CuraApplication import CuraApplication -faulthandler.enable() +app = CuraApplication() +app.addCommandLineOptions() +app.parseCliOptions() +app.initialize() -# Force an instance of CuraContainerRegistry to be created and reused later. -cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance() +app.startSlashWindowPhase() +app.startPostSlashWindowPhase() -# This pre-start up check is needed to determine if we should start the application at all. -if not cura.CuraApplication.CuraApplication.preStartUp(parser = parser, parsed_command_line = known_args): - sys.exit(0) - -app = cura.CuraApplication.CuraApplication.getInstance(parser = parser, parsed_command_line = known_args) app.run() diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 654c1024bb..111b253dd8 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -275,7 +275,7 @@ class CuraEngineBackend(QObject, Backend): self.processingProgress.emit(0) Logger.log("d", "Attempting to kill the engine process") - if Application.getInstance().getCommandLineOption("external-backend", False): + if Application.getInstance().getUseExternalBackend(): return if self._process is not None: diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 6375c92879..5fc657e50f 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -1,10 +1,16 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Signal import Signal, signalemitter -from UM.Application import Application -from UM.Resources import Resources +import threading +import platform +import time +import serial.tools.list_ports + +from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal + from UM.Logger import Logger +from UM.Resources import Resources +from UM.Signal import Signal, signalemitter from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.i18n import i18nCatalog @@ -12,12 +18,6 @@ from cura.PrinterOutputDevice import ConnectionState from cura.CuraApplication import CuraApplication from . import USBPrinterOutputDevice -from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal - -import threading -import platform -import time -import serial.tools.list_ports i18n_catalog = i18nCatalog("cura") @@ -28,8 +28,14 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): addUSBOutputDeviceSignal = Signal() progressChanged = pyqtSignal() - def __init__(self, parent = None): + def __init__(self, application, parent = None): + if USBPrinterOutputDeviceManager.__instance is not None: + raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) + USBPrinterOutputDeviceManager.__instance = self + super().__init__(parent = parent) + self._application = application + self._serial_port_list = [] self._usb_output_devices = {} self._usb_output_devices_model = None @@ -38,11 +44,11 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): self._check_updates = True - Application.getInstance().applicationShuttingDown.connect(self.stop) + self._application.applicationShuttingDown.connect(self.stop) # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) - Application.getInstance().globalContainerStackChanged.connect(self.updateUSBPrinterOutputDevices) + self._application.globalContainerStackChanged.connect(self.updateUSBPrinterOutputDevices) # The method updates/reset the USB settings for all connected USB devices def updateUSBPrinterOutputDevices(self): @@ -69,7 +75,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): def _updateThread(self): while self._check_updates: - container_stack = Application.getInstance().getGlobalContainerStack() + container_stack = self._application.getGlobalContainerStack() if container_stack is None: time.sleep(5) continue @@ -80,19 +86,10 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): self._addRemovePorts(port_list) time.sleep(5) - ## Return the singleton instance of the USBPrinterManager - @classmethod - def getInstance(cls, engine = None, script_engine = None): - # Note: Explicit use of class name to prevent issues with inheritance. - if USBPrinterOutputDeviceManager._instance is None: - USBPrinterOutputDeviceManager._instance = cls() - - return USBPrinterOutputDeviceManager._instance - @pyqtSlot(result = str) def getDefaultFirmwareName(self): # Check if there is a valid global container stack - global_container_stack = Application.getInstance().getGlobalContainerStack() + global_container_stack = self._application.getGlobalContainerStack() if not global_container_stack: Logger.log("e", "There is no global container stack. Can not update firmware.") self._firmware_view.close() @@ -181,4 +178,8 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): return list(base_list) - _instance = None # type: "USBPrinterOutputDeviceManager" + __instance = None + + @classmethod + def getInstance(cls, *args, **kwargs) -> "USBPrinterOutputDeviceManager": + return cls.__instance diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 7bf5853c10..fd5488eead 100644 --- a/plugins/USBPrinting/__init__.py +++ b/plugins/USBPrinting/__init__.py @@ -15,4 +15,4 @@ def register(app): # We are violating the QT API here (as we use a factory, which is technically not allowed). # but we don't really have another means for doing this (and it seems to you know -work-) qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance) - return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()} + return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager(app)} From 42ecb126145eddebe0ebe7f5ae5ea303ef40ac05 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 8 May 2018 11:16:00 +0200 Subject: [PATCH 004/109] CURA-5164 Fix typo in the method signature --- cura/CuraApplication.py | 4 ++-- cura_app.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 47b2dd25bf..b8c417d8d3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -406,8 +406,8 @@ class CuraApplication(QtApplication): ) # Runs preparations that needs to be done before the starting process. - def startSlashWindowPhase(self): - super().startSlashWindowPhase() + def startSplashWindowPhase(self): + super().startSplashWindowPhase() self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) diff --git a/cura_app.py b/cura_app.py index 18f9b6365f..1495a9e526 100755 --- a/cura_app.py +++ b/cura_app.py @@ -125,7 +125,7 @@ app.addCommandLineOptions() app.parseCliOptions() app.initialize() -app.startSlashWindowPhase() -app.startPostSlashWindowPhase() +app.startSplashWindowPhase() +app.startPostSplashWindowPhase() app.run() From 43657010bae9de322b1d1b8a28df1714d10a54da Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 11 May 2018 08:42:03 +0200 Subject: [PATCH 005/109] CURA-5164 The Preferences is not a singleton class anymore since in some point several instances need to be created. - In the ThreeMFWorkspaceReader we need to create some temporal instances of Preferences that makes it not singleton anymore. - The current preferences are kept in the Application class and so all the calls to the preferences are changed to get the preferences from Application. - The method getInstance in Preferences is kept as deprecated since some external plugins. --- cura/CuraApplication.py | 24 +++++------ .../Models/SettingVisibilityPresetsModel.py | 4 +- cura/ObjectsModel.py | 5 +-- cura/PlatformPhysics.py | 9 ++-- cura/PrintInformation.py | 7 ++- cura/Settings/MachineManager.py | 29 ++++++------- plugins/3MFReader/ThreeMFWorkspaceReader.py | 4 +- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- plugins/AutoSave/AutoSave.py | 9 ++-- plugins/ChangeLogPlugin/ChangeLog.py | 9 ++-- .../CuraEngineBackend/CuraEngineBackend.py | 11 +++-- .../ProcessSlicedLayersJob.py | 3 +- .../FirmwareUpdateChecker.py | 9 ++-- .../FirmwareUpdateCheckerJob.py | 5 +-- plugins/GCodeReader/FlavorParser.py | 5 +-- plugins/GCodeReader/GCodeReader.py | 4 +- .../PerObjectSettingsTool.py | 5 +-- plugins/SimulationView/SimulationView.py | 43 +++++++++---------- plugins/SliceInfoPlugin/SliceInfo.py | 17 ++++---- plugins/SolidView/SolidView.py | 5 +-- .../UM3OutputDevicePlugin.py | 3 +- plugins/UserAgreement/UserAgreement.py | 9 ++-- 22 files changed, 102 insertions(+), 119 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b8c417d8d3..72f2d0af09 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -459,7 +459,7 @@ class CuraApplication(QtApplication): self._container_registry.loadAllMetadata() # set the setting version for Preferences - preferences = Preferences.getInstance() + preferences = self.getPreferences() preferences.addPreference("metadata/setting_version", 0) preferences.setValue("metadata/setting_version", self.SettingVersion) #Don't make it equal to the default so that the setting version always gets written to the file. @@ -484,7 +484,7 @@ class CuraApplication(QtApplication): preferences.addPreference("view/filter_current_build_plate", False) preferences.addPreference("cura/sidebar_collapsed", False) - self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") + self._need_to_show_user_agreement = not self.getPreferences().getValue("general/accepted_user_agreement") for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin @@ -546,7 +546,7 @@ class CuraApplication(QtApplication): def discardOrKeepProfileChanges(self): has_user_interaction = False - choice = Preferences.getInstance().getValue("cura/choice_on_profile_override") + choice = self.getPreferences().getValue("cura/choice_on_profile_override") if choice == "always_discard": # don't show dialog and DISCARD the profile self.discardOrKeepProfileChangesClosed("discard") @@ -597,12 +597,12 @@ class CuraApplication(QtApplication): @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): - default_path = Preferences.getInstance().getValue("local_file/%s" % key) + default_path = self.getPreferences().getValue("local_file/%s" % key) return QUrl.fromLocalFile(default_path) @pyqtSlot(str, str) def setDefaultPath(self, key, default_path): - Preferences.getInstance().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile()) + self.getPreferences().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile()) ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistry @@ -672,7 +672,7 @@ class CuraApplication(QtApplication): # Initialize setting visibility presets model self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self) default_visibility_profile = self._setting_visibility_presets_model.getItem(0) - Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) + self.getPreferences().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) # Detect in which mode to run and execute that mode if self._is_headless: @@ -934,7 +934,7 @@ class CuraApplication(QtApplication): # Default self.getController().setActiveTool("TranslateTool") - if Preferences.getInstance().getValue("view/center_on_select"): + if self.getPreferences().getValue("view/center_on_select"): self._center_after_select = True else: if self.getController().getActiveTool(): @@ -1329,15 +1329,15 @@ class CuraApplication(QtApplication): categories = list(set(categories)) categories.sort() joined = ";".join(categories) - if joined != Preferences.getInstance().getValue("cura/categories_expanded"): - Preferences.getInstance().setValue("cura/categories_expanded", joined) + if joined != self.getPreferences().getValue("cura/categories_expanded"): + self.getPreferences().setValue("cura/categories_expanded", joined) self.expandedCategoriesChanged.emit() expandedCategoriesChanged = pyqtSignal() @pyqtProperty("QStringList", notify = expandedCategoriesChanged) def expandedCategories(self): - return Preferences.getInstance().getValue("cura/categories_expanded").split(";") + return self.getPreferences().getValue("cura/categories_expanded").split(";") @pyqtSlot() def mergeSelected(self): @@ -1600,8 +1600,8 @@ class CuraApplication(QtApplication): self.fileLoaded.emit(filename) arrange_objects_on_load = ( - not Preferences.getInstance().getValue("cura/use_multi_build_plate") or - not Preferences.getInstance().getValue("cura/not_arrange_objects_on_load")) + not self.getPreferences().getValue("cura/use_multi_build_plate") or + not self.getPreferences().getValue("cura/not_arrange_objects_on_load")) target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1 root = self.getController().getScene().getRoot() diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index 8880ac5ce1..3062e83889 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -8,9 +8,9 @@ from configparser import ConfigParser from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot +from UM.Application import Application from UM.Logger import Logger from UM.Qt.ListModel import ListModel -from UM.Preferences import Preferences from UM.Resources import Resources from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError @@ -33,7 +33,7 @@ class SettingVisibilityPresetsModel(ListModel): basic_item = self.items[1] basic_visibile_settings = ";".join(basic_item["settings"]) - self._preferences = Preferences.getInstance() + self._preferences = Application.getInstance().getPreferences() # Preference to store which preset is currently selected self._preferences.addPreference("cura/active_setting_visibility_preset", "basic") # Preference that stores the "custom" set so it can always be restored (even after a restart) diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index cfe4320e28..f3c703d424 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -8,7 +8,6 @@ from UM.Qt.ListModel import ListModel from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.Scene.Selection import Selection -from UM.Preferences import Preferences from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -20,7 +19,7 @@ class ObjectsModel(ListModel): super().__init__() Application.getInstance().getController().getScene().sceneChanged.connect(self._updateDelayed) - Preferences.getInstance().preferenceChanged.connect(self._updateDelayed) + Application.getInstance().getPreferences().preferenceChanged.connect(self._updateDelayed) self._update_timer = QTimer() self._update_timer.setInterval(100) @@ -38,7 +37,7 @@ class ObjectsModel(ListModel): def _update(self, *args): nodes = [] - filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate") + filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number group_nr = 1 for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 1a5d6ef837..db28478597 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -8,7 +8,6 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Math.Vector import Vector from UM.Scene.Selection import Selection -from UM.Preferences import Preferences from cura.Scene.ConvexHullDecorator import ConvexHullDecorator @@ -36,8 +35,8 @@ class PlatformPhysics: self._max_overlap_checks = 10 # How many times should we try to find a new spot per tick? self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models - Preferences.getInstance().addPreference("physics/automatic_push_free", True) - Preferences.getInstance().addPreference("physics/automatic_drop_down", True) + Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", True) + Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True) def _onSceneChanged(self, source): if not source.getMeshData(): @@ -71,7 +70,7 @@ class PlatformPhysics: # Move it downwards if bottom is above platform move_vector = Vector() - if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down + if Application.getInstance().getPreferences().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y = -bbox.bottom + z_offset) @@ -80,7 +79,7 @@ class PlatformPhysics: node.addDecorator(ConvexHullDecorator()) # only push away objects if this node is a printing mesh - if not node.callDecoration("isNonPrintingMesh") and Preferences.getInstance().getValue("physics/automatic_push_free"): + if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 8c897230d5..debc765084 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -12,7 +12,6 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from UM.i18n import i18nCatalog from UM.Logger import Logger -from UM.Preferences import Preferences from UM.Qt.Duration import Duration from UM.Scene.SceneNode import SceneNode @@ -80,7 +79,7 @@ class PrintInformation(QObject): self._application.workspaceLoaded.connect(self.setProjectName) self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged) - Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + self._application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged) self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged) self._onActiveMaterialsChanged() @@ -208,7 +207,7 @@ class PrintInformation(QObject): self._material_costs[build_plate_number] = [] self._material_names[build_plate_number] = [] - material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings")) + material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings")) extruder_stacks = global_stack.extruders for position, extruder_stack in extruder_stacks.items(): @@ -299,7 +298,7 @@ class PrintInformation(QObject): self._setAbbreviatedMachineName() if self._pre_sliced: self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}", base_name) - elif Preferences.getInstance().getValue("cura/jobname_prefix"): + elif self._application.getInstance().getPreferences().getValue("cura/jobname_prefix"): # Don't add abbreviation if it already has the exact same abbreviation. if base_name.startswith(self._abbr_machine + "_"): self._job_name = base_name diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 436f9814db..8943734f3e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -16,7 +16,6 @@ from UM.FlameProfiler import pyqtSlot from UM import Util from UM.Application import Application -from UM.Preferences import Preferences from UM.Logger import Logger from UM.Message import Message @@ -97,12 +96,12 @@ class MachineManager(QObject): ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged) self.activeStackChanged.connect(self.activeStackValueChanged) - Preferences.getInstance().addPreference("cura/active_machine", "") + self._application.getPreferences().addPreference("cura/active_machine", "") self._global_event_keys = set() self._printer_output_devices = [] # type: List[PrinterOutputDevice] - Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) + self._application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) # There might already be some output devices by the time the signal is connected self._onOutputDevicesChanged() @@ -163,7 +162,7 @@ class MachineManager(QObject): rootMaterialChanged = pyqtSignal() def setInitialActiveMachine(self) -> None: - active_machine_id = Preferences.getInstance().getValue("cura/active_machine") + active_machine_id = self._application.getPreferences().getValue("cura/active_machine") if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id): # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) @@ -172,7 +171,7 @@ class MachineManager(QObject): def _onOutputDevicesChanged(self) -> None: self._printer_output_devices = [] - for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): + for printer_output_device in self._application.getOutputDeviceManager().getOutputDevices(): if isinstance(printer_output_device, PrinterOutputDevice): self._printer_output_devices.append(printer_output_device) @@ -239,7 +238,7 @@ class MachineManager(QObject): extruder_stack.containersChanged.disconnect(self._onContainersChanged) # Update the local global container stack reference - self._global_container_stack = Application.getInstance().getGlobalContainerStack() + self._global_container_stack = self._application.getGlobalContainerStack() if self._global_container_stack: self.updateDefaultExtruder() self.updateNumberExtrudersEnabled() @@ -247,7 +246,7 @@ class MachineManager(QObject): # after switching the global stack we reconnect all the signals and set the variant and material references if self._global_container_stack: - Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId()) + self._application.getPreferences().setValue("cura/active_machine", self._global_container_stack.getId()) self._global_container_stack.nameChanged.connect(self._onMachineNameChanged) self._global_container_stack.containersChanged.connect(self._onContainersChanged) @@ -271,7 +270,7 @@ class MachineManager(QObject): if self._global_container_stack.getId() in self.machine_extruder_material_update_dict: for func in self.machine_extruder_material_update_dict[self._global_container_stack.getId()]: - Application.getInstance().callLater(func) + self._application.callLater(func) del self.machine_extruder_material_update_dict[self._global_container_stack.getId()] self.activeQualityGroupChanged.emit() @@ -361,7 +360,7 @@ class MachineManager(QObject): global_stack = containers[0] ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder self._global_container_stack = global_stack - Application.getInstance().setGlobalContainerStack(global_stack) + self._application.setGlobalContainerStack(global_stack) ExtruderManager.getInstance()._globalContainerStackChanged() self._initMachineState(containers[0]) self._onGlobalContainerChanged() @@ -835,7 +834,7 @@ class MachineManager(QObject): ## Set the amount of extruders on the active machine (global stack) # \param extruder_count int the number of extruders to set def setActiveMachineExtruderCount(self, extruder_count: int) -> None: - extruder_manager = Application.getInstance().getExtruderManager() + extruder_manager = self._application.getExtruderManager() definition_changes_container = self._global_container_stack.definitionChanges if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container: @@ -852,7 +851,7 @@ class MachineManager(QObject): self.correctExtruderSettings() # Check to see if any objects are set to print with an extruder that will no longer exist - root_node = Application.getInstance().getController().getScene().getRoot() + root_node = self._application.getController().getScene().getRoot() for node in DepthFirstIterator(root_node): if node.getMeshData(): extruder_nr = node.callDecoration("getActiveExtruderPosition") @@ -885,7 +884,7 @@ class MachineManager(QObject): global_user_container.removeInstance(setting_key) # Signal that the global stack has changed - Application.getInstance().globalContainerStackChanged.emit() + self._application.globalContainerStackChanged.emit() self.forceUpdateAllSettings() @pyqtSlot(int, result = QObject) @@ -1117,7 +1116,7 @@ class MachineManager(QObject): def _setGlobalVariant(self, container_node): self._global_container_stack.variant = container_node.getContainer() if not self._global_container_stack.variant: - self._global_container_stack.variant = Application.getInstance().empty_variant_container + self._global_container_stack.variant = self._application.empty_variant_container def _setMaterial(self, position, container_node = None): if container_node and container_node.getContainer(): @@ -1359,7 +1358,7 @@ class MachineManager(QObject): self._setQualityGroup(quality_group) # See if we need to show the Discard or Keep changes screen - if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: self._application.discardOrKeepProfileChanges() @pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged) @@ -1373,7 +1372,7 @@ class MachineManager(QObject): self._setQualityChangesGroup(quality_changes_group) # See if we need to show the Discard or Keep changes screen - if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: self._application.discardOrKeepProfileChanges() @pyqtSlot() diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 5cd0ef5ced..8eecc3b5d9 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -456,7 +456,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): num_visible_settings = len(visible_settings_string.split(";")) active_mode = temp_preferences.getValue("cura/active_mode") if not active_mode: - active_mode = Preferences.getInstance().getValue("cura/active_mode") + active_mode = Application.getInstance().getPreferences().getValue("cura/active_mode") except KeyError: # If there is no preferences file, it's not a workspace, so notify user of failure. Logger.log("w", "File %s is not a valid workspace.", file_name) @@ -575,7 +575,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): temp_preferences.deserialize(serialized) # Copy a number of settings from the temp preferences to the global - global_preferences = Preferences.getInstance() + global_preferences = application.getInstance().getPreferences() visible_settings = temp_preferences.getValue("general/visible_settings") if visible_settings is None: diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index e948f62337..33df0bfe90 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -51,7 +51,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): self._writeContainerToArchive(container, archive) # Write preferences to archive - original_preferences = Preferences.getInstance() #Copy only the preferences that we use to the workspace. + original_preferences = Application.getInstance().getPreferences() #Copy only the preferences that we use to the workspace. temp_preferences = Preferences() for preference in {"general/visible_settings", "cura/active_mode", "cura/categories_expanded"}: temp_preferences.addPreference(preference, None) diff --git a/plugins/AutoSave/AutoSave.py b/plugins/AutoSave/AutoSave.py index 5fdac502b5..5025cc563c 100644 --- a/plugins/AutoSave/AutoSave.py +++ b/plugins/AutoSave/AutoSave.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import QTimer from UM.Extension import Extension -from UM.Preferences import Preferences from UM.Application import Application from UM.Resources import Resources from UM.Logger import Logger @@ -14,14 +13,14 @@ class AutoSave(Extension): def __init__(self): super().__init__() - Preferences.getInstance().preferenceChanged.connect(self._triggerTimer) + Application.getInstance().getPreferences().preferenceChanged.connect(self._triggerTimer) self._global_stack = None - Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10) + Application.getInstance().getPreferences().addPreference("cura/autosave_delay", 1000 * 10) self._change_timer = QTimer() - self._change_timer.setInterval(Preferences.getInstance().getValue("cura/autosave_delay")) + self._change_timer.setInterval(Application.getInstance().getPreferences().getValue("cura/autosave_delay")) self._change_timer.setSingleShot(True) self._saving = False @@ -72,6 +71,6 @@ class AutoSave(Extension): Application.getInstance().saveSettings() - Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences, Application.getInstance().getApplicationName() + ".cfg")) + Application.getInstance().getPreferences().writeToFile(Resources.getStoragePath(Resources.Preferences, Application.getInstance().getApplicationName() + ".cfg")) self._saving = False diff --git a/plugins/ChangeLogPlugin/ChangeLog.py b/plugins/ChangeLogPlugin/ChangeLog.py index 030d854d3f..e93d5c4395 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.py +++ b/plugins/ChangeLogPlugin/ChangeLog.py @@ -3,7 +3,6 @@ from UM.i18n import i18nCatalog from UM.Extension import Extension -from UM.Preferences import Preferences from UM.Application import Application from UM.PluginRegistry import PluginRegistry from UM.Version import Version @@ -29,7 +28,7 @@ class ChangeLog(Extension, QObject,): self._change_logs = None Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "2.0.0") #First version of CURA with uranium + Application.getInstance().getPreferences().addPreference("general/latest_version_changelog_shown", "2.0.0") #First version of CURA with uranium self.addMenuItem(catalog.i18nc("@item:inmenu", "Show Changelog"), self.showChangelog) def getChangeLogs(self): @@ -79,12 +78,12 @@ class ChangeLog(Extension, QObject,): if not self._current_app_version: return #We're on dev branch. - if Preferences.getInstance().getValue("general/latest_version_changelog_shown") == "master": + if Application.getInstance().getPreferences().getValue("general/latest_version_changelog_shown") == "master": latest_version_shown = Version("0.0.0") else: - latest_version_shown = Version(Preferences.getInstance().getValue("general/latest_version_changelog_shown")) + latest_version_shown = Version(Application.getInstance().getPreferences().getValue("general/latest_version_changelog_shown")) - Preferences.getInstance().setValue("general/latest_version_changelog_shown", Application.getInstance().getVersion()) + Application.getInstance().getPreferences().setValue("general/latest_version_changelog_shown", Application.getInstance().getVersion()) # Do not show the changelog when there is no global container stack # This implies we are running Cura for the first time. diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 111b253dd8..d298ff1aca 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -4,7 +4,6 @@ from UM.Backend.Backend import Backend, BackendState from UM.Application import Application from UM.Scene.SceneNode import SceneNode -from UM.Preferences import Preferences from UM.Signal import Signal from UM.Logger import Logger from UM.Message import Message @@ -72,7 +71,7 @@ class CuraEngineBackend(QObject, Backend): Logger.log("i", "Found CuraEngine at: %s", default_engine_location) default_engine_location = os.path.abspath(default_engine_location) - Preferences.getInstance().addPreference("backend/location", default_engine_location) + Application.getInstance().getPreferences().addPreference("backend/location", default_engine_location) # Workaround to disable layer view processing if layer view is not active. self._layer_view_active = False @@ -121,7 +120,7 @@ class CuraEngineBackend(QObject, Backend): self._slice_start_time = None self._is_disabled = False - Preferences.getInstance().addPreference("general/auto_slice", False) + Application.getInstance().getPreferences().addPreference("general/auto_slice", False) self._use_timer = False # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. @@ -131,7 +130,7 @@ class CuraEngineBackend(QObject, Backend): self._change_timer.setSingleShot(True) self._change_timer.setInterval(500) self.determineAutoSlicing() - Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged) self._application.initializationFinished.connect(self.initialize) @@ -170,7 +169,7 @@ class CuraEngineBackend(QObject, Backend): # \return list of commands and args / parameters. def getEngineCommand(self): json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") - return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] + return [Application.getInstance().getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] ## Emitted when we get a message containing print duration and material amount. # This also implies the slicing has finished. @@ -408,7 +407,7 @@ class CuraEngineBackend(QObject, Backend): enable_timer = True self._is_disabled = False - if not Preferences.getInstance().getValue("general/auto_slice"): + if not Application.getInstance().getPreferences().getValue("general/auto_slice"): enable_timer = False for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("isBlockSlicing"): diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index cbc097bb33..3bd6d79198 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -6,7 +6,6 @@ import gc from UM.Job import Job from UM.Application import Application from UM.Mesh.MeshData import MeshData -from UM.Preferences import Preferences from UM.View.GL.OpenGLContext import OpenGLContext from UM.Message import Message @@ -199,7 +198,7 @@ class ProcessSlicedLayersJob(Job): material_color_map[0, :] = color # We have to scale the colors for compatibility mode - if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")): + if OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")): line_type_brightness = 0.5 # for compatibility mode else: line_type_brightness = 1.0 diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py index 23a040f2e2..982f9d0b24 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py @@ -5,8 +5,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtGui import QDesktopServices from UM.Extension import Extension -from UM.Preferences import Preferences -from UM.Logger import Logger +from UM.Application import Application from UM.i18n import i18nCatalog from cura.Settings.GlobalStack import GlobalStack @@ -27,12 +26,12 @@ class FirmwareUpdateChecker(Extension): # Initialize the Preference called `latest_checked_firmware` that stores the last version # checked for the UM3. In the future if we need to check other printers' firmware - Preferences.getInstance().addPreference("info/latest_checked_firmware", "") + Application.getInstance().getPreferences().addPreference("info/latest_checked_firmware", "") # Listen to a Signal that indicates a change in the list of printers, just if the user has enabled the # 'check for updates' option - Preferences.getInstance().addPreference("info/automatic_update_check", True) - if Preferences.getInstance().getValue("info/automatic_update_check"): + Application.getInstance().getPreferences().addPreference("info/automatic_update_check", True) + if Application.getInstance().getPreferences().getValue("info/automatic_update_check"): ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) self._download_url = None diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 66ee43209f..c3b766e7b9 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -1,7 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Preferences import Preferences from UM.Application import Application from UM.Message import Message from UM.Logger import Logger @@ -51,11 +50,11 @@ class FirmwareUpdateCheckerJob(Job): current_version = reader(current_version_file).readline().rstrip() # If it is the first time the version is checked, the checked_version is '' - checked_version = Preferences.getInstance().getValue("info/latest_checked_firmware") + checked_version = Application.getInstance().getPreferences().getValue("info/latest_checked_firmware") # If the checked_version is '', it's because is the first time we check firmware and in this case # we will not show the notification, but we will store it for the next time - Preferences.getInstance().setValue("info/latest_checked_firmware", current_version) + Application.getInstance().getPreferences().setValue("info/latest_checked_firmware", current_version) Logger.log("i", "Reading firmware version of %s: checked = %s - latest = %s", machine_name, checked_version, current_version) # The first time we want to store the current version, the notification will not be shown, diff --git a/plugins/GCodeReader/FlavorParser.py b/plugins/GCodeReader/FlavorParser.py index 2679cc23a4..ceb5fbdb5d 100644 --- a/plugins/GCodeReader/FlavorParser.py +++ b/plugins/GCodeReader/FlavorParser.py @@ -10,7 +10,6 @@ from UM.Math.Vector import Vector from UM.Message import Message from cura.Scene.CuraSceneNode import CuraSceneNode from UM.i18n import i18nCatalog -from UM.Preferences import Preferences catalog = i18nCatalog("cura") @@ -43,7 +42,7 @@ class FlavorParser: self._current_layer_thickness = 0.2 # default self._filament_diameter = 2.85 # default - Preferences.getInstance().addPreference("gcodereader/show_caution", True) + Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True) def _clearValues(self): self._extruder_number = 0 @@ -462,7 +461,7 @@ class FlavorParser: Logger.log("d", "GCode loading finished") - if Preferences.getInstance().getValue("gcodereader/show_caution"): + if Application.getInstance().getPreferences().getValue("gcodereader/show_caution"): caution_message = Message(catalog.i18nc( "@info:generic", "Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."), diff --git a/plugins/GCodeReader/GCodeReader.py b/plugins/GCodeReader/GCodeReader.py index 6f872bc6f7..b821fa2cb3 100755 --- a/plugins/GCodeReader/GCodeReader.py +++ b/plugins/GCodeReader/GCodeReader.py @@ -4,7 +4,7 @@ from UM.FileHandler.FileReader import FileReader from UM.Mesh.MeshReader import MeshReader from UM.i18n import i18nCatalog -from UM.Preferences import Preferences +from UM.Application import Application catalog = i18nCatalog("cura") from . import MarlinFlavorParser, RepRapFlavorParser @@ -22,7 +22,7 @@ class GCodeReader(MeshReader): self._supported_extensions = [".gcode", ".g"] self._flavor_reader = None - Preferences.getInstance().addPreference("gcodereader/show_caution", True) + Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True) def preReadFromStream(self, stream, *args, **kwargs): for line in stream.split("\n"): diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index d2db5ff420..baa700165c 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -5,7 +5,6 @@ from UM.Tool import Tool from UM.Scene.Selection import Selection from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Application import Application -from UM.Preferences import Preferences from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.ExtruderManager import ExtruderManager from UM.Settings.SettingInstance import SettingInstance @@ -27,7 +26,7 @@ class PerObjectSettingsTool(Tool): Selection.selectionChanged.connect(self.propertyChanged) - Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged) + Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferenceChanged) self._onPreferenceChanged("cura/active_mode") Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) @@ -106,7 +105,7 @@ class PerObjectSettingsTool(Tool): def _onPreferenceChanged(self, preference): if preference == "cura/active_mode": - self._advanced_mode = Preferences.getInstance().getValue(preference) == 1 + self._advanced_mode = Application.getInstance().getPreferences().getValue(preference) == 1 self._updateEnabled() def _onGlobalContainerChanged(self): diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 85849efb2f..44643dbf1c 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -16,7 +16,6 @@ from UM.Mesh.MeshBuilder import MeshBuilder from UM.Message import Message from UM.Platform import Platform from UM.PluginRegistry import PluginRegistry -from UM.Preferences import Preferences from UM.Resources import Resources from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection @@ -81,30 +80,30 @@ class SimulationView(View): self._show_travel_moves = False self._nozzle_node = None - Preferences.getInstance().addPreference("view/top_layer_count", 5) - Preferences.getInstance().addPreference("view/only_show_top_layers", False) - Preferences.getInstance().addPreference("view/force_layer_view_compatibility_mode", False) + Application.getInstance().getPreferences().addPreference("view/top_layer_count", 5) + Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False) + Application.getInstance().getPreferences().addPreference("view/force_layer_view_compatibility_mode", False) - Preferences.getInstance().addPreference("layerview/layer_view_type", 0) - Preferences.getInstance().addPreference("layerview/extruder_opacities", "") + Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 0) + Application.getInstance().getPreferences().addPreference("layerview/extruder_opacities", "") - Preferences.getInstance().addPreference("layerview/show_travel_moves", False) - Preferences.getInstance().addPreference("layerview/show_helpers", True) - Preferences.getInstance().addPreference("layerview/show_skin", True) - Preferences.getInstance().addPreference("layerview/show_infill", True) + Application.getInstance().getPreferences().addPreference("layerview/show_travel_moves", False) + Application.getInstance().getPreferences().addPreference("layerview/show_helpers", True) + Application.getInstance().getPreferences().addPreference("layerview/show_skin", True) + Application.getInstance().getPreferences().addPreference("layerview/show_infill", True) - Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged) self._updateWithPreferences() - self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) - self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) + self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count")) + self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers")) self._compatibility_mode = self._evaluateCompatibilityMode() self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), title = catalog.i18nc("@info:title", "Simulation View")) def _evaluateCompatibilityMode(self): - return OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")) + return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")) def _resetSettings(self): self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness @@ -543,23 +542,23 @@ class SimulationView(View): self._top_layers_job = None def _updateWithPreferences(self): - self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) - self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) + self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count")) + self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers")) self._compatibility_mode = self._evaluateCompatibilityMode() - self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type")))); + self.setSimulationViewType(int(float(Application.getInstance().getPreferences().getValue("layerview/layer_view_type")))); - for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")): + for extruder_nr, extruder_opacity in enumerate(Application.getInstance().getPreferences().getValue("layerview/extruder_opacities").split("|")): try: opacity = float(extruder_opacity) except ValueError: opacity = 1.0 self.setExtruderOpacity(extruder_nr, opacity) - self.setShowTravelMoves(bool(Preferences.getInstance().getValue("layerview/show_travel_moves"))) - self.setShowHelpers(bool(Preferences.getInstance().getValue("layerview/show_helpers"))) - self.setShowSkin(bool(Preferences.getInstance().getValue("layerview/show_skin"))) - self.setShowInfill(bool(Preferences.getInstance().getValue("layerview/show_infill"))) + self.setShowTravelMoves(bool(Application.getInstance().getPreferences().getValue("layerview/show_travel_moves"))) + self.setShowHelpers(bool(Application.getInstance().getPreferences().getValue("layerview/show_helpers"))) + self.setShowSkin(bool(Application.getInstance().getPreferences().getValue("layerview/show_skin"))) + self.setShowInfill(bool(Application.getInstance().getPreferences().getValue("layerview/show_infill"))) self._startUpdateTopLayers() self.preferencesChanged.emit() diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 82e07da464..d32e512e4f 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -10,7 +10,6 @@ from PyQt5.QtCore import pyqtSlot, QObject from UM.Extension import Extension from UM.Application import Application -from UM.Preferences import Preferences from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Message import Message from UM.i18n import i18nCatalog @@ -34,13 +33,13 @@ class SliceInfo(QObject, Extension): QObject.__init__(self, parent) Extension.__init__(self) Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._onWriteStarted) - Preferences.getInstance().addPreference("info/send_slice_info", True) - Preferences.getInstance().addPreference("info/asked_send_slice_info", False) + Application.getInstance().getPreferences().addPreference("info/send_slice_info", True) + Application.getInstance().getPreferences().addPreference("info/asked_send_slice_info", False) self._more_info_dialog = None self._example_data_content = None - if not Preferences.getInstance().getValue("info/asked_send_slice_info"): + if not Application.getInstance().getPreferences().getValue("info/asked_send_slice_info"): self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymized usage statistics."), lifetime = 0, dismissable = False, @@ -62,7 +61,7 @@ class SliceInfo(QObject, Extension): ## Perform action based on user input. # Note that clicking "Disable" won't actually disable the data sending, but rather take the user to preferences where they can disable it. def messageActionTriggered(self, message_id, action_id): - Preferences.getInstance().setValue("info/asked_send_slice_info", True) + Application.getInstance().getPreferences().setValue("info/asked_send_slice_info", True) if action_id == "MoreInfo": self.showMoreInfoDialog() self.send_slice_info_message.hide() @@ -88,11 +87,11 @@ class SliceInfo(QObject, Extension): @pyqtSlot(bool) def setSendSliceInfo(self, enabled: bool): - Preferences.getInstance().setValue("info/send_slice_info", enabled) + Application.getInstance().getPreferences().setValue("info/send_slice_info", enabled) def _onWriteStarted(self, output_device): try: - if not Preferences.getInstance().getValue("info/send_slice_info"): + if not Application.getInstance().getPreferences().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data @@ -107,7 +106,7 @@ class SliceInfo(QObject, Extension): data["schema_version"] = 0 data["cura_version"] = application.getVersion() - active_mode = Preferences.getInstance().getValue("cura/active_mode") + active_mode = Application.getInstance().getPreferences().getValue("cura/active_mode") if active_mode == 0: data["active_mode"] = "recommended" else: @@ -122,7 +121,7 @@ class SliceInfo(QObject, Extension): machine_settings_changed_by_user = True data["machine_settings_changed_by_user"] = machine_settings_changed_by_user - data["language"] = Preferences.getInstance().getValue("general/language") + data["language"] = Application.getInstance().getPreferences().getValue("general/language") data["os"] = {"type": platform.system(), "version": platform.version()} data["active_machine"] = {"definition_id": global_stack.definition.getId(), diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 8892ddb138..b9ad5c8829 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -6,7 +6,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection from UM.Resources import Resources from UM.Application import Application -from UM.Preferences import Preferences from UM.View.RenderBatch import RenderBatch from UM.Settings.Validator import ValidatorState from UM.Math.Color import Color @@ -23,7 +22,7 @@ class SolidView(View): def __init__(self): super().__init__() - Preferences.getInstance().addPreference("view/show_overhang", True) + Application.getInstance().getPreferences().addPreference("view/show_overhang", True) self._enabled_shader = None self._disabled_shader = None @@ -65,7 +64,7 @@ class SolidView(View): support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr") support_angle_stack = Application.getInstance().getExtruderManager().getExtruderStack(support_extruder_nr) - if support_angle_stack is not None and Preferences.getInstance().getValue("view/show_overhang"): + if support_angle_stack is not None and Application.getInstance().getPreferences().getValue("view/show_overhang"): angle = support_angle_stack.getProperty("support_angle", "value") # Make sure the overhang angle is valid before passing it to the shader # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None) diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py index 2add388db6..ac011a5557 100644 --- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py @@ -5,7 +5,6 @@ from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Logger import Logger from UM.Application import Application from UM.Signal import Signal, signalemitter -from UM.Preferences import Preferences from UM.Version import Version from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice @@ -54,7 +53,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences - self._preferences = Preferences.getInstance() + self._preferences = Application.getInstance().getPreferences() self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames diff --git a/plugins/UserAgreement/UserAgreement.py b/plugins/UserAgreement/UserAgreement.py index 915c1761fa..30b2c00f08 100644 --- a/plugins/UserAgreement/UserAgreement.py +++ b/plugins/UserAgreement/UserAgreement.py @@ -2,7 +2,6 @@ # Cura is released under the terms of the LGPLv3 or higher. from UM.Extension import Extension -from UM.Preferences import Preferences from UM.Application import Application from UM.PluginRegistry import PluginRegistry from UM.Logger import Logger @@ -19,10 +18,10 @@ class UserAgreement(QObject, Extension): self._user_agreement_window = None self._user_agreement_context = None Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - Preferences.getInstance().addPreference("general/accepted_user_agreement", False) + Application.getInstance().getPreferences().addPreference("general/accepted_user_agreement", False) def _onEngineCreated(self): - if not Preferences.getInstance().getValue("general/accepted_user_agreement"): + if not Application.getInstance().getPreferences().getValue("general/accepted_user_agreement"): self.showUserAgreement() def showUserAgreement(self): @@ -35,11 +34,11 @@ class UserAgreement(QObject, Extension): def didAgree(self, user_choice): if user_choice: Logger.log("i", "User agreed to the user agreement") - Preferences.getInstance().setValue("general/accepted_user_agreement", True) + Application.getInstance().getPreferences().setValue("general/accepted_user_agreement", True) self._user_agreement_window.hide() else: Logger.log("i", "User did NOT agree to the user agreement") - Preferences.getInstance().setValue("general/accepted_user_agreement", False) + Application.getInstance().getPreferences().setValue("general/accepted_user_agreement", False) CuraApplication.getInstance().quit() CuraApplication.getInstance().setNeedToShowUserAgreement(False) From 67ce55049bc3f0cf6b06eb9a2162b787a38cb7fb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 16 May 2018 10:26:13 +0200 Subject: [PATCH 006/109] CURA-5164 Fix a bug when open the discard or keep dialog. It was using a non-existing variable. --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b629332f47..667390b946 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -555,7 +555,7 @@ class CuraApplication(QtApplication): elif choice == "always_keep": # don't show dialog and KEEP the profile self.discardOrKeepProfileChangesClosed("keep") - elif self._use_gui: + elif not self._is_headless: # ALWAYS ask whether to keep or discard the profile self.showDiscardOrKeepProfileChanges.emit() has_user_interaction = True From 1bec6d7aa57ae91611d2387dc43e33a9659cb179 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 16 May 2018 17:26:56 +0200 Subject: [PATCH 007/109] CURA-5164 Fix types --- cura/CuraPackageManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 0c2c438fcc..6fe3840679 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -26,8 +26,8 @@ class CuraPackageManager(QObject): self._plugin_registry = self._application.getPluginRegistry() #JSON files that keep track of all installed packages. - self._user_package_management_file_path = None - self._bundled_package_management_file_path = None + self._user_package_management_file_path = None #type: str + self._bundled_package_management_file_path = None #type: str for search_path in Resources.getSearchPaths(): candidate_bundled_path = os.path.join(search_path, "bundled_packages.json") if os.path.exists(candidate_bundled_path): From 39768e29cc10e09c1eb3fbb304739b1d4ef162ca Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 23 May 2018 12:12:58 +0200 Subject: [PATCH 008/109] CURA-5164 First add the resources and then initialize the package manager. --- cura/CuraApplication.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e40efceeb3..80cf7a3d74 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -262,11 +262,6 @@ class CuraApplication(QtApplication): def initialize(self) -> None: super().initialize() - # Initialize the package manager to remove and install scheduled packages. - from cura.CuraPackageManager import CuraPackageManager - self._cura_package_manager = CuraPackageManager(self) - self._cura_package_manager.initialize() - self.__sendCommandToSingleInstance() self.__addExpectedResourceDirsAndSearchPaths() self.__initializeSettingDefinitionsAndFunctions() @@ -274,6 +269,11 @@ class CuraApplication(QtApplication): self.__addAllEmptyContainers() self.__setLatestResouceVersionsForVersionUpgrade() + # Initialize the package manager to remove and install scheduled packages. + from cura.CuraPackageManager import CuraPackageManager + self._cura_package_manager = CuraPackageManager(self) + self._cura_package_manager.initialize() + self._machine_action_manager = MachineActionManager.MachineActionManager(self) self._machine_action_manager.initialize() From ddd7b056f387c9bb1a68e4952deac5b783ef723e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 23 May 2018 17:17:41 +0200 Subject: [PATCH 009/109] CURA-5164 Make the mesh readers compliant with the new signature of the mesh reader. Also adapt the tests. --- plugins/3MFReader/ThreeMFReader.py | 6 +++--- plugins/3MFReader/__init__.py | 2 +- plugins/GCodeGzReader/GCodeGzReader.py | 6 +++--- plugins/GCodeGzReader/__init__.py | 2 +- plugins/GCodeReader/GCodeReader.py | 6 +++--- plugins/GCodeReader/__init__.py | 2 +- plugins/ImageReader/ImageReader.py | 6 +++--- plugins/ImageReader/__init__.py | 2 +- plugins/X3DReader/X3DReader.py | 6 +++--- plugins/X3DReader/__init__.py | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 9eec09b202..2ecea82691 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -44,8 +44,8 @@ except ImportError: ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): - def __init__(self): - super().__init__() + def __init__(self, application): + super().__init__(application) self._supported_extensions = [".3mf"] self._root = None self._base_name = "" @@ -158,7 +158,7 @@ class ThreeMFReader(MeshReader): um_node.addDecorator(sliceable_decorator) return um_node - def read(self, file_name): + def _read(self, file_name): result = [] self._object_count = 0 # Used to name objects as there is no node name yet. # The base object of 3mf is a zipped archive. diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 9da54586a3..ddaf76e01e 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -42,7 +42,7 @@ def getMetaData() -> Dict: def register(app): if "3MFReader.ThreeMFReader" in sys.modules: - return {"mesh_reader": ThreeMFReader.ThreeMFReader(), + return {"mesh_reader": ThreeMFReader.ThreeMFReader(app), "workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()} else: return {} diff --git a/plugins/GCodeGzReader/GCodeGzReader.py b/plugins/GCodeGzReader/GCodeGzReader.py index 86c0af89a2..7a6a76d4a5 100644 --- a/plugins/GCodeGzReader/GCodeGzReader.py +++ b/plugins/GCodeGzReader/GCodeGzReader.py @@ -12,11 +12,11 @@ from UM.PluginRegistry import PluginRegistry # If you're zipping g-code, you might as well use gzip! class GCodeGzReader(MeshReader): - def __init__(self): - super().__init__() + def __init__(self, application): + super().__init__(application) self._supported_extensions = [".gcode.gz"] - def read(self, file_name): + def _read(self, file_name): with open(file_name, "rb") as file: file_data = file.read() uncompressed_gcode = gzip.decompress(file_data).decode("utf-8") diff --git a/plugins/GCodeGzReader/__init__.py b/plugins/GCodeGzReader/__init__.py index 6e720b1ed1..e6bae6615e 100644 --- a/plugins/GCodeGzReader/__init__.py +++ b/plugins/GCodeGzReader/__init__.py @@ -21,4 +21,4 @@ def getMetaData(): def register(app): app.addNonSliceableExtension(".gz") - return { "mesh_reader": GCodeGzReader.GCodeGzReader() } + return { "mesh_reader": GCodeGzReader.GCodeGzReader(app) } diff --git a/plugins/GCodeReader/GCodeReader.py b/plugins/GCodeReader/GCodeReader.py index a576d6b49f..c51fc9c8a7 100755 --- a/plugins/GCodeReader/GCodeReader.py +++ b/plugins/GCodeReader/GCodeReader.py @@ -27,8 +27,8 @@ class GCodeReader(MeshReader): _flavor_readers_dict = {"RepRap" : RepRapFlavorParser.RepRapFlavorParser(), "Marlin" : MarlinFlavorParser.MarlinFlavorParser()} - def __init__(self): - super(GCodeReader, self).__init__() + def __init__(self, application): + super(GCodeReader, self).__init__(application) self._supported_extensions = [".gcode", ".g"] self._flavor_reader = None @@ -57,7 +57,7 @@ class GCodeReader(MeshReader): def readFromStream(self, stream): return self._flavor_reader.processGCodeStream(stream) - def read(self, file_name): + def _read(self, file_name): with open(file_name, "r", encoding = "utf-8") as file: file_data = file.read() return self.readFromStream(file_data) diff --git a/plugins/GCodeReader/__init__.py b/plugins/GCodeReader/__init__.py index 999dd37a37..399de8a90e 100644 --- a/plugins/GCodeReader/__init__.py +++ b/plugins/GCodeReader/__init__.py @@ -23,4 +23,4 @@ def getMetaData(): def register(app): app.addNonSliceableExtension(".gcode") app.addNonSliceableExtension(".g") - return { "mesh_reader": GCodeReader.GCodeReader() } + return { "mesh_reader": GCodeReader.GCodeReader(app) } diff --git a/plugins/ImageReader/ImageReader.py b/plugins/ImageReader/ImageReader.py index 3a98abccf5..89f163cfca 100644 --- a/plugins/ImageReader/ImageReader.py +++ b/plugins/ImageReader/ImageReader.py @@ -17,8 +17,8 @@ from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode class ImageReader(MeshReader): - def __init__(self): - super(ImageReader, self).__init__() + def __init__(self, application): + super(ImageReader, self).__init__(application) self._supported_extensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png"] self._ui = ImageReaderUI(self) @@ -44,7 +44,7 @@ class ImageReader(MeshReader): return MeshReader.PreReadResult.cancelled return MeshReader.PreReadResult.accepted - def read(self, file_name): + def _read(self, file_name): size = max(self._ui.getWidth(), self._ui.getDepth()) return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert) diff --git a/plugins/ImageReader/__init__.py b/plugins/ImageReader/__init__.py index ea6ff8cceb..f30fdb83b0 100644 --- a/plugins/ImageReader/__init__.py +++ b/plugins/ImageReader/__init__.py @@ -33,4 +33,4 @@ def getMetaData(): } def register(app): - return { "mesh_reader": ImageReader.ImageReader() } + return { "mesh_reader": ImageReader.ImageReader(app) } diff --git a/plugins/X3DReader/X3DReader.py b/plugins/X3DReader/X3DReader.py index b0b9e00a5b..e57ec524db 100644 --- a/plugins/X3DReader/X3DReader.py +++ b/plugins/X3DReader/X3DReader.py @@ -38,14 +38,14 @@ class Shape: self.name = name class X3DReader(MeshReader): - def __init__(self): - super().__init__() + def __init__(self, application): + super().__init__(application) self._supported_extensions = [".x3d"] self._namespaces = {} # Main entry point # Reads the file, returns a SceneNode (possibly with nested ones), or None - def read(self, file_name): + def _read(self, file_name): try: self.defs = {} self.shapes = [] diff --git a/plugins/X3DReader/__init__.py b/plugins/X3DReader/__init__.py index 9e0e2df91c..03ed4ae301 100644 --- a/plugins/X3DReader/__init__.py +++ b/plugins/X3DReader/__init__.py @@ -16,4 +16,4 @@ def getMetaData(): } def register(app): - return { "mesh_reader": X3DReader.X3DReader() } + return { "mesh_reader": X3DReader.X3DReader(app) } From 74ef90694957b4cb68de722342b98dd2b9ee878c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 25 May 2018 10:15:04 +0200 Subject: [PATCH 010/109] CURA-5164 Reuse method savePreferences --- cura/CuraApplication.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 70c4d7727a..5b1d557f49 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -10,7 +10,6 @@ import time import numpy from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS -from PyQt5.QtNetwork import QLocalServer from PyQt5.QtGui import QColor, QIcon from PyQt5.QtWidgets import QMessageBox from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType @@ -597,8 +596,7 @@ class CuraApplication(QtApplication): # Do not do saving during application start or when data should not be safed on quit. return ContainerRegistry.getInstance().saveDirtyContainers() - Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences, - self._application_name + ".cfg")) + self.savePreferences() def saveStack(self, stack): ContainerRegistry.getInstance().saveContainer(stack) From d6e7b96437c863352aa75291142d8f7e3b866276 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 25 May 2018 10:49:16 +0200 Subject: [PATCH 011/109] Set machine preset to be equal to Replicator 1 Dual This was in the original Cura 15.06 plug-in. --- resources/definitions/malyan_m180.def.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/definitions/malyan_m180.def.json b/resources/definitions/malyan_m180.def.json index 11b61328ed..c74317a633 100644 --- a/resources/definitions/malyan_m180.def.json +++ b/resources/definitions/malyan_m180.def.json @@ -7,6 +7,7 @@ "visible": true, "author": "Ruben Dulek", "manufacturer": "Malyan", + "machine_x3g_variant": "r1d", "file_formats": "application/x3g" }, From f5a77071391f37d65f07d22e7bbd967cb706094e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 25 May 2018 11:24:31 +0200 Subject: [PATCH 012/109] Show ConfigurationErrorMessage when loading faulty XML files This is consistent with the rest of Cura's behaviour. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 70d07c1fc5..f0d6915f04 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import copy @@ -12,10 +12,10 @@ import xml.etree.ElementTree as ET from UM.Resources import Resources from UM.Logger import Logger from cura.CuraApplication import CuraApplication - import UM.Dictionary from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from .XmlMaterialValidator import XmlMaterialValidator @@ -540,7 +540,9 @@ class XmlMaterialProfile(InstanceContainer): validation_message = XmlMaterialValidator.validateMaterialMetaData(meta_data) if validation_message is not None: - raise Exception("Not valid material profile: %s" % (validation_message)) + ConfigurationErrorMessage.getInstance().addFaultyContainers(self.getId()) + Logger.log("e", "Not a valid material profile: {message}".format(message = validation_message)) + return property_values = {} properties = data.iterfind("./um:properties/*", self.__namespaces) From 7c0eb43a238dbce8c5efb3194d5282060ac01988 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 25 May 2018 11:44:37 +0200 Subject: [PATCH 013/109] Fix unpausing after cancelling a print The variable name is self._paused, not self._is_paused. This was doing nothing at all. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index b2ca5562e3..dcc2387b07 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -385,7 +385,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._gcode.clear() self._printers[0].updateActivePrintJob(None) self._is_printing = False - self._is_paused = False + self._paused = False # Turn off temperatures, fan and steppers self._sendCommand("M140 S0") From 0d55b8b4406f3845ec32cc848827e1e193883b34 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 25 May 2018 12:04:00 +0200 Subject: [PATCH 014/109] Resume after pausing without waiting for next ok Previously the resume only worked because in the message receiving loop it would no longer ignore the next 'ok'. It doesn't actually send a message that would trigger an 'ok'. This only worked because we are polling every 2 seconds for the nozzle temperature which also happens to trigger an 'ok' response, which we interpret as a go-ahead for being able to send the next g-code line. Now it's no longer dependent on the fact that we happen to also poll the temperatures, so the code is more stable. And it also resumes immediately instead of waiting for up to 2 seconds. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index dcc2387b07..00eb2f0b25 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -379,6 +379,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def resumePrint(self): self._paused = False + self._sendNextGcodeLine() #Send one line of g-code next so that we'll trigger an "ok" response loop even if we're not polling temperatures. def cancelPrint(self): self._gcode_position = 0 From 87555a38c42ca343afccf8c09ff06cd1407ddb54 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 25 May 2018 21:40:30 +0200 Subject: [PATCH 015/109] Fix text rendering in the ConfigurationMenu --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 1 + .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index be8c8bcb45..6f0130d5ca 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -92,6 +92,7 @@ Rectangle anchors.verticalCenter: buildplateIcon.verticalCenter anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) text: configuration.buildplateConfiguration + renderType: Text.NativeRendering color: textColor } } diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index ca1b666e69..97b5bee745 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -26,6 +26,7 @@ Column { id: extruderLabel text: catalog.i18nc("@label:extruder label", "Extruder") + renderType: Text.NativeRendering elide: Text.ElideRight anchors.left: parent.left font: UM.Theme.getFont("default") @@ -59,6 +60,7 @@ Column id: extruderNumberText anchors.centerIn: parent text: printCoreConfiguration.position + 1 + renderType: Text.NativeRendering font: UM.Theme.getFont("default") color: mainColor } @@ -69,6 +71,7 @@ Column { id: materialLabel text: printCoreConfiguration.material.name + renderType: Text.NativeRendering elide: Text.ElideRight width: parent.width font: UM.Theme.getFont("default_bold") @@ -79,6 +82,7 @@ Column { id: printCoreTypeLabel text: printCoreConfiguration.hotendID + renderType: Text.NativeRendering elide: Text.ElideRight width: parent.width font: UM.Theme.getFont("default") From 94813d9e0d10076f6b13a779cb48e1e570670ebb Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 25 May 2018 21:40:57 +0200 Subject: [PATCH 016/109] Fix untranslatable text --- resources/qml/Menus/ConfigurationMenu/SyncButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index 078acb65b2..3099d684c1 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -13,7 +13,7 @@ Button id: base property var outputDevice: null property var matched: updateOnSync() - text: matched == true ? "Yes" : "No" + text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No") width: parent.width height: parent.height From 689b88a02466e9dfce2e1506bd0377467f30f624 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 09:53:47 +0200 Subject: [PATCH 017/109] Do not change prefix for user-specified job names CURA-5280 --- cura/PrintInformation.py | 30 +++++++++++++++++++----------- resources/qml/JobSpecs.qml | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 3260d55c74..2c9f6511fa 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -65,6 +65,7 @@ class PrintInformation(QObject): self._backend.printDurationMessage.connect(self._onPrintDurationMessage) Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) + self._is_user_specified_job_name = False self._base_name = "" self._abbr_machine = "" self._job_name = "" @@ -281,10 +282,13 @@ class PrintInformation(QObject): # Manual override of job name should also set the base name so that when the printer prefix is updated, it the # prefix can be added to the manually added name, not the old base name - @pyqtSlot(str) - def setJobName(self, name): + @pyqtSlot(str, bool) + def setJobName(self, name, is_user_specified_job_name = False): + self._is_user_specified_job_name = is_user_specified_job_name self._job_name = name self._base_name = name.replace(self._abbr_machine + "_", "") + if name == "": + self._is_user_specified_job_name = False self.jobNameChanged.emit() jobNameChanged = pyqtSignal() @@ -296,21 +300,25 @@ class PrintInformation(QObject): def _updateJobName(self): if self._base_name == "": self._job_name = "" + self._is_user_specified_job_name = False self.jobNameChanged.emit() return base_name = self._stripAccents(self._base_name) self._setAbbreviatedMachineName() - if self._pre_sliced: - self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}", base_name) - elif Preferences.getInstance().getValue("cura/jobname_prefix"): - # Don't add abbreviation if it already has the exact same abbreviation. - if base_name.startswith(self._abbr_machine + "_"): - self._job_name = base_name + + # Only update the job name when it's not user-specified. + if not self._is_user_specified_job_name: + if self._pre_sliced: + self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}", base_name) + elif Preferences.getInstance().getValue("cura/jobname_prefix"): + # Don't add abbreviation if it already has the exact same abbreviation. + if base_name.startswith(self._abbr_machine + "_"): + self._job_name = base_name + else: + self._job_name = self._abbr_machine + "_" + base_name else: - self._job_name = self._abbr_machine + "_" + base_name - else: - self._job_name = base_name + self._job_name = base_name self.jobNameChanged.emit() diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index e7f2d304b5..579f9bcc05 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -81,7 +81,7 @@ Item { text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight onTextChanged: { - PrintInformation.setJobName(text); + PrintInformation.setJobName(text, true); } onEditingFinished: { if (printJobTextfield.text != ''){ From ad131ab30c0312e40a871df4faac9351bc298004 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 11:18:22 +0200 Subject: [PATCH 018/109] Fix downgrade packages to bundled version CURA-5296 --- cura/CuraPackageManager.py | 6 ++++++ plugins/Toolbox/src/Toolbox.py | 12 +++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index a4b60b3251..7f0d49cf12 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -111,6 +111,12 @@ class CuraPackageManager(QObject): del self._to_install_package_dict[package_id] self._saveManagementData() + def getBundledPackageInfo(self, package_id: str) -> Optional[dict]: + package_info = None + if package_id in self._bundled_package_dict: + package_info = self._bundled_package_dict[package_id]["package_info"] + return package_info + # Checks the given package is installed. If so, return a dictionary that contains the package's information. def getInstalledPackageInfo(self, package_id: str) -> Optional[dict]: if package_id in self._to_remove_package_set: diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 6dac823f06..824a7e30f3 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -318,19 +318,21 @@ class Toolbox(QObject, Extension): remote_version = Version(remote_package["package_version"]) return remote_version > local_version - @pyqtSlot(str, result=bool) + @pyqtSlot(str, result = bool) def canDowngrade(self, package_id: str) -> bool: + # If the currently installed version is higher than the bundled version (if present), the we can downgrade + # this package. local_package = self._package_manager.getInstalledPackageInfo(package_id) if local_package is None: return False - remote_package = self.getRemotePackage(package_id) - if remote_package is None: + bundled_package = self._package_manager.getBundledPackageInfo(package_id) + if bundled_package is None: return False local_version = Version(local_package["package_version"]) - remote_version = Version(remote_package["package_version"]) - return remote_version < local_version + bundled_version = Version(bundled_package["package_version"]) + return bundled_version < local_version @pyqtSlot(str, result = bool) def isInstalled(self, package_id: str) -> bool: From 43fcb183aee8f2bee2b01abd53564a7f749c0b35 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 13:09:55 +0200 Subject: [PATCH 019/109] Remove unnecessary code CURA-5296 --- cura/CuraPackageManager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 7f0d49cf12..bc2e020037 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -89,7 +89,6 @@ class CuraPackageManager(QObject): "installed": self._installed_package_dict, "to_remove": list(self._to_remove_package_set), "to_install": self._to_install_package_dict} - data_dict["to_remove"] = list(data_dict["to_remove"]) json.dump(data_dict, f, sort_keys = True, indent = 4) Logger.log("i", "Package management file %s was saved", self._user_package_management_file_path) @@ -103,7 +102,6 @@ class CuraPackageManager(QObject): # (for initialize) Installs all packages that have been scheduled to be installed. def _installAllScheduledPackages(self) -> None: - while self._to_install_package_dict: package_id, package_info = list(self._to_install_package_dict.items())[0] self._installPackage(package_info) From 57ca41291a8859c4f0d0156c25d609e759e62e1e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 13:10:18 +0200 Subject: [PATCH 020/109] Fix package upgrade CURA-5296 The removal of already installed package is already handled by the package manager in the package upgrade. Removing the package before the download starts will make the package to be removed before the new content is available and its license gets approved by the user. --- plugins/Toolbox/src/Toolbox.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 824a7e30f3..ea6bdf791c 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -250,8 +250,6 @@ class Toolbox(QObject, Extension): if remote_package: download_url = remote_package["download_url"] Logger.log("d", "Updating package [%s]..." % plugin_id) - if self._package_manager.isUserInstalledPackage(plugin_id): - self.uninstall(plugin_id) self.startDownload(download_url) else: Logger.log("e", "Could not update package [%s] because there is no remote package info available.", plugin_id) From 24b8ea4a4b1a738463c78475d83b366725de8ed4 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 13:18:52 +0200 Subject: [PATCH 021/109] Quality changes are now in the quality_changes folder CURA-5296 --- plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py b/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py index c36247353f..4faa1290b5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py @@ -21,7 +21,7 @@ def getMetaData(): }, "quality_changes": { "get_version": upgrade.getCfgVersion, - "location": {"./quality"} + "location": {"./quality_changes"} }, "user": { "get_version": upgrade.getCfgVersion, From 6a7812a18f2533f152e4effcaa291a33bd5b18f4 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 28 May 2018 15:27:22 +0200 Subject: [PATCH 022/109] Textual fix for Marketing --- plugins/ModelChecker/ModelChecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 297844a0a3..d2c2eefac2 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -27,7 +27,7 @@ class ModelChecker(QObject, Extension): self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question. lifetime = 0, - title = catalog.i18nc("@info:title", "Model Checker Warning")) + title = catalog.i18nc("@info:title", "3D Model Assistant")) Application.getInstance().initializationFinished.connect(self._pluginsInitialized) Application.getInstance().getController().getScene().sceneChanged.connect(self._onChanged) From 6a3eec07e2b3fe68e1b9265761383b07532184d5 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 28 May 2018 16:03:05 +0200 Subject: [PATCH 023/109] Message stack shows properly messages CURA-4952 --- .../FirmwareUpdateCheckerJob.py | 20 +++++++++++++------ resources/themes/cura-light/styles.qml | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 66ee43209f..19b77c1181 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -63,13 +63,21 @@ class FirmwareUpdateCheckerJob(Job): # notify the user when no new firmware version is available. if (checked_version != "") and (checked_version != current_version): Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE") - message = Message(i18n_catalog.i18nc("@info Don't translate {machine_name}, since it gets replaced by a printer name!", "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(machine_name = machine_name), - title = i18n_catalog.i18nc("@info:title The %s gets replaced with the printer name.", "New %s firmware available") % machine_name) - message.addAction("download", i18n_catalog.i18nc("@action:button", "How to update"), "[no_icon]", "[no_description]") - # If we do this in a cool way, the download url should be available in the JSON file - if self._set_download_url_callback: - self._set_download_url_callback("https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update") + footer_text = i18n_catalog.i18nc("@action:info", "Read more on how to update printer firmware") + footer_link = "?url=https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update" + + footer_message = footer_text + " " + footer_link + + message = Message(i18n_catalog.i18nc( + "@info Don't translate {machine_name}, since it gets replaced by a printer name!", + "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format( + machine_name=machine_name), + title=i18n_catalog.i18nc( + "@info:title The %s gets replaced with the printer name.", + "New %s firmware available") % machine_name, + footer = footer_message) + message.actionTriggered.connect(self._callback) message.show() diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 14e7d196a7..b71ddd2d86 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -532,7 +532,7 @@ QtObject { SequentialAnimation on x { id: xAnim - property int animEndPoint: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2) - Theme.getSize("progressbar_control").width + property int animEndPoint: Theme.getSize("message").width - Math.round((Theme.getSize("default_margin").width * 2.5)) - Theme.getSize("progressbar_control").width running: control.indeterminate && control.visible loops: Animation.Infinite NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;} From 8a35d52ddd6d0c71a92ac299de644d07799d8176 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 28 May 2018 17:21:08 +0200 Subject: [PATCH 024/109] Rename "cura_version" to "sdk_version" --- plugins/Toolbox/src/Toolbox.py | 17 ++-- resources/bundled_packages.json | 132 ++++++++++++++++---------------- 2 files changed, 76 insertions(+), 73 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ea6bdf791c..e540bce523 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -36,13 +36,16 @@ class Toolbox(QObject, Extension): self._application = Application.getInstance() self._package_manager = None self._plugin_registry = Application.getInstance().getPluginRegistry() - self._packages_api_root = self._getPackagesApiRoot() - self._packages_version = self._getPackagesVersion() - self._api_version = 1 - self._api_url = "{api_root}/cura-packages/v{api_version}/cura/v{package_version}".format( - api_root = self._packages_api_root, - api_version = self._api_version, - package_version = self._packages_version + + self._sdk_version = self._getPackagesVersion() + + self._cloud_api_version = 1 + self._cloud_api_root = self._getPackagesApiRoot() + + self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( + cloud_api_root = self._cloud_api_root, + cloud_api_version = self._cloud_api_version, + sdk_version = self._sdk_version ) # Network: diff --git a/resources/bundled_packages.json b/resources/bundled_packages.json index a63d08ddab..ab6bf7e925 100644 --- a/resources/bundled_packages.json +++ b/resources/bundled_packages.json @@ -6,7 +6,7 @@ "display_name": "3MF Reader", "description": "Provides support for reading 3MF files.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -23,7 +23,7 @@ "display_name": "3MF Writer", "description": "Provides support for writing 3MF files.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -40,7 +40,7 @@ "display_name": "Change Log", "description": "Shows changes since latest checked version.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -57,7 +57,7 @@ "display_name": "CuraEngine Backend", "description": "Provides the link to the CuraEngine slicing backend.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -74,7 +74,7 @@ "display_name": "Cura Profile Reader", "description": "Provides support for importing Cura profiles.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -91,7 +91,7 @@ "display_name": "Cura Profile Writer", "description": "Provides support for exporting Cura profiles.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -108,7 +108,7 @@ "display_name": "Firmware Update Checker", "description": "Checks for firmware updates.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -125,7 +125,7 @@ "display_name": "Compressed G-code Reader", "description": "Reads g-code from a compressed archive.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -142,7 +142,7 @@ "display_name": "Compressed G-code Writer", "description": "Writes g-code to a compressed archive.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -159,7 +159,7 @@ "display_name": "G-Code Profile Reader", "description": "Provides support for importing profiles from g-code files.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -176,7 +176,7 @@ "display_name": "G-Code Reader", "description": "Allows loading and displaying G-code files.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "VictorLarchenko", @@ -193,7 +193,7 @@ "display_name": "G-Code Writer", "description": "Writes g-code to a file.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -210,7 +210,7 @@ "display_name": "Image Reader", "description": "Enables ability to generate printable geometry from 2D image files.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -227,7 +227,7 @@ "display_name": "Legacy Cura Profile Reader", "description": "Provides support for importing profiles from legacy Cura versions.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -244,7 +244,7 @@ "display_name": "Machine Settings Action", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -261,7 +261,7 @@ "display_name": "Model Checker", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "package_version": "0.1.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -278,7 +278,7 @@ "display_name": "Monitor Stage", "description": "Provides a monitor stage in Cura.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -295,7 +295,7 @@ "display_name": "Per-Object Settings Tool", "description": "Provides the per-model settings.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -312,7 +312,7 @@ "display_name": "Post Processing", "description": "Extension that allows for user created scripts for post processing.", "package_version": "2.2.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -329,7 +329,7 @@ "display_name": "Prepare Stage", "description": "Provides a prepare stage in Cura.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -346,7 +346,7 @@ "display_name": "Removable Drive Output Device", "description": "Provides removable drive hotplugging and writing support.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -363,7 +363,7 @@ "display_name": "Simulation View", "description": "Provides the Simulation view.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -380,7 +380,7 @@ "display_name": "Slice Info", "description": "Submits anonymous slice info. Can be disabled through preferences.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -397,7 +397,7 @@ "display_name": "Solid View", "description": "Provides a normal solid mesh view.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -414,7 +414,7 @@ "display_name": "Support Eraser Tool", "description": "Creates an eraser mesh to block the printing of support in certain places.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -431,7 +431,7 @@ "display_name": "Toolbox", "description": "Find, manage and install new Cura packages.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -448,7 +448,7 @@ "display_name": "UFP Writer", "description": "Provides support for writing Ultimaker Format Packages.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -465,7 +465,7 @@ "display_name": "Ultimaker Machine Actions", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -482,7 +482,7 @@ "display_name": "UM3 Network Printing", "description": "Manages network connections to Ultimaker 3 printers.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -499,7 +499,7 @@ "display_name": "USB Printing", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -516,7 +516,7 @@ "display_name": "User Agreement", "description": "Ask the user once if he/she agrees with our license.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -533,7 +533,7 @@ "display_name": "Version Upgrade 2.1 to 2.2", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -550,7 +550,7 @@ "display_name": "Version Upgrade 2.2 to 2.4", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -567,7 +567,7 @@ "display_name": "Version Upgrade 2.5 to 2.6", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -584,7 +584,7 @@ "display_name": "Version Upgrade 2.6 to 2.7", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -601,7 +601,7 @@ "display_name": "Version Upgrade 2.7 to 3.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -618,7 +618,7 @@ "display_name": "Version Upgrade 3.0 to 3.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -635,7 +635,7 @@ "display_name": "Version Upgrade 3.2 to 3.3", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -652,7 +652,7 @@ "display_name": "Version Upgrade 3.3 to 3.4", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -669,7 +669,7 @@ "display_name": "X3D Reader", "description": "Provides support for reading X3D files.", "package_version": "0.5.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "SevaAlekseyev", @@ -686,7 +686,7 @@ "display_name": "XML Material Profiles", "description": "Provides capabilities to read and write XML-based material profiles.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -703,7 +703,7 @@ "display_name": "X-Ray View", "description": "Provides the X-Ray view.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com", "author": { "author_id": "Ultimaker", @@ -720,7 +720,7 @@ "display_name": "Dagoma Chromatik PLA", "description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://dagoma.fr/boutique/filaments.html", "author": { "author_id": "Dagoma", @@ -737,7 +737,7 @@ "display_name": "FABtotum ABS", "description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so it’s compatible with all versions of the FABtotum Personal fabricator.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40", "author": { "author_id": "FABtotum", @@ -754,7 +754,7 @@ "display_name": "FABtotum Nylon", "description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53", "author": { "author_id": "FABtotum", @@ -771,7 +771,7 @@ "display_name": "FABtotum PLA", "description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starch’s sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotum’s one is between 185° and 195°.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39", "author": { "author_id": "FABtotum", @@ -788,7 +788,7 @@ "display_name": "FABtotum TPU Shore 98A", "description": "", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66", "author": { "author_id": "FABtotum", @@ -805,7 +805,7 @@ "display_name": "Fiberlogy HD PLA", "description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS – better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/", "author": { "author_id": "Fiberlogy", @@ -822,7 +822,7 @@ "display_name": "Filo3D PLA", "description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://dagoma.fr", "author": { "author_id": "Dagoma", @@ -839,7 +839,7 @@ "display_name": "IMADE3D JellyBOX PETG", "description": "", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -856,7 +856,7 @@ "display_name": "IMADE3D JellyBOX PLA", "description": "", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -873,7 +873,7 @@ "display_name": "Octofiber PLA", "description": "PLA material from Octofiber.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://nl.octofiber.com/3d-printing-filament/pla.html", "author": { "author_id": "Octofiber", @@ -890,7 +890,7 @@ "display_name": "PolyFlex™ PLA", "description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://www.polymaker.com/shop/polyflex/", "author": { "author_id": "Polymaker", @@ -907,7 +907,7 @@ "display_name": "PolyMax™ PLA", "description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://www.polymaker.com/shop/polymax/", "author": { "author_id": "Polymaker", @@ -924,7 +924,7 @@ "display_name": "PolyPlus™ PLA True Colour", "description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://www.polymaker.com/shop/polyplus-true-colour/", "author": { "author_id": "Polymaker", @@ -941,7 +941,7 @@ "display_name": "PolyWood™ PLA", "description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "http://www.polymaker.com/shop/polywood/", "author": { "author_id": "Polymaker", @@ -958,7 +958,7 @@ "display_name": "Ultimaker ABS", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "Ultimaker", @@ -977,7 +977,7 @@ "display_name": "Ultimaker CPE", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "Ultimaker", @@ -996,7 +996,7 @@ "display_name": "Ultimaker Nylon", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "Ultimaker", @@ -1015,7 +1015,7 @@ "display_name": "Ultimaker PC", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/pc", "author": { "author_id": "Ultimaker", @@ -1034,7 +1034,7 @@ "display_name": "Ultimaker PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "Ultimaker", @@ -1053,7 +1053,7 @@ "display_name": "Ultimaker PVA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "Ultimaker", @@ -1072,7 +1072,7 @@ "display_name": "Vertex Delta ABS", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1089,7 +1089,7 @@ "display_name": "Vertex Delta PET", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1106,7 +1106,7 @@ "display_name": "Vertex Delta PLA", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1123,7 +1123,7 @@ "display_name": "Vertex Delta TPU", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.0", - "cura_version": 4, + "sdk_version": 4, "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", From 3b0b6d300b0abe6571c35955040d0a3192a61019 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 May 2018 17:34:12 +0200 Subject: [PATCH 025/109] Only set user-specified job name when the editting is finished CURA-5280 --- resources/qml/JobSpecs.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 579f9bcc05..f9ce286706 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -80,10 +80,8 @@ Item { property int unremovableSpacing: 5 text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight - onTextChanged: { - PrintInformation.setJobName(text, true); - } onEditingFinished: { + PrintInformation.setJobName(text, true); if (printJobTextfield.text != ''){ printJobTextfield.focus = false; } From ac1dfcf15d913ed25a582d82e34921d4322145da Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 28 May 2018 22:52:49 +0200 Subject: [PATCH 026/109] Make headers non-transparent --- plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 229ab5afb3..b4219d53bf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -31,8 +31,9 @@ Item frameVisible: false selectionMode: 0 model: packageData.supported_configs - headerDelegate: Item + headerDelegate: Rectangle { + color: UM.Theme.getColor("sidebar") height: UM.Theme.getSize("toolbox_chart_row").height Label { From 504889b472418f8565fda946ec5413043a7dcace Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 07:31:39 +0200 Subject: [PATCH 027/109] Remove Alternate Cross 3D Pockets setting It is no longer used in the new implementation of cross infill. --- resources/definitions/fdmprinter.def.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 0d6c1eaa7b..570b6c8625 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5772,16 +5772,6 @@ "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, - "cross_infill_apply_pockets_alternatingly": - { - "label": "Alternate Cross 3D Pockets", - "description": "Only apply pockets at half of the four-way crossings in the cross 3D pattern and alternate the location of the pockets between heights where the pattern is touching itself.", - "type": "bool", - "default_value": true, - "enabled": "infill_pattern == 'cross_3d'", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, "cross_infill_density_image": { "label": "Cross Infill Density Image", From 8a6996e920d6dde3a98fddf4a80ee078a24db385 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 29 May 2018 09:32:06 +0200 Subject: [PATCH 028/109] If an object does not have a convex hull, it does not crash the ArrangeObjectsJob --- cura/Arranging/ArrangeObjectsJob.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 01a91a3c22..08fd1985a9 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -43,6 +43,9 @@ class ArrangeObjectsJob(Job): nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset) + if offset_shape_arr is None: + Logger.log("w", "Node [%s] could not be converted to an array for arranging...", str(node)) + continue nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. From 9627eb0fc101616d94073450a0e31b3c309af021 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 29 May 2018 10:45:44 +0200 Subject: [PATCH 029/109] Only exclude explicitly specified materials in the "exclude_materials" field CURA-5344 --- cura/Machines/MaterialManager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 8b74596667..ff666f392d 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -291,9 +291,10 @@ class MaterialManager(QObject): material_id_metadata_dict = dict() for node in nodes_to_check: if node is not None: + # Only exclude the materials that are explicitly specified in the "exclude_materials" field. + # Do not exclude other materials that are of the same type. for material_id, node in node.material_map.items(): - fallback_id = self.getFallbackMaterialIdByMaterialType(node.metadata["material"]) - if fallback_id in machine_exclude_materials: + if material_id in machine_exclude_materials: Logger.log("d", "Exclude material [%s] for machine [%s]", material_id, machine_definition.getId()) continue From 1c3bae3fe478e6e30df7be4bdce76729224c66bd Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 29 May 2018 10:52:04 +0200 Subject: [PATCH 030/109] Show keep/discard dialog upon material change if there are user changes CURA-5417 --- cura/Settings/MachineManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 78f462d8e9..44f9f04740 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1331,6 +1331,10 @@ class MachineManager(QObject): self._setMaterial(position, container_node) self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + @pyqtSlot(str, str) def setVariantByName(self, position: str, variant_name: str) -> None: machine_definition_id = self._global_container_stack.definition.id From 77feb95191317a6e7bf077127a6a4d3293f67955 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 11:05:55 +0200 Subject: [PATCH 031/109] Don't crash when failing to reload file Now it shows a message that it failed to load the file. --- cura/CuraApplication.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5db616ab3b..ed4336a9a0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1500,11 +1500,15 @@ class CuraApplication(QtApplication): def _reloadMeshFinished(self, job): # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh! - mesh_data = job.getResult()[0].getMeshData() - if mesh_data: - job._node.setMeshData(mesh_data) - else: + job_result = job.getResult() + if len(job_result) == 0: + Logger.log("e", "Reloading the mesh failed.") + return + mesh_data = job_result[0].getMeshData() + if not mesh_data: Logger.log("w", "Could not find a mesh in reloaded node.") + return + job._node.setMeshData(mesh_data) def _openFile(self, filename): self.readLocalFile(QUrl.fromLocalFile(filename)) From 373555fa4a16eafbec5e9986d1f88d837af204d1 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 28 Feb 2018 16:26:11 +0100 Subject: [PATCH 032/109] settings for infill support --- resources/definitions/fdmprinter.def.json | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index fc8395a8dc..4ab08d2f24 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1807,6 +1807,30 @@ "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, + "infill_support_enabled": + { + "label": "Infill Support", + "description": "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength.", + "type": "bool", + "default_value": false, + "enabled": "infill_sparse_density > 0", + "limit_to_extruder": "infill_extruder_nr", + "settable_per_mesh": true + }, + "infill_support_angle": + { + "label": "Infill Overhang Angle", + "description": "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "minimum_value_warning": "2", + "maximum_value": "90", + "default_value": 40, + "enabled": "infill_sparse_density > 0 and infill_support_enabled", + "limit_to_extruder": "infill_extruder_nr", + "settable_per_mesh": true + }, "skin_preshrink": { "label": "Skin Removal Width", From ab2503834bda73e51ce4950d91f5739f5ae9bdd2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 11:31:13 +0200 Subject: [PATCH 033/109] Remove Hollow Out Objects setting It has been replaced by Infill Support. Contributes to issue CURA-5108. --- resources/definitions/fdmprinter.def.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 4ab08d2f24..292748433e 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5942,14 +5942,6 @@ "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": true }, - "infill_hollow": - { - "label": "Hollow Out Objects", - "description": "Remove all infill and make the inside of the object eligible for support.", - "type": "bool", - "default_value": false, - "settable_per_mesh": true - }, "magic_fuzzy_skin_enabled": { "label": "Fuzzy Skin", From db16a6275f43ce9a51326ea9ddcaa80f8ab3a4c9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 11:51:54 +0200 Subject: [PATCH 034/109] Add version upgrade for infill_hollow -> infill_support The functionality is more or less the same, so in an attempt to keep people's profiles as similar as possible we translate this setting to the newer implementation. --- .../VersionUpgrade33to34/VersionUpgrade33to34.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py index 17abace547..16f17c5e36 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py @@ -6,6 +6,9 @@ import io #To serialise the preference files afterwards. from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. +_renamed_settings = { + "infill_hollow": "infill_support_enabled" +} ## Upgrades configurations from the state they were in at version 3.3 to the # state they should be in at version 3.4. @@ -38,6 +41,13 @@ class VersionUpgrade33to34(VersionUpgrade): # Update version number. parser["general"]["version"] = "4" + #Renamed settings. + if "values" in parser: + for original, replacement in _renamed_settings.items(): + if original in parser["value"]: + parser["value"][replacement] = parser["value"][original] + del parser["value"][original] + result = io.StringIO() parser.write(result) - return [filename], [result.getvalue()] + return [filename], [result.getvalue()] \ No newline at end of file From 0d89240bca0c57ac7d6d6486fca9ecc35248fce1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 11:57:14 +0200 Subject: [PATCH 035/109] Copy support angle to infill support angle if used for hollow infill This keeps the new profile as close to the profile in the previous version as possible. Contributes to issue CURA-5108. --- .../VersionUpgrade33to34/VersionUpgrade33to34.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py index 16f17c5e36..e2241fd195 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py @@ -41,12 +41,16 @@ class VersionUpgrade33to34(VersionUpgrade): # Update version number. parser["general"]["version"] = "4" - #Renamed settings. if "values" in parser: + #If infill_hollow was enabled and the overhang angle was adjusted, copy that overhang angle to the new infill support angle. + if "infill_hollow" in parser["values"] and parser["values"]["infill_hollow"] and "support_angle" in parser["values"]: + parser["values"]["infill_support_angle"] = parser["values"]["support_angle"] + + #Renamed settings. for original, replacement in _renamed_settings.items(): - if original in parser["value"]: - parser["value"][replacement] = parser["value"][original] - del parser["value"][original] + if original in parser["values"]: + parser["values"][replacement] = parser["values"][original] + del parser["values"][original] result = io.StringIO() parser.write(result) From 3614da0f9d33a69b5ef904999f1ba50d96bdf3d1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 29 May 2018 13:22:59 +0200 Subject: [PATCH 036/109] Replace infill_hollow by infill_support_enabled It's not the exact same functionality, but very similar and equally expert. Contributes to issue CURA-5108. --- resources/setting_visibility/expert.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/setting_visibility/expert.cfg b/resources/setting_visibility/expert.cfg index 6d6b84883c..be0950ec6f 100644 --- a/resources/setting_visibility/expert.cfg +++ b/resources/setting_visibility/expert.cfg @@ -87,6 +87,7 @@ gradual_infill_steps gradual_infill_step_height infill_before_walls min_infill_area +infill_support_enabled skin_preshrink top_skin_preshrink bottom_skin_preshrink @@ -369,7 +370,6 @@ spaghetti_infill_extra_volume support_conical_enabled support_conical_angle support_conical_min_width -infill_hollow magic_fuzzy_skin_enabled magic_fuzzy_skin_thickness magic_fuzzy_skin_point_density From 7cf2832183d7cdd5550aac83012119e69b58c495 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 29 May 2018 13:43:08 +0200 Subject: [PATCH 037/109] Fix quality_group is None check in _setQualityGroup() CURA-5423 --- cura/Settings/MachineManager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 44f9f04740..84ffc8aaf4 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1032,6 +1032,10 @@ class MachineManager(QObject): self.activeQualityChangesGroupChanged.emit() def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None: + if quality_group is None: + self._setEmptyQuality() + return + if quality_group.node_for_global.getContainer() is None: return for node in quality_group.nodes_for_extruders.values(): @@ -1042,10 +1046,6 @@ class MachineManager(QObject): if empty_quality_changes: self._current_quality_changes_group = None - if quality_group is None: - self._setEmptyQuality() - return - # Set quality and quality_changes for the GlobalStack self._global_container_stack.quality = quality_group.node_for_global.getContainer() if empty_quality_changes: From a969d3c5eab54fc904b37611029a704c24c4a120 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 29 May 2018 14:37:13 +0200 Subject: [PATCH 038/109] CURA-5404 Remove retraction_amount values that must be in the material profile. --- resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg | 1 - resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg | 1 - resources/variants/ultimaker3_aa0.8.inst.cfg | 1 - resources/variants/ultimaker3_aa04.inst.cfg | 1 - resources/variants/ultimaker3_bb0.8.inst.cfg | 1 - resources/variants/ultimaker3_bb04.inst.cfg | 1 - resources/variants/ultimaker3_extended_aa0.8.inst.cfg | 1 - resources/variants/ultimaker3_extended_aa04.inst.cfg | 1 - resources/variants/ultimaker3_extended_bb0.8.inst.cfg | 1 - resources/variants/ultimaker3_extended_bb04.inst.cfg | 1 - resources/variants/ultimaker_s5_aa0.8.inst.cfg | 1 - resources/variants/ultimaker_s5_aa04.inst.cfg | 1 - resources/variants/ultimaker_s5_bb0.8.inst.cfg | 1 - resources/variants/ultimaker_s5_bb04.inst.cfg | 1 - 14 files changed, 14 deletions(-) diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg index 4ea3552891..ff64790c96 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg @@ -40,7 +40,6 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_size = 16 prime_tower_wipe_enabled = True -retraction_amount = 6.5 retraction_count_max = 12 retraction_extra_prime_amount = 0.8 retraction_extrusion_window = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index f0a889c12e..316161db32 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -39,7 +39,6 @@ material_print_temperature_layer_0 = =default_material_print_temperature + 2 material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_wipe_enabled = True -retraction_amount = 7 retraction_count_max = 12 retraction_extra_prime_amount = 0.8 retraction_extrusion_window = 1 diff --git a/resources/variants/ultimaker3_aa0.8.inst.cfg b/resources/variants/ultimaker3_aa0.8.inst.cfg index 025c346377..ee0aad9819 100644 --- a/resources/variants/ultimaker3_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_aa0.8.inst.cfg @@ -41,7 +41,6 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = True -retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker3_aa04.inst.cfg b/resources/variants/ultimaker3_aa04.inst.cfg index 84b137df60..0cc652d29e 100644 --- a/resources/variants/ultimaker3_aa04.inst.cfg +++ b/resources/variants/ultimaker3_aa04.inst.cfg @@ -22,7 +22,6 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 -retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index 32f9dc6fd2..5e19f2b259 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -54,7 +54,6 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 -retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker3_bb04.inst.cfg b/resources/variants/ultimaker3_bb04.inst.cfg index 8c94ce1e19..6fc83310f9 100644 --- a/resources/variants/ultimaker3_bb04.inst.cfg +++ b/resources/variants/ultimaker3_bb04.inst.cfg @@ -27,7 +27,6 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 -retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width diff --git a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg index ea2e92478a..16c857b2d5 100644 --- a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg @@ -41,7 +41,6 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = True -retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker3_extended_aa04.inst.cfg b/resources/variants/ultimaker3_extended_aa04.inst.cfg index e653616c89..215c08337d 100644 --- a/resources/variants/ultimaker3_extended_aa04.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa04.inst.cfg @@ -22,7 +22,6 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 -retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg index da47f6eb28..995d2a8d2e 100644 --- a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -54,7 +54,6 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 -retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker3_extended_bb04.inst.cfg b/resources/variants/ultimaker3_extended_bb04.inst.cfg index 04e625f87e..f9c31e9ef6 100644 --- a/resources/variants/ultimaker3_extended_bb04.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb04.inst.cfg @@ -27,7 +27,6 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 -retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width diff --git a/resources/variants/ultimaker_s5_aa0.8.inst.cfg b/resources/variants/ultimaker_s5_aa0.8.inst.cfg index 71b1f2f003..d88cb87e1d 100644 --- a/resources/variants/ultimaker_s5_aa0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.8.inst.cfg @@ -41,7 +41,6 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = =not magic_spiralize -retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker_s5_aa04.inst.cfg b/resources/variants/ultimaker_s5_aa04.inst.cfg index 9c3b5934ee..616b433658 100644 --- a/resources/variants/ultimaker_s5_aa04.inst.cfg +++ b/resources/variants/ultimaker_s5_aa04.inst.cfg @@ -22,7 +22,6 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 -retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker_s5_bb0.8.inst.cfg b/resources/variants/ultimaker_s5_bb0.8.inst.cfg index 0c77d42e7e..e1d08591d9 100644 --- a/resources/variants/ultimaker_s5_bb0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_bb0.8.inst.cfg @@ -54,7 +54,6 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 -retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker_s5_bb04.inst.cfg b/resources/variants/ultimaker_s5_bb04.inst.cfg index 76b609249a..f769cae63e 100644 --- a/resources/variants/ultimaker_s5_bb04.inst.cfg +++ b/resources/variants/ultimaker_s5_bb04.inst.cfg @@ -27,7 +27,6 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 -retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width From 9873d5728646b7f711f9ce4e53fa0cc38f41fc20 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 29 May 2018 14:48:34 +0200 Subject: [PATCH 039/109] CURA-5404 Change default retraction distance to 6.5mm for ultimaker3 and S5 --- resources/definitions/ultimaker3.def.json | 2 +- resources/definitions/ultimaker_s5.def.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 64948b4fe9..08fe01a76b 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -122,7 +122,7 @@ "raft_jerk": { "value": "jerk_layer_0" }, "raft_margin": { "value": "10" }, "raft_surface_layers": { "value": "1" }, - "retraction_amount": { "value": "2" }, + "retraction_amount": { "value": "6.5" }, "retraction_count_max": { "value": "10" }, "retraction_extrusion_window": { "value": "1" }, "retraction_hop": { "value": "2" }, diff --git a/resources/definitions/ultimaker_s5.def.json b/resources/definitions/ultimaker_s5.def.json index 8af77e22ac..f6971d0da3 100644 --- a/resources/definitions/ultimaker_s5.def.json +++ b/resources/definitions/ultimaker_s5.def.json @@ -119,7 +119,7 @@ "raft_margin": { "value": "10" }, "raft_speed": { "value": "25" }, "raft_surface_layers": { "value": "1" }, - "retraction_amount": { "value": "2" }, + "retraction_amount": { "value": "6.5" }, "retraction_count_max": { "value": "10" }, "retraction_extrusion_window": { "value": "1" }, "retraction_hop": { "value": "2" }, From fed0a015eefd87c4ca0cf63c78cffe249dcc7b91 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 29 May 2018 15:56:00 +0200 Subject: [PATCH 040/109] Clear is_user_specified_job_name flag when setting a project file name CURA-5280 --- cura/PrintInformation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 2c9f6511fa..9239dec8b7 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -329,6 +329,8 @@ class PrintInformation(QObject): baseNameChanged = pyqtSignal() def setBaseName(self, base_name: str, is_project_file: bool = False): + self._is_user_specified_job_name = False + # Ensure that we don't use entire path but only filename name = os.path.basename(base_name) From c77ea76b214a20ee814f24d4e319fe8967e9d70c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 29 May 2018 16:34:36 +0200 Subject: [PATCH 041/109] CURA-5357 Drag & dribble multiple packages like a plugable trickle, yo Also improved interfacing with `CuraVersion.py` --- plugins/Toolbox/src/Toolbox.py | 62 ++++++++++++++++++++-------------- resources/qml/Cura.qml | 15 ++++---- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e540bce523..b9e7f8f94d 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -28,7 +28,8 @@ i18n_catalog = i18nCatalog("cura") ## The Toolbox class is responsible of communicating with the server through the API class Toolbox(QObject, Extension): - DEFAULT_PACKAGES_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_VERSION = 1 def __init__(self, parent=None) -> None: super().__init__(parent) @@ -37,16 +38,10 @@ class Toolbox(QObject, Extension): self._package_manager = None self._plugin_registry = Application.getInstance().getPluginRegistry() - self._sdk_version = self._getPackagesVersion() - - self._cloud_api_version = 1 - self._cloud_api_root = self._getPackagesApiRoot() - - self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( - cloud_api_root = self._cloud_api_root, - cloud_api_version = self._cloud_api_version, - sdk_version = self._sdk_version - ) + self._sdk_version = None + self._cloud_api_version = None + self._cloud_api_root = None + self._api_url = None # Network: self._get_packages_request = None @@ -67,12 +62,7 @@ class Toolbox(QObject, Extension): ) ) ] - self._request_urls = { - "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), - "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), - "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), - "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)) - } + self._request_urls = {} self._to_update = [] # Package_ids that are waiting to be updated # Data: @@ -164,22 +154,44 @@ class Toolbox(QObject, Extension): # this is initialized. Therefore, we wait until the application is ready. def _onAppInitialized(self) -> None: self._package_manager = Application.getInstance().getCuraPackageManager() + self._sdk_version = self._getSDKVersion() + self._cloud_api_version = self._getCloudAPIVersion() + self._cloud_api_root = self._getCloudAPIRoot() + self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( + cloud_api_root=self._cloud_api_root, + cloud_api_version=self._cloud_api_version, + sdk_version=self._sdk_version + ) + self._request_urls = { + "authors": QUrl("{base_url}/authors".format(base_url=self._api_url)), + "packages": QUrl("{base_url}/packages".format(base_url=self._api_url)), + "plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)), + "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)) + } # Get the API root for the packages API depending on Cura version settings. - def _getPackagesApiRoot(self) -> str: + def _getCloudAPIRoot(self) -> str: if not hasattr(cura, "CuraVersion"): - return self.DEFAULT_PACKAGES_API_ROOT - if not hasattr(cura.CuraVersion, "CuraPackagesApiRoot"): - return self.DEFAULT_PACKAGES_API_ROOT - return cura.CuraVersion.CuraPackagesApiRoot + return self.DEFAULT_CLOUD_API_ROOT + if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): + return self.DEFAULT_CLOUD_API_ROOT + return cura.CuraVersion.CuraCloudAPIRoot + + # Get the cloud API version from CuraVersion + def _getCloudAPIVersion(self) -> int: + if not hasattr(cura, "CuraVersion"): + return self.DEFAULT_CLOUD_API_VERSION + if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): + return self.DEFAULT_CLOUD_API_VERSION + return cura.CuraVersion.CuraCloudAPIVersion # Get the packages version depending on Cura version settings. - def _getPackagesVersion(self) -> int: + def _getSDKVersion(self) -> int: if not hasattr(cura, "CuraVersion"): return self._plugin_registry.APIVersion - if not hasattr(cura.CuraVersion, "CuraPackagesVersion"): + if not hasattr(cura.CuraVersion, "CuraSDKVersion"): return self._plugin_registry.APIVersion - return cura.CuraVersion.CuraPackagesVersion + return cura.CuraVersion.CuraSDKVersion @pyqtSlot() def browsePackages(self) -> None: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e1cc4f6e45..b8f289278c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -323,10 +323,11 @@ UM.MainWindow { if (drop.urls.length > 0) { - // As the drop area also supports plugins, first check if it's a plugin that was dropped. - if (drop.urls.length == 1) + + var nonPackages = []; + for (var i = 0; i < drop.urls.length; i++) { - var filename = drop.urls[0]; + var filename = drop.urls[i]; if (filename.endsWith(".curapackage")) { // Try to install plugin & close. @@ -334,11 +335,13 @@ UM.MainWindow packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting."); packageInstallDialog.icon = StandardIcon.Information; packageInstallDialog.open(); - return; + } + else + { + others.push(filename); } } - - openDialog.handleOpenFileUrls(drop.urls); + openDialog.handleOpenFileUrls(nonPackages); } } } From b46a08f566b573dee767d1216fc9c340e350e02b Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 29 May 2018 18:15:16 +0200 Subject: [PATCH 042/109] Add Reft and Right side aligned action buttons to the message box CURA-4952 --- cura/CuraApplication.py | 2 +- .../FirmwareUpdateCheckerJob.py | 14 ++++++++++++-- plugins/SliceInfoPlugin/SliceInfo.py | 5 +++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index ed4336a9a0..34b6b5cde1 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -452,7 +452,7 @@ class CuraApplication(QtApplication): ## A reusable dialogbox # - showMessageBox = pyqtSignal(str, str, str, str, str, int, int, arguments = ["title", "footer", "text", "informativeText", "detailedText", "buttons", "icon"]) + showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []): self._message_box_callback = callback diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 19b77c1181..089d1847ed 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -75,9 +75,19 @@ class FirmwareUpdateCheckerJob(Job): machine_name=machine_name), title=i18n_catalog.i18nc( "@info:title The %s gets replaced with the printer name.", - "New %s firmware available") % machine_name, - footer = footer_message) + "New %s firmware available") % machine_name) + message.addAction("download", + i18n_catalog.i18nc("@action:button", "How to update"), + "[no_icon]", + "[no_description]", + button_style=Message.ActionButtonStyle.LINK, + button_align=Message.ActionButtonStyle.BUTTON_ALIGN_LEFT) + + + # If we do this in a cool way, the download url should be available in the JSON file + if self._set_download_url_callback: + self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware") message.actionTriggered.connect(self._callback) message.show() diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 82e07da464..b0525bdbfa 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -46,10 +46,11 @@ class SliceInfo(QObject, Extension): dismissable = False, title = catalog.i18nc("@info:title", "Collecting Data")) - self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None, - description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing.")) self.send_slice_info_message.addAction("MoreInfo", name = catalog.i18nc("@action:button", "More info"), icon = None, description = catalog.i18nc("@action:tooltip", "See more information on what data Cura sends."), button_style = Message.ActionButtonStyle.LINK) + + self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None, + description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing.")) self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() From 4d6753a1f163d78a9fe61756c94d22e674887a46 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 29 May 2018 18:33:38 +0200 Subject: [PATCH 043/109] remove unused code lines in firmware checker CURA-4952 --- plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 089d1847ed..ce4fb8c92f 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -64,11 +64,6 @@ class FirmwareUpdateCheckerJob(Job): if (checked_version != "") and (checked_version != current_version): Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE") - footer_text = i18n_catalog.i18nc("@action:info", "Read more on how to update printer firmware") - footer_link = "?url=https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update" - - footer_message = footer_text + " " + footer_link - message = Message(i18n_catalog.i18nc( "@info Don't translate {machine_name}, since it gets replaced by a printer name!", "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format( From 80d4989843f4c629a562d9816cabeaa276e2a110 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 30 May 2018 09:03:34 +0200 Subject: [PATCH 044/109] Show keep/discard user settings dialog upon variant change CURA-5417 --- cura/Settings/MachineManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 84ffc8aaf4..5c87b6ffba 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1350,6 +1350,10 @@ class MachineManager(QObject): self._updateMaterialWithVariant(position) self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + @pyqtSlot(str) def setQualityGroupByQualityType(self, quality_type: str) -> None: if self._global_container_stack is None: From 11db13aa2851e1c51b70b66ca37301a6d4968c76 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 29 May 2018 15:56:00 +0200 Subject: [PATCH 045/109] Clear is_user_specified_job_name flag when setting a project file name CURA-5280 --- cura/PrintInformation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 2c9f6511fa..9239dec8b7 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -329,6 +329,8 @@ class PrintInformation(QObject): baseNameChanged = pyqtSignal() def setBaseName(self, base_name: str, is_project_file: bool = False): + self._is_user_specified_job_name = False + # Ensure that we don't use entire path but only filename name = os.path.basename(base_name) From 13b3e4afa5c00ab3baab0570d318467f50fdf688 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 29 May 2018 16:34:36 +0200 Subject: [PATCH 046/109] CURA-5357 Drag & dribble multiple packages like a plugable trickle, yo Also improved interfacing with `CuraVersion.py` --- plugins/Toolbox/src/Toolbox.py | 62 ++++++++++++++++++++-------------- resources/qml/Cura.qml | 15 ++++---- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e540bce523..b9e7f8f94d 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -28,7 +28,8 @@ i18n_catalog = i18nCatalog("cura") ## The Toolbox class is responsible of communicating with the server through the API class Toolbox(QObject, Extension): - DEFAULT_PACKAGES_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_VERSION = 1 def __init__(self, parent=None) -> None: super().__init__(parent) @@ -37,16 +38,10 @@ class Toolbox(QObject, Extension): self._package_manager = None self._plugin_registry = Application.getInstance().getPluginRegistry() - self._sdk_version = self._getPackagesVersion() - - self._cloud_api_version = 1 - self._cloud_api_root = self._getPackagesApiRoot() - - self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( - cloud_api_root = self._cloud_api_root, - cloud_api_version = self._cloud_api_version, - sdk_version = self._sdk_version - ) + self._sdk_version = None + self._cloud_api_version = None + self._cloud_api_root = None + self._api_url = None # Network: self._get_packages_request = None @@ -67,12 +62,7 @@ class Toolbox(QObject, Extension): ) ) ] - self._request_urls = { - "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), - "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), - "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), - "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)) - } + self._request_urls = {} self._to_update = [] # Package_ids that are waiting to be updated # Data: @@ -164,22 +154,44 @@ class Toolbox(QObject, Extension): # this is initialized. Therefore, we wait until the application is ready. def _onAppInitialized(self) -> None: self._package_manager = Application.getInstance().getCuraPackageManager() + self._sdk_version = self._getSDKVersion() + self._cloud_api_version = self._getCloudAPIVersion() + self._cloud_api_root = self._getCloudAPIRoot() + self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( + cloud_api_root=self._cloud_api_root, + cloud_api_version=self._cloud_api_version, + sdk_version=self._sdk_version + ) + self._request_urls = { + "authors": QUrl("{base_url}/authors".format(base_url=self._api_url)), + "packages": QUrl("{base_url}/packages".format(base_url=self._api_url)), + "plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)), + "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)) + } # Get the API root for the packages API depending on Cura version settings. - def _getPackagesApiRoot(self) -> str: + def _getCloudAPIRoot(self) -> str: if not hasattr(cura, "CuraVersion"): - return self.DEFAULT_PACKAGES_API_ROOT - if not hasattr(cura.CuraVersion, "CuraPackagesApiRoot"): - return self.DEFAULT_PACKAGES_API_ROOT - return cura.CuraVersion.CuraPackagesApiRoot + return self.DEFAULT_CLOUD_API_ROOT + if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): + return self.DEFAULT_CLOUD_API_ROOT + return cura.CuraVersion.CuraCloudAPIRoot + + # Get the cloud API version from CuraVersion + def _getCloudAPIVersion(self) -> int: + if not hasattr(cura, "CuraVersion"): + return self.DEFAULT_CLOUD_API_VERSION + if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): + return self.DEFAULT_CLOUD_API_VERSION + return cura.CuraVersion.CuraCloudAPIVersion # Get the packages version depending on Cura version settings. - def _getPackagesVersion(self) -> int: + def _getSDKVersion(self) -> int: if not hasattr(cura, "CuraVersion"): return self._plugin_registry.APIVersion - if not hasattr(cura.CuraVersion, "CuraPackagesVersion"): + if not hasattr(cura.CuraVersion, "CuraSDKVersion"): return self._plugin_registry.APIVersion - return cura.CuraVersion.CuraPackagesVersion + return cura.CuraVersion.CuraSDKVersion @pyqtSlot() def browsePackages(self) -> None: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e1cc4f6e45..b8f289278c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -323,10 +323,11 @@ UM.MainWindow { if (drop.urls.length > 0) { - // As the drop area also supports plugins, first check if it's a plugin that was dropped. - if (drop.urls.length == 1) + + var nonPackages = []; + for (var i = 0; i < drop.urls.length; i++) { - var filename = drop.urls[0]; + var filename = drop.urls[i]; if (filename.endsWith(".curapackage")) { // Try to install plugin & close. @@ -334,11 +335,13 @@ UM.MainWindow packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting."); packageInstallDialog.icon = StandardIcon.Information; packageInstallDialog.open(); - return; + } + else + { + others.push(filename); } } - - openDialog.handleOpenFileUrls(drop.urls); + openDialog.handleOpenFileUrls(nonPackages); } } } From 9223122fc8af07bf209eb85a947e351e0d617262 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 30 May 2018 09:13:25 +0200 Subject: [PATCH 047/109] Add CuraCloudAPIRoot into CuraVersion.py.in and CMakeLists.txt CURA-5357 --- CMakeLists.txt | 2 ++ cura/CuraVersion.py.in | 1 + 2 files changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96efd68a2f..079b44890a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ endif() set(CURA_VERSION "master" CACHE STRING "Version name of Cura") set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") set(CURA_PACKAGES_VERSION "" CACHE STRING "Packages version of Cura") +set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative cloud API root of Cura") + configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY) configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index f45a24cae9..3077ad5441 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -5,3 +5,4 @@ CuraVersion = "@CURA_VERSION@" CuraBuildType = "@CURA_BUILDTYPE@" CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False CuraPackagesVersion = "@CURA_PACKAGES_VERSION@" +CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" From fae9bc838b4a9cb8d293b3785db5f5da4ce290c0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 30 May 2018 09:18:43 +0200 Subject: [PATCH 048/109] Update CURA_* setups in CuraVersion.py.in and CMakeLists.txt CURA-5357 --- CMakeLists.txt | 5 +++-- cura/CuraVersion.py.in | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 079b44890a..9e9bf4b538 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,8 +19,9 @@ endif() set(CURA_VERSION "master" CACHE STRING "Version name of Cura") set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") -set(CURA_PACKAGES_VERSION "" CACHE STRING "Packages version of Cura") -set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative cloud API root of Cura") +set(CURA_SDK_VERSION "" CACHE STRING "SDK version of Cura") +set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative Cura cloud API root") +set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version") configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY) configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index 3077ad5441..226b2183f2 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -4,5 +4,6 @@ CuraVersion = "@CURA_VERSION@" CuraBuildType = "@CURA_BUILDTYPE@" CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False -CuraPackagesVersion = "@CURA_PACKAGES_VERSION@" +CuraSDKVersion = "@CURA_SDK_VERSION@" CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" +CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@" From f1bb0e58e725d884a1d56cf5d7eeffff3372d388 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 30 May 2018 09:20:00 +0200 Subject: [PATCH 049/109] Do not use CuraVersion data if strings are empty CURA-5357 --- plugins/Toolbox/src/Toolbox.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index b9e7f8f94d..b019dfccbe 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -175,6 +175,8 @@ class Toolbox(QObject, Extension): return self.DEFAULT_CLOUD_API_ROOT if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): return self.DEFAULT_CLOUD_API_ROOT + if not cura.CuraVersion.CuraCloudAPIRoot: + return self.DEFAULT_CLOUD_API_ROOT return cura.CuraVersion.CuraCloudAPIRoot # Get the cloud API version from CuraVersion @@ -183,6 +185,8 @@ class Toolbox(QObject, Extension): return self.DEFAULT_CLOUD_API_VERSION if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): return self.DEFAULT_CLOUD_API_VERSION + if not cura.CuraVersion.CuraCloudAPIVersion: + return self.DEFAULT_CLOUD_API_VERSION return cura.CuraVersion.CuraCloudAPIVersion # Get the packages version depending on Cura version settings. @@ -191,6 +195,8 @@ class Toolbox(QObject, Extension): return self._plugin_registry.APIVersion if not hasattr(cura.CuraVersion, "CuraSDKVersion"): return self._plugin_registry.APIVersion + if not cura.CuraVersion.CuraSDKVersion: + return self._plugin_registry.APIVersion return cura.CuraVersion.CuraSDKVersion @pyqtSlot() From a616e923676a48cf36e3a4511c55630341a1ebda Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 30 May 2018 09:44:33 +0200 Subject: [PATCH 050/109] CURA-5357 fix boo boo, rename variable --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b8f289278c..dce106e219 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -338,7 +338,7 @@ UM.MainWindow } else { - others.push(filename); + nonPackages.push(filename); } } openDialog.handleOpenFileUrls(nonPackages); From 3395610b677e8be30f116a411e722a910b86a30a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 30 May 2018 09:44:33 +0200 Subject: [PATCH 051/109] CURA-5357 fix boo boo, rename variable --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b8f289278c..dce106e219 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -338,7 +338,7 @@ UM.MainWindow } else { - others.push(filename); + nonPackages.push(filename); } } openDialog.handleOpenFileUrls(nonPackages); From 6ea5924c95f8b8a2d6521d35df3568b146c5f73f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 30 May 2018 10:35:50 +0200 Subject: [PATCH 052/109] Remove Try Multiple Line Thicknesses setting This setting is now always enabled, hard-coded in the engine. --- resources/definitions/fdmprinter.def.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b3932de827..5ad478bfc3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -6678,14 +6678,6 @@ "type": "float", "enabled": "bridge_settings_enabled and bridge_enable_more_layers", "settable_per_mesh": true - }, - "wall_try_line_thickness": - { - "label": "Try Multiple Line Thicknesses", - "description": "When creating inner walls, try various line thicknesses to fit the wall lines better in narrow spaces. This reduces or increases the inner wall line width by up to 0.01mm.", - "default_value": false, - "type": "bool", - "settable_per_mesh": true } } }, From ed0a0dd7651d90c271b87b05e87427253a2d0583 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 30 May 2018 10:58:19 +0200 Subject: [PATCH 053/109] Fix multiply object in a rare case that an object is just too big --- cura/MultiplyObjectsJob.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 46f7f56f8a..af24036eeb 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -1,6 +1,8 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import copy + from UM.Job import Job from UM.Operations.GroupedOperation import GroupedOperation from UM.Message import Message @@ -64,6 +66,8 @@ class MultiplyObjectsJob(Job): # We do place the nodes one by one, as we want to yield in between. if not node_too_big: new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) + else: + new_node = copy.deepcopy(node) if node_too_big or not solution_found: found_solution_for_all = False new_location = new_node.getPosition() From 86d4f0583cccb869978084a67c6093267c2356fc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 30 May 2018 13:15:32 +0200 Subject: [PATCH 054/109] Make showing of support density image dependent on support pattern Because that's what it's actually depending on. Contributes to issue CURA-4513. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 5ad478bfc3..25fe841ee4 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5812,7 +5812,7 @@ "description": "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support.", "type": "str", "default_value": "", - "enabled": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'", + "enabled": "support_pattern == 'cross' or support_pattern == 'cross_3d'", "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": true From 00fe18008586fb33aeed1df9dc684f5f5d35d113 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 30 May 2018 16:29:55 +0200 Subject: [PATCH 055/109] Hide Toolbox Showcase & Materials for 3.4 --- plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml | 4 ++++ plugins/Toolbox/resources/qml/ToolboxHeader.qml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml index 170fd10fc7..69e508cd55 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml @@ -18,6 +18,8 @@ ScrollView spacing: UM.Theme.getSize("default_margin").height padding: UM.Theme.getSize("wide_margin").height height: childrenRect.height + 2 * padding + + /* Hide for 3.4 ToolboxDownloadsShowcase { id: showcase @@ -29,6 +31,8 @@ ScrollView width: parent.width height: UM.Theme.getSize("default_lining").height } + */ + ToolboxDownloadsGrid { id: allPlugins diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index 88495e3f63..ee4241beaf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -33,6 +33,8 @@ Item toolbox.viewPage = "overview" } } + + /* Hide for 3.4 ToolboxTabButton { text: catalog.i18nc("@title:tab", "Materials") @@ -45,6 +47,7 @@ Item toolbox.viewPage = "overview" } } + */ } ToolboxTabButton { From 931d4716e11d4a61f92879e3f52732e42bfe4a65 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 30 May 2018 16:58:11 +0200 Subject: [PATCH 056/109] CURA-5404 Retrieve the material settings' values from the variant before retrieving it from the definition if there are not defined in the material. --- cura/Settings/MachineManager.py | 9 +++++++++ resources/qml/Preferences/MaterialView.qml | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 78f462d8e9..1b7aef65d6 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -655,6 +655,15 @@ class MachineManager(QObject): return "" + @pyqtProperty(str, notify = activeVariantChanged) + def activeVariantId(self) -> str: + if self._active_container_stack: + variant = self._active_container_stack.variant + if variant: + return variant.getId() + + return "" + @pyqtProperty(str, notify = activeVariantChanged) def activeVariantBuildplateName(self) -> str: if self._global_container_stack: diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index ceb2ed12be..ad91f2ee9a 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -404,10 +404,17 @@ TabView id: spinBox anchors.left: label.right value: { + // In case the setting is not in the material... if (!isNaN(parseFloat(materialPropertyProvider.properties.value))) { return parseFloat(materialPropertyProvider.properties.value); } + // ... we search in the variant, and if it is not there... + if (!isNaN(parseFloat(variantPropertyProvider.properties.value))) + { + return parseFloat(variantPropertyProvider.properties.value); + } + // ... then look in the definition container. if (!isNaN(parseFloat(machinePropertyProvider.properties.value))) { return parseFloat(machinePropertyProvider.properties.value); @@ -431,6 +438,13 @@ TabView key: model.key } UM.ContainerPropertyProvider + { + id: variantPropertyProvider + containerId: Cura.MachineManager.activeVariantId + watchedProperties: [ "value" ] + key: model.key + } + UM.ContainerPropertyProvider { id: machinePropertyProvider containerId: Cura.MachineManager.activeDefinitionId From 33e49bebbed34cc7a18eab33ab0f38ba2fd140d7 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 31 May 2018 10:59:17 +0200 Subject: [PATCH 057/109] Don't connect when aborting while the signal is not yet connected Do this using try-except so that it is thread safe. --- plugins/Toolbox/src/Toolbox.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index b019dfccbe..13daeced2c 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -404,7 +404,10 @@ class Toolbox(QObject, Extension): def resetDownload(self) -> None: if self._download_reply: - self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) + try: + self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) + except TypeError: #Raised when the method is not connected to the signal yet. + pass #Don't need to disconnect. self._download_reply.abort() self._download_reply = None self._download_request = None From 416f695610ffc931e8517b5998a0002cab99abb8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 31 May 2018 11:52:36 +0200 Subject: [PATCH 058/109] Canceling sending job to cluster no longer results in "still sending print" message. Previously it would always show this, requiring a restart of Cura before print could be sent. CL-913 --- plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py | 4 ++++ plugins/UM3NetworkPrinting/PrintWindow.qml | 1 + 2 files changed, 5 insertions(+) diff --git a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py index 282d507e09..4c63115bb4 100644 --- a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py @@ -148,6 +148,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def selectPrinter(self, target_printer: str = "") -> None: self._sending_job.send(target_printer) + @pyqtSlot() + def cancelPrintSelection(self) -> None: + self._sending_gcode = False + ## Greenlet to send a job to the printer over the network. # # This greenlet gets called asynchronously in requestWrite. It is a diff --git a/plugins/UM3NetworkPrinting/PrintWindow.qml b/plugins/UM3NetworkPrinting/PrintWindow.qml index 5b011d98c4..0553db0eb2 100644 --- a/plugins/UM3NetworkPrinting/PrintWindow.qml +++ b/plugins/UM3NetworkPrinting/PrintWindow.qml @@ -90,6 +90,7 @@ UM.Dialog onClicked: { base.visible = false; printerSelectionCombobox.currentIndex = 0 + OutputDevice.cancelPrintSelection() } } ] From a468fc2cc4a42778c137b11029b8dfb3bfc8032c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 31 May 2018 12:21:51 +0200 Subject: [PATCH 059/109] Only load loading.gif when needed Toolbox crashes on Mac OS X due to reaching the max limit of file handlers. This seems to be caused by the loading the "loading.gif" animation image. Probably because many widgets are created and each of them has an animation image, and Qt (5.8?) seems to load everything at the same time. --- plugins/Toolbox/resources/qml/ToolboxProgressButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index b598bd96d0..2744e40ec9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -150,7 +150,7 @@ Item { id: loader visible: active - source: "../images/loading.gif" + source: visible ? "../images/loading.gif" : "" width: UM.Theme.getSize("toolbox_loader").width height: UM.Theme.getSize("toolbox_loader").height anchors.right: button.left From b3ef1bfd51e33ee91dd8047b33050e8ec2dc8bef Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 31 May 2018 14:29:56 +0200 Subject: [PATCH 060/109] Simplify UserAgreement --- plugins/UserAgreement/UserAgreement.py | 33 +++++++++++++------------- plugins/UserAgreement/__init__.py | 2 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/plugins/UserAgreement/UserAgreement.py b/plugins/UserAgreement/UserAgreement.py index 30b2c00f08..4ea1ccf9bb 100644 --- a/plugins/UserAgreement/UserAgreement.py +++ b/plugins/UserAgreement/UserAgreement.py @@ -1,27 +1,26 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Extension import Extension -from UM.Application import Application -from UM.PluginRegistry import PluginRegistry -from UM.Logger import Logger - -from cura.CuraApplication import CuraApplication +import os from PyQt5.QtCore import QObject, pyqtSlot -import os.path +from UM.Extension import Extension +from UM.Logger import Logger + class UserAgreement(QObject, Extension): - def __init__(self): + def __init__(self, application): super(UserAgreement, self).__init__() + self._application = application self._user_agreement_window = None self._user_agreement_context = None - Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - Application.getInstance().getPreferences().addPreference("general/accepted_user_agreement", False) + self._application.engineCreatedSignal.connect(self._onEngineCreated) + + self._application.getPreferences().addPreference("general/accepted_user_agreement", False) def _onEngineCreated(self): - if not Application.getInstance().getPreferences().getValue("general/accepted_user_agreement"): + if not self._application.getPreferences().getValue("general/accepted_user_agreement"): self.showUserAgreement() def showUserAgreement(self): @@ -34,14 +33,14 @@ class UserAgreement(QObject, Extension): def didAgree(self, user_choice): if user_choice: Logger.log("i", "User agreed to the user agreement") - Application.getInstance().getPreferences().setValue("general/accepted_user_agreement", True) + self._application.getPreferences().setValue("general/accepted_user_agreement", True) self._user_agreement_window.hide() else: Logger.log("i", "User did NOT agree to the user agreement") - Application.getInstance().getPreferences().setValue("general/accepted_user_agreement", False) - CuraApplication.getInstance().quit() - CuraApplication.getInstance().setNeedToShowUserAgreement(False) + self._application.getPreferences().setValue("general/accepted_user_agreement", False) + self._application.quit() + self._application.setNeedToShowUserAgreement(False) def createUserAgreementWindow(self): - path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "UserAgreement.qml") - self._user_agreement_window = Application.getInstance().createQmlComponent(path, {"manager": self}) + path = os.path.join(self._application.getPluginRegistry().getPluginPath(self.getPluginId()), "UserAgreement.qml") + self._user_agreement_window = self._application.createQmlComponent(path, {"manager": self}) diff --git a/plugins/UserAgreement/__init__.py b/plugins/UserAgreement/__init__.py index 88cb151f9e..3cf81c64f4 100644 --- a/plugins/UserAgreement/__init__.py +++ b/plugins/UserAgreement/__init__.py @@ -7,4 +7,4 @@ def getMetaData(): return {} def register(app): - return {"extension": UserAgreement.UserAgreement()} + return {"extension": UserAgreement.UserAgreement(app)} From a8173344a3f61f9a13c6e9f678810d2d783d1183 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 31 May 2018 14:45:57 +0200 Subject: [PATCH 061/109] Hidden materials --- plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml | 2 ++ plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index e1ffc6326c..05186b961d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -12,6 +12,7 @@ Column height: childrenRect.height width: parent.width spacing: UM.Theme.getSize("default_margin").height + /* Hidden for 3.4 Label { id: heading @@ -20,6 +21,7 @@ Column color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } + */ GridLayout { id: grid diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index 9d916182f6..bb0f6fe346 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -65,6 +65,7 @@ ScrollView } } } + /* Hidden in 3.4 Label { visible: toolbox.materialsInstalledModel.items.length > 0 @@ -102,5 +103,6 @@ ScrollView } } } + */ } } From 3fefdad14be57092abdae453e4685a9476f9995b Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 31 May 2018 15:22:49 +0200 Subject: [PATCH 062/109] Round margins CURA-5435 --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 78c970659c..ebd4d979f6 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -40,9 +40,9 @@ Item Column { id: pluginInfo - topPadding: UM.Theme.getSize("default_margin").height / 2 + topPadding: Math.floor(UM.Theme.getSize("default_margin").height / 2) property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text") - width: tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0)) + width: Math.floor(tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0))) Label { text: model.name From 30cbdfed6952df393f2390cff1f42cc06ea7d7c2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 31 May 2018 14:50:29 +0200 Subject: [PATCH 063/109] Make sure that file extensions are always lowered CURA-5367 --- cura/CuraApplication.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 34b6b5cde1..e416196775 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1572,10 +1572,11 @@ class CuraApplication(QtApplication): f = file.toLocalFile() extension = os.path.splitext(f)[1] + extension = extension.lower() filename = os.path.basename(f) if len(self._currently_loading_files) > 0: # If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files - if extension.lower() in self._non_sliceable_extensions: + if extension in self._non_sliceable_extensions: message = Message( self._i18n_catalog.i18nc("@info:status", "Only one G-code file can be loaded at a time. Skipped importing {0}", @@ -1584,7 +1585,8 @@ class CuraApplication(QtApplication): return # If file being loaded is non-slicable file, then prevent loading of any other files extension = os.path.splitext(self._currently_loading_files[0])[1] - if extension.lower() in self._non_sliceable_extensions: + extension = extension.lower() + if extension in self._non_sliceable_extensions: message = Message( self._i18n_catalog.i18nc("@info:status", "Can't open any other file if G-code is loading. Skipped importing {0}", From bb313c45a9465d4097b3b4d3a31ed6f99ceca278 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 31 May 2018 16:25:56 +0200 Subject: [PATCH 064/109] Set job name to "unnamed" if user types in nothing CURA-5367 --- resources/qml/JobSpecs.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index f9ce286706..6cd9999db7 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -81,10 +81,9 @@ Item { text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight onEditingFinished: { - PrintInformation.setJobName(text, true); - if (printJobTextfield.text != ''){ - printJobTextfield.focus = false; - } + text = text == "" ? "unnamed" : text; + PrintInformation.setJobName(printJobTextfield.text, true); + printJobTextfield.focus = false; } validator: RegExpValidator { regExp: /^[^\\ \/ \*\?\|\[\]]*$/ From c988e0eccb88652971de47bb3a386fce52e3a264 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 31 May 2018 17:20:53 +0200 Subject: [PATCH 065/109] Also added "none" to the state list of prints_jobs not to be matched with printers --- plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py index 4c63115bb4..c54ced6b13 100644 --- a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py @@ -392,7 +392,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJob(print_job, print_job_data) if print_job.state != "queued": # Print job should be assigned to a printer. - if print_job.state in ["failed", "finished", "aborted"]: + if print_job.state in ["failed", "finished", "aborted", "none"]: # Print job was already completed, so don't attach it to a printer. printer = None else: From bc1fa5c3f9b361ca7f0c764eea5dab7b56fbb9df Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 1 Jun 2018 09:38:37 +0200 Subject: [PATCH 066/109] CURA-5435 Attempt to fix "wobbly" text --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index ebd4d979f6..b16564fdd2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -16,7 +16,7 @@ Item { color: UM.Theme.getColor("lining") width: parent.width - height: UM.Theme.getSize("default_lining").height + height: Math.floor(UM.Theme.getSize("default_lining").height) anchors.bottom: parent.bottom } Row @@ -47,7 +47,7 @@ Item { text: model.name width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap font: UM.Theme.getFont("default_bold") color: pluginInfo.color @@ -81,7 +81,7 @@ Item } } width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft From 9b5369a5b1b3b27bd9bdc5b393797980caa2a459 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 1 Jun 2018 09:38:45 +0200 Subject: [PATCH 067/109] Revert "CURA-5435 Attempt to fix "wobbly" text" This reverts commit bc1fa5c3f9b361ca7f0c764eea5dab7b56fbb9df. --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index b16564fdd2..ebd4d979f6 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -16,7 +16,7 @@ Item { color: UM.Theme.getColor("lining") width: parent.width - height: Math.floor(UM.Theme.getSize("default_lining").height) + height: UM.Theme.getSize("default_lining").height anchors.bottom: parent.bottom } Row @@ -47,7 +47,7 @@ Item { text: model.name width: parent.width - height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) + height: UM.Theme.getSize("toolbox_property_label").height wrapMode: Text.WordWrap font: UM.Theme.getFont("default_bold") color: pluginInfo.color @@ -81,7 +81,7 @@ Item } } width: parent.width - height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) + height: UM.Theme.getSize("toolbox_property_label").height wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft From cca51cb74b8befea5cab8d078587d06c1b952bcc Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 1 Jun 2018 09:39:04 +0200 Subject: [PATCH 068/109] Revert "Revert "CURA-5435 Attempt to fix "wobbly" text"" This reverts commit 9b5369a5b1b3b27bd9bdc5b393797980caa2a459. --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index ebd4d979f6..b16564fdd2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -16,7 +16,7 @@ Item { color: UM.Theme.getColor("lining") width: parent.width - height: UM.Theme.getSize("default_lining").height + height: Math.floor(UM.Theme.getSize("default_lining").height) anchors.bottom: parent.bottom } Row @@ -47,7 +47,7 @@ Item { text: model.name width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap font: UM.Theme.getFont("default_bold") color: pluginInfo.color @@ -81,7 +81,7 @@ Item } } width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft From a4b46be2c58ce6641b3ac2f21f400a32ae84efd9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 1 Jun 2018 12:58:08 +0200 Subject: [PATCH 069/109] CURA-5427 Force the material models to update when the machine changes and so when the extruder stack changes. - What happen was that when the extruder model needed to update, the material model was not updated correctly and so when changing a material, the node was incorrect. --- cura/Machines/Models/BaseMaterialsModel.py | 6 +++++- resources/qml/Menus/MaterialMenu.qml | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 0a1337feeb..4759c8b5b0 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -39,6 +39,8 @@ class BaseMaterialsModel(ListModel): self._extruder_position = 0 self._extruder_stack = None + # Update the stack and the model data when the machine changes + self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) def _updateExtruderStack(self): global_stack = self._machine_manager.activeMachine @@ -50,9 +52,11 @@ class BaseMaterialsModel(ListModel): self._extruder_stack = global_stack.extruders.get(str(self._extruder_position)) if self._extruder_stack is not None: self._extruder_stack.pyqtContainersChanged.connect(self._update) + # Force update the model when the extruder stack changes + self._update() def setExtruderPosition(self, position: int): - if self._extruder_position != position: + if self._extruder_stack is None or self._extruder_position != position: self._extruder_position = position self._updateExtruderStack() self.extruderPositionChanged.emit() diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index d81e0c86c3..64b3130724 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -63,8 +63,7 @@ Menu exclusiveGroup: group onTriggered: { - var activeExtruderIndex = Cura.ExtruderManager.activeExtruderIndex; - Cura.MachineManager.setMaterial(activeExtruderIndex, model.container_node); + Cura.MachineManager.setMaterial(extruderIndex, model.container_node); } } onObjectAdded: brandMaterialsMenu.insertItem(index, object) From 85560abca308dce140671e4c5a3364ea1507207c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 1 Jun 2018 13:26:14 +0200 Subject: [PATCH 070/109] CURA-5439 Show the keep or discard changes dialog also when the user applies a configuration using the Sync button. --- cura/Settings/MachineManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5c87b6ffba..723bf91d0d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1280,6 +1280,10 @@ class MachineManager(QObject): self._global_container_stack.variant = self._empty_variant_container self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + ## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value' def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None: machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine") From a0263676e1305c2cc4b778ed5cfb7215e938705a Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 1 Jun 2018 13:36:50 +0200 Subject: [PATCH 071/109] The prepare button slices, so it should be a slice button --- resources/qml/SaveButton.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 0369f492b4..77537b0af2 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -100,8 +100,8 @@ Item { if (saveToButton.enabled) { saveToButton.clicked(); } - // prepare button - if (prepareButton.enabled) { + // slice button + if (sliceButton.enabled) { sliceOrStopSlicing(); } } @@ -131,7 +131,7 @@ Item { Row { id: additionalComponentsRow anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) + anchors.right: saveToButton.visible ? saveToButton.left : (sliceButton.visible ? sliceButton.left : parent.right) anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -159,14 +159,14 @@ Item { onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice"); - prepareButton.autoSlice = autoSlice; + sliceButton.autoSlice = autoSlice; saveToButton.autoSlice = autoSlice; } } - // Prepare button, only shows if auto_slice is off + // Slice button, only shows if auto_slice is off Button { - id: prepareButton + id: sliceButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing @@ -180,7 +180,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Slice") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); From a479176c63fe3c53a380b47d0d1abe3b2c80c4a0 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 1 Jun 2018 13:36:50 +0200 Subject: [PATCH 072/109] The prepare button slices, so it should be a slice button --- resources/qml/SaveButton.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 0369f492b4..77537b0af2 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -100,8 +100,8 @@ Item { if (saveToButton.enabled) { saveToButton.clicked(); } - // prepare button - if (prepareButton.enabled) { + // slice button + if (sliceButton.enabled) { sliceOrStopSlicing(); } } @@ -131,7 +131,7 @@ Item { Row { id: additionalComponentsRow anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) + anchors.right: saveToButton.visible ? saveToButton.left : (sliceButton.visible ? sliceButton.left : parent.right) anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -159,14 +159,14 @@ Item { onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice"); - prepareButton.autoSlice = autoSlice; + sliceButton.autoSlice = autoSlice; saveToButton.autoSlice = autoSlice; } } - // Prepare button, only shows if auto_slice is off + // Slice button, only shows if auto_slice is off Button { - id: prepareButton + id: sliceButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing @@ -180,7 +180,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Slice") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); From 41e94cd2a15c022a616798bcfafaf6583119d8e2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 1 Jun 2018 13:51:43 +0200 Subject: [PATCH 073/109] Fix merge problems from 3.4 --- cura/Settings/MachineManager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 87c914bed3..3ee14ca85b 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1280,7 +1280,7 @@ class MachineManager(QObject): self._updateQualityWithMaterial() # See if we need to show the Discard or Keep changes screen - if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: self._application.discardOrKeepProfileChanges() ## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value' @@ -1335,7 +1335,7 @@ class MachineManager(QObject): self._updateQualityWithMaterial() # See if we need to show the Discard or Keep changes screen - if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: self._application.discardOrKeepProfileChanges() @pyqtSlot(str, str) @@ -1354,7 +1354,7 @@ class MachineManager(QObject): self._updateQualityWithMaterial() # See if we need to show the Discard or Keep changes screen - if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: self._application.discardOrKeepProfileChanges() @pyqtSlot(str) From e859861517202121b2f715212f9209d50ecced5f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 1 Jun 2018 13:55:28 +0200 Subject: [PATCH 074/109] No need to set parent for ExtruderStack --- cura/Settings/CuraStackBuilder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index e401cad1ae..7aef63db2f 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -143,7 +143,7 @@ class CuraStackBuilder: application = CuraApplication.getInstance() registry = application.getContainerRegistry() - stack = ExtruderStack(new_stack_id, parent = global_stack) + stack = ExtruderStack(new_stack_id) stack.setName(extruder_definition.getName()) stack.setDefinition(extruder_definition) From 2fbcc2212386c52f18dfb9ba0fbc16e36238b8aa Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jun 2018 14:02:32 +0200 Subject: [PATCH 075/109] Fix adding printers due to unused 'parent' parameter The 'parent' parameter was unused, so I removed it. But I didn't remove all things that called it, apparently. I just removed some. I didn't try the stackbuilder. Contributes to issue CURA-5330. --- cura/Settings/CuraContainerStack.py | 4 ++-- cura/Settings/CuraStackBuilder.py | 7 +++---- cura/Settings/ExtruderStack.py | 4 ++-- cura/Settings/GlobalStack.py | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 308a91bc76..e1f89eb725 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -39,8 +39,8 @@ from . import Exceptions # This also means that operations on the stack that modifies the container ordering is prohibited and # will raise an exception. class CuraContainerStack(ContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self._container_registry = ContainerRegistry.getInstance() diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 640489adb3..da8315b3d2 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -99,8 +99,7 @@ class CuraStackBuilder: position = position, variant_container = extruder_variant_container, material_container = material_container, - quality_container = application.empty_quality_container, - global_stack = new_global_stack, + quality_container = application.empty_quality_container ) new_extruder.setNextStack(new_global_stack) new_global_stack.addExtruder(new_extruder) @@ -139,11 +138,11 @@ class CuraStackBuilder: @classmethod def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str, position: int, - variant_container, material_container, quality_container, global_stack) -> ExtruderStack: + variant_container, material_container, quality_container) -> ExtruderStack: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() - stack = ExtruderStack(new_stack_id, parent = global_stack) + stack = ExtruderStack(new_stack_id) stack.setName(extruder_definition.getName()) stack.setDefinition(extruder_definition) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 5e944b401f..b3f7d529a2 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -25,8 +25,8 @@ if TYPE_CHECKING: # # class ExtruderStack(CuraContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self.addMetaDataEntry("type", "extruder_train") # For backward compatibility diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index f76ac1ab3f..6d300954c2 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -23,8 +23,8 @@ from .CuraContainerStack import CuraContainerStack ## Represents the Global or Machine stack and its related containers. # class GlobalStack(CuraContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self.addMetaDataEntry("type", "machine") # For backward compatibility From 74ba10444e48b303df057520c693d899c6b5dc00 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 1 Jun 2018 14:31:52 +0200 Subject: [PATCH 076/109] CURA-5357 Prevent uninstall of plugins that are not-yet-installed --- cura/CuraPackageManager.py | 3 +++ .../resources/qml/ToolboxInstalledTileActions.qml | 12 +++++++++++- plugins/Toolbox/src/PackagesModel.py | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index bc2e020037..096bfc9065 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -158,14 +158,17 @@ class CuraPackageManager(QObject): # Add bundled plugins if package_id in self._bundled_package_dict: package_info = self._bundled_package_dict[package_id]["package_info"] + package_info["is_installed"] = True # Add installed plugins if package_id in self._installed_package_dict: package_info = self._installed_package_dict[package_id]["package_info"] + package_info["is_installed"] = True # Add to install plugins if package_id in self._to_install_package_dict: package_info = self._to_install_package_dict[package_id]["package_info"] + package_info["is_installed"] = False if package_info is None: continue diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 0ae738b71d..b0aecfc9a2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -13,6 +13,16 @@ Column width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height + Label + { + visible: !model.is_installed + text: catalog.i18nc("@label", "Will install upon restarting") + color: UM.Theme.getColor("lining") + font: UM.Theme.getFont("default") + wrapMode: Text.WordWrap + width: parent.width + } + ToolboxProgressButton { id: updateButton @@ -39,7 +49,7 @@ Column { id: removeButton text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall") - visible: !model.is_bundled + visible: !model.is_bundled && model.is_installed enabled: !toolbox.isDownloading style: ButtonStyle { diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 5ec3eba1d8..2a3b169e87 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -29,8 +29,9 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 12, "last_updated") self.addRoleName(Qt.UserRole + 13, "is_bundled") self.addRoleName(Qt.UserRole + 14, "is_enabled") - self.addRoleName(Qt.UserRole + 15, "has_configs") - self.addRoleName(Qt.UserRole + 16, "supported_configs") + self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed + self.addRoleName(Qt.UserRole + 16, "has_configs") + self.addRoleName(Qt.UserRole + 17, "supported_configs") # List of filters for queries. The result is the union of the each list of results. self._filter = {} # type: Dict[str, str] @@ -73,6 +74,7 @@ class PackagesModel(ListModel): "last_updated": package["last_updated"] if "last_updated" in package else None, "is_bundled": package["is_bundled"] if "is_bundled" in package else False, "is_enabled": package["is_enabled"] if "is_enabled" in package else False, + "is_installed": package["is_installed"] if "is_installed" in package else False, "has_configs": has_configs, "supported_configs": configs_model }) From 30a05e6ba6e7af6b498276bcc5b62229c4dd3821 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 3 Jun 2018 12:57:08 +0200 Subject: [PATCH 077/109] make sure old plugins are updatable --- plugins/Toolbox/src/Toolbox.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index b019dfccbe..99d7ac46c3 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -169,6 +169,27 @@ class Toolbox(QObject, Extension): "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)) } + OLD_PLUGINS = ['3DPrinterOS', + 'BarbarianPlugin', + 'CuraSolidWorksPlugin', + 'MakePrintablePlugin', + 'OctoPrintPlugin', + 'OrientationPlugin', + 'ZOffsetPlugin', + 'cura-siemensnx-plugin'] + + # check for plugins that were installed with the old plugin-browser + def _isOldPlugin(self, plugin_id) -> bool: + if plugin_id in self.OLD_PLUGINS and plugin_id in self._plugin_registry.getInstalledPlugins(): + Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) + if not self._package_manager.isPackageInstalled(plugin_id): + Logger.log('i', 'Plugin was not found in package.json: %s', self._package_manager.getInstalledPackageInfo(plugin_id)) + return True + return False + + + + # Get the API root for the packages API depending on Cura version settings. def _getCloudAPIRoot(self) -> str: if not hasattr(cura, "CuraVersion"): @@ -325,6 +346,9 @@ class Toolbox(QObject, Extension): # -------------------------------------------------------------------------- @pyqtSlot(str, result = bool) def canUpdate(self, package_id: str) -> bool: + if self._isOldPlugin(package_id): + return True + local_package = self._package_manager.getInstalledPackageInfo(package_id) if local_package is None: return False @@ -355,7 +379,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = bool) def isInstalled(self, package_id: str) -> bool: - return self._package_manager.isPackageInstalled(package_id) + return self._package_manager.isPackageInstalled(package_id) or self._isOldPlugin(package_id) @pyqtSlot(str, result = bool) def isEnabled(self, package_id: str) -> bool: From 0e11a165a3a39ec74f1884041e6f3a5734ed817b Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 3 Jun 2018 18:14:34 +0200 Subject: [PATCH 078/109] add typing --- plugins/Toolbox/src/Toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 99d7ac46c3..4bf04c1e38 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -179,7 +179,7 @@ class Toolbox(QObject, Extension): 'cura-siemensnx-plugin'] # check for plugins that were installed with the old plugin-browser - def _isOldPlugin(self, plugin_id) -> bool: + def _isOldPlugin(self, plugin_id: str) -> bool: if plugin_id in self.OLD_PLUGINS and plugin_id in self._plugin_registry.getInstalledPlugins(): Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) if not self._package_manager.isPackageInstalled(plugin_id): From 4922988e7daa0ccd2cf6e070d55fca1a3ccbd99c Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 4 Jun 2018 11:11:02 +0200 Subject: [PATCH 079/109] Added .3mf extension to the MimeType CURA-5367 --- plugins/3MFReader/ThreeMFReader.py | 10 ---------- plugins/3MFReader/__init__.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 9eec09b202..6c2fb9a59d 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -15,7 +15,6 @@ from UM.Math.Vector import Vector from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshReader import MeshReader from UM.Scene.GroupDecorator import GroupDecorator -from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from cura.Settings.ExtruderManager import ExtruderManager from cura.Scene.CuraSceneNode import CuraSceneNode @@ -26,15 +25,6 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch MYPY = False - -MimeTypeDatabase.addMimeType( - MimeType( - name = "application/x-cura-project-file", - comment = "Cura Project File", - suffixes = ["curaproject.3mf"] - ) -) - try: if not MYPY: import xml.etree.cElementTree as ET diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 9da54586a3..304cd8ef65 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -13,8 +13,24 @@ from . import ThreeMFWorkspaceReader from UM.i18n import i18nCatalog from UM.Platform import Platform +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType catalog = i18nCatalog("cura") +MimeTypeDatabase.addMimeType( + MimeType( + name = "application/x-cura-project-file", + comment = "Cura Project File", + suffixes = ["curaproject.3mf"] + ) +) +MimeTypeDatabase.addMimeType( + MimeType( + name = "application/x-cura-project-file", + comment = "Cura Project File", + suffixes = ["3mf"] + ) +) + def getMetaData() -> Dict: # Workarround for osx not supporting double file extensions correctly. if Platform.isOSX(): From 14294936c544abcb8bcfb779cca561d6afc85018 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Jun 2018 11:39:37 +0200 Subject: [PATCH 080/109] CURA-5434 reversed the role of offset_shape_arr and hull_shape_arr, to fix one-at-a-time arranging --- cura/Arranging/Arrange.py | 14 ++++++++------ cura/Arranging/ArrangeObjectsJob.py | 6 +++--- cura/MultiplyObjectsJob.py | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index 1027b39199..21ed45dbf1 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -3,6 +3,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Logger import Logger +from UM.Math.Polygon import Polygon from UM.Math.Vector import Vector from cura.Arranging.ShapeArray import ShapeArray from cura.Scene import ZOffsetDecorator @@ -45,7 +46,7 @@ class Arrange: # \param scene_root Root for finding all scene nodes # \param fixed_nodes Scene nodes to be placed @classmethod - def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250): + def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250, min_offset = 8): arranger = Arrange(x, y, x // 2, y // 2, scale = scale) arranger.centerFirst() @@ -58,9 +59,10 @@ class Arrange: # Place all objects fixed nodes for fixed_node in fixed_nodes: - vertices = fixed_node.callDecoration("getConvexHull") + vertices = fixed_node.callDecoration("getConvexHullHead") or fixed_node.callDecoration("getConvexHull") if not vertices: continue + vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset)) points = copy.deepcopy(vertices._points) shape_arr = ShapeArray.fromPolygon(points, scale = scale) arranger.place(0, 0, shape_arr) @@ -81,12 +83,12 @@ class Arrange: ## Find placement for a node (using offset shape) and place it (using hull shape) # return the nodes that should be placed # \param node - # \param offset_shape_arr ShapeArray with offset, used to find location - # \param hull_shape_arr ShapeArray without offset, for placing the shape + # \param offset_shape_arr ShapeArray with offset, for placing the shape + # \param hull_shape_arr ShapeArray without offset, used to find location def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1): new_node = copy.deepcopy(node) best_spot = self.bestSpot( - offset_shape_arr, start_prio = self._last_priority, step = step) + hull_shape_arr, start_prio = self._last_priority, step = step) x, y = best_spot.x, best_spot.y # Save the last priority. @@ -102,7 +104,7 @@ class Arrange: if x is not None: # We could find a place new_node.setPosition(Vector(x, center_y, y)) found_spot = True - self.place(x, y, hull_shape_arr) # place the object in arranger + self.place(x, y, offset_shape_arr) # place the object in arranger else: Logger.log("d", "Could not find spot!"), found_spot = False diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 08fd1985a9..5e982582fd 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -37,7 +37,7 @@ class ArrangeObjectsJob(Job): machine_width = global_container_stack.getProperty("machine_width", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value") - arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes) + arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes, min_offset = self._min_offset) # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) @@ -66,7 +66,7 @@ class ArrangeObjectsJob(Job): start_priority = last_priority else: start_priority = 0 - best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority) + best_spot = arranger.bestSpot(hull_shape_arr, start_prio = start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): @@ -77,7 +77,7 @@ class ArrangeObjectsJob(Job): last_size = size last_priority = best_spot.priority - arranger.place(x, y, hull_shape_arr) # take place before the next one + arranger.place(x, y, offset_shape_arr) # take place before the next one grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) else: Logger.log("d", "Arrange all: could not find spot!") diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index af24036eeb..57db7734e7 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -38,7 +38,7 @@ class MultiplyObjectsJob(Job): root = scene.getRoot() scale = 0.5 - arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale) + arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale, min_offset = self._min_offset) processed_nodes = [] nodes = [] From a9095f2b778cffb6d2f5049f8638010cc1c0b651 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 4 Jun 2018 11:46:51 +0200 Subject: [PATCH 081/109] After command "Undo" the project name was empty CURA-5367 --- cura/PrintInformation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 9239dec8b7..d17720228f 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -357,11 +357,12 @@ class PrintInformation(QObject): data = mime_type.stripExtension(name) except: Logger.log("w", "Unsupported Mime Type Database file extension") + data = 'unnamed' if data is not None and check_name is not None: self._base_name = data else: - self._base_name = '' + self._base_name = 'unnamed' self._updateJobName() From 096f0775a8e08b3d7bdbb4648307c30c78f709f7 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 4 Jun 2018 11:49:42 +0200 Subject: [PATCH 082/109] CURA-5442 Compare plugin registry to package manager --- cura/CuraPackageManager.py | 8 +++++- plugins/Toolbox/src/Toolbox.py | 49 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 096bfc9065..24a7bda267 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -134,7 +134,7 @@ class CuraPackageManager(QObject): return None - def getAllInstalledPackagesInfo(self) -> dict: + def getAllInstalledPackageIDs(self) -> set: # Add bundled, installed, and to-install packages to the set of installed package IDs all_installed_ids = set() @@ -147,6 +147,12 @@ class CuraPackageManager(QObject): if self._to_install_package_dict.keys(): all_installed_ids = all_installed_ids.union(set(self._to_install_package_dict.keys())) + return all_installed_ids + + def getAllInstalledPackagesInfo(self) -> dict: + + all_installed_ids = self.getAllInstalledPackageIDs() + # map of -> -> installed_packages_dict = {} for package_id in all_installed_ids: diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 4bf04c1e38..9e3adcc789 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -64,14 +64,17 @@ class Toolbox(QObject, Extension): ] self._request_urls = {} self._to_update = [] # Package_ids that are waiting to be updated + self._old_plugin_ids = [] # Data: self._metadata = { "authors": [], "packages": [], "plugins_showcase": [], + "plugins_available": [], "plugins_installed": [], "materials_showcase": [], + "materials_available": [], "materials_installed": [] } @@ -166,30 +169,11 @@ class Toolbox(QObject, Extension): "authors": QUrl("{base_url}/authors".format(base_url=self._api_url)), "packages": QUrl("{base_url}/packages".format(base_url=self._api_url)), "plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)), - "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)) + "plugins_available": QUrl("{base_url}/packages?package_type=plugin".format(base_url=self._api_url)), + "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)), + "materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url=self._api_url)) } - OLD_PLUGINS = ['3DPrinterOS', - 'BarbarianPlugin', - 'CuraSolidWorksPlugin', - 'MakePrintablePlugin', - 'OctoPrintPlugin', - 'OrientationPlugin', - 'ZOffsetPlugin', - 'cura-siemensnx-plugin'] - - # check for plugins that were installed with the old plugin-browser - def _isOldPlugin(self, plugin_id: str) -> bool: - if plugin_id in self.OLD_PLUGINS and plugin_id in self._plugin_registry.getInstalledPlugins(): - Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) - if not self._package_manager.isPackageInstalled(plugin_id): - Logger.log('i', 'Plugin was not found in package.json: %s', self._package_manager.getInstalledPackageInfo(plugin_id)) - return True - return False - - - - # Get the API root for the packages API depending on Cura version settings. def _getCloudAPIRoot(self) -> str: if not hasattr(cura, "CuraVersion"): @@ -256,6 +240,17 @@ class Toolbox(QObject, Extension): @pyqtSlot() def _updateInstalledModels(self) -> None: + + # This is moved here to avoid code duplication and so that after installing plugins they get removed from the + # list of old plugins + old_plugin_ids = self._plugin_registry.getInstalledPlugins() + installed_package_ids = self._package_manager.getAllInstalledPackageIDs() + self._old_plugin_ids = [] + for plugin_id in old_plugin_ids: + if plugin_id not in installed_package_ids: + Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) + self._old_plugin_ids.append(plugin_id) + all_packages = self._package_manager.getAllInstalledPackagesInfo() if "plugin" in all_packages: self._metadata["plugins_installed"] = all_packages["plugin"] @@ -346,7 +341,7 @@ class Toolbox(QObject, Extension): # -------------------------------------------------------------------------- @pyqtSlot(str, result = bool) def canUpdate(self, package_id: str) -> bool: - if self._isOldPlugin(package_id): + if self.isOldPlugin(package_id): return True local_package = self._package_manager.getInstalledPackageInfo(package_id) @@ -379,7 +374,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = bool) def isInstalled(self, package_id: str) -> bool: - return self._package_manager.isPackageInstalled(package_id) or self._isOldPlugin(package_id) + return self._package_manager.isPackageInstalled(package_id) @pyqtSlot(str, result = bool) def isEnabled(self, package_id: str) -> bool: @@ -387,6 +382,12 @@ class Toolbox(QObject, Extension): return True return False + # Check for plugins that were installed with the old plugin browser + def isOldPlugin(self, plugin_id: str) -> bool: + if plugin_id in self._old_plugin_ids: + return True + return False + def loadingComplete(self) -> bool: populated = 0 for list in self._metadata.items(): From 79d6a9a614ec67d66833cfa75238dff626661450 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Jun 2018 12:53:13 +0200 Subject: [PATCH 083/109] CURA-5434 fixed arrange to all build plates by reversing hull_shape and offset_shape --- cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py index 252cef4e65..6abc553a4b 100644 --- a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py +++ b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py @@ -110,7 +110,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job): arrange_array.add() arranger = arrange_array.get(current_build_plate_number) - best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority) + best_spot = arranger.bestSpot(hull_shape_arr, start_prio=start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): @@ -118,7 +118,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job): else: center_y = 0 if x is not None: # We could find a place - arranger.place(x, y, hull_shape_arr) # place the object in the arranger + arranger.place(x, y, offset_shape_arr) # place the object in the arranger node.callDecoration("setBuildPlateNumber", current_build_plate_number) grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) From 68ddf443a1eb9bb1065a123c68619d77388fd2c2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jun 2018 12:58:56 +0200 Subject: [PATCH 084/109] Use explicit key-word arguments instead of kwargs Makes it easier to use, and better self-documenting. Contributes to issue CURA-5330. --- cura/Scene/CuraSceneNode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 428a59f554..9e641b5b68 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -13,9 +13,9 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator ## Scene nodes that are models are only seen when selecting the corresponding build plate # Note that many other nodes can just be UM SceneNode objects. class CuraSceneNode(SceneNode): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if "no_setting_override" not in kwargs: + def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_settings_override: bool = False): + super().__init__(parent = parent, visible = visible, name = name) + if not "no_setting_override": self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled self._outside_buildarea = False From 360d4f14e4c4e235645eaa4720ead23f5fd2ec3d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jun 2018 12:59:44 +0200 Subject: [PATCH 085/109] Fix broken import Oops. Contributes to issue CURA-5330. --- cura/Scene/CuraSceneNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 9e641b5b68..651eef9efc 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from copy import deepcopy -from typing import List +from typing import List, Optional from UM.Application import Application from UM.Math.AxisAlignedBox import AxisAlignedBox From 258a90d8507f7e83eaa0ebc1fab588c96001b6a6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jun 2018 13:00:57 +0200 Subject: [PATCH 086/109] Fix parameter Oops again. I should not do this when I have 30 seconds left on the clock. Contributes to issue CURA-5330. --- cura/Scene/CuraSceneNode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 651eef9efc..92f1d839fb 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -13,9 +13,9 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator ## Scene nodes that are models are only seen when selecting the corresponding build plate # Note that many other nodes can just be UM SceneNode objects. class CuraSceneNode(SceneNode): - def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_settings_override: bool = False): + def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False): super().__init__(parent = parent, visible = visible, name = name) - if not "no_setting_override": + if not no_setting_override: self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled self._outside_buildarea = False From e518d07f5a5ca2a56fed51b6862c60bdd9d36963 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 4 Jun 2018 13:22:08 +0200 Subject: [PATCH 087/109] No need to give parent to ExtruderStack --- cura/Settings/CuraContainerRegistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 6d8ed7c037..f5036078be 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -475,7 +475,7 @@ class CuraContainerRegistry(ContainerRegistry): extruder_definition = extruder_definitions[0] unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id - extruder_stack = ExtruderStack.ExtruderStack(unique_name, parent = machine) + extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) From dfd9fc2ae99857871012cd7fd87d3483345326f5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 4 Jun 2018 14:43:00 +0200 Subject: [PATCH 088/109] Revert "CURA-5404 Remove retraction_amount values that must be in the material" This reverts commit a969d3c5eab54fc904b37611029a704c24c4a120. --- resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg | 1 + resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg | 1 + resources/variants/ultimaker3_aa0.8.inst.cfg | 1 + resources/variants/ultimaker3_aa04.inst.cfg | 1 + resources/variants/ultimaker3_bb0.8.inst.cfg | 1 + resources/variants/ultimaker3_bb04.inst.cfg | 1 + resources/variants/ultimaker3_extended_aa0.8.inst.cfg | 1 + resources/variants/ultimaker3_extended_aa04.inst.cfg | 1 + resources/variants/ultimaker3_extended_bb0.8.inst.cfg | 1 + resources/variants/ultimaker3_extended_bb04.inst.cfg | 1 + resources/variants/ultimaker_s5_aa0.8.inst.cfg | 1 + resources/variants/ultimaker_s5_aa04.inst.cfg | 1 + resources/variants/ultimaker_s5_bb0.8.inst.cfg | 1 + resources/variants/ultimaker_s5_bb04.inst.cfg | 1 + 14 files changed, 14 insertions(+) diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg index ff64790c96..4ea3552891 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg @@ -40,6 +40,7 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_size = 16 prime_tower_wipe_enabled = True +retraction_amount = 6.5 retraction_count_max = 12 retraction_extra_prime_amount = 0.8 retraction_extrusion_window = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index 316161db32..f0a889c12e 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -39,6 +39,7 @@ material_print_temperature_layer_0 = =default_material_print_temperature + 2 material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_wipe_enabled = True +retraction_amount = 7 retraction_count_max = 12 retraction_extra_prime_amount = 0.8 retraction_extrusion_window = 1 diff --git a/resources/variants/ultimaker3_aa0.8.inst.cfg b/resources/variants/ultimaker3_aa0.8.inst.cfg index ee0aad9819..025c346377 100644 --- a/resources/variants/ultimaker3_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_aa0.8.inst.cfg @@ -41,6 +41,7 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = True +retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker3_aa04.inst.cfg b/resources/variants/ultimaker3_aa04.inst.cfg index 0cc652d29e..84b137df60 100644 --- a/resources/variants/ultimaker3_aa04.inst.cfg +++ b/resources/variants/ultimaker3_aa04.inst.cfg @@ -22,6 +22,7 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 +retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index 5e19f2b259..32f9dc6fd2 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -54,6 +54,7 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 +retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker3_bb04.inst.cfg b/resources/variants/ultimaker3_bb04.inst.cfg index 6fc83310f9..8c94ce1e19 100644 --- a/resources/variants/ultimaker3_bb04.inst.cfg +++ b/resources/variants/ultimaker3_bb04.inst.cfg @@ -27,6 +27,7 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 +retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width diff --git a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg index 16c857b2d5..ea2e92478a 100644 --- a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg @@ -41,6 +41,7 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = True +retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker3_extended_aa04.inst.cfg b/resources/variants/ultimaker3_extended_aa04.inst.cfg index 215c08337d..e653616c89 100644 --- a/resources/variants/ultimaker3_extended_aa04.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa04.inst.cfg @@ -22,6 +22,7 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 +retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg index 995d2a8d2e..da47f6eb28 100644 --- a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -54,6 +54,7 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 +retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker3_extended_bb04.inst.cfg b/resources/variants/ultimaker3_extended_bb04.inst.cfg index f9c31e9ef6..04e625f87e 100644 --- a/resources/variants/ultimaker3_extended_bb04.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb04.inst.cfg @@ -27,6 +27,7 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 +retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width diff --git a/resources/variants/ultimaker_s5_aa0.8.inst.cfg b/resources/variants/ultimaker_s5_aa0.8.inst.cfg index d88cb87e1d..71b1f2f003 100644 --- a/resources/variants/ultimaker_s5_aa0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.8.inst.cfg @@ -41,6 +41,7 @@ multiple_mesh_overlap = 0 prime_tower_enable = False prime_tower_wipe_enabled = True retract_at_layer_change = =not magic_spiralize +retraction_amount = 6.5 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_hop = 2 diff --git a/resources/variants/ultimaker_s5_aa04.inst.cfg b/resources/variants/ultimaker_s5_aa04.inst.cfg index 616b433658..9c3b5934ee 100644 --- a/resources/variants/ultimaker_s5_aa04.inst.cfg +++ b/resources/variants/ultimaker_s5_aa04.inst.cfg @@ -22,6 +22,7 @@ raft_interface_thickness = =layer_height * 1.5 raft_jerk = =jerk_print raft_margin = 15 raft_surface_layers = 2 +retraction_amount = 6.5 retraction_count_max = 25 retraction_min_travel = =line_width * 2 retraction_prime_speed = =retraction_speed diff --git a/resources/variants/ultimaker_s5_bb0.8.inst.cfg b/resources/variants/ultimaker_s5_bb0.8.inst.cfg index e1d08591d9..0c77d42e7e 100644 --- a/resources/variants/ultimaker_s5_bb0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_bb0.8.inst.cfg @@ -54,6 +54,7 @@ raft_interface_thickness = 0.2 raft_margin = 10 raft_speed = 25 raft_surface_layers = 1 +retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 diff --git a/resources/variants/ultimaker_s5_bb04.inst.cfg b/resources/variants/ultimaker_s5_bb04.inst.cfg index f769cae63e..76b609249a 100644 --- a/resources/variants/ultimaker_s5_bb04.inst.cfg +++ b/resources/variants/ultimaker_s5_bb04.inst.cfg @@ -27,6 +27,7 @@ prime_tower_wall_thickness = 1.5 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 +retraction_amount = 4.5 retraction_count_max = 20 retraction_extrusion_window = =retraction_amount retraction_min_travel = =3 * line_width From d80e66af029be148b0f75e1bc6f98882ac814441 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 4 Jun 2018 15:18:31 +0200 Subject: [PATCH 089/109] Fix 21to22 upgrade CURA-5440 --- .../VersionUpgrade21to22/MachineInstance.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 4a75b23c2f..37b6989add 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -107,7 +107,12 @@ class MachineInstance: user_profile["values"] = {} version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance() - user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(version_upgrade_manager.getStoragePaths("user")))) + user_version_to_paths_dict = version_upgrade_manager.getStoragePaths("user") + paths_set = set() + for paths in user_version_to_paths_dict.values(): + paths_set |= paths + + user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(paths_set))) user_profile_file = os.path.join(user_storage, urllib.parse.quote_plus(self._name) + "_current_settings.inst.cfg") if not os.path.exists(user_storage): os.makedirs(user_storage) @@ -135,4 +140,4 @@ class MachineInstance: output = io.StringIO() config.write(output) - return [self._filename], [output.getvalue()] \ No newline at end of file + return [self._filename], [output.getvalue()] From cec888150670147d238f5c6cd94e499b25d1ca33 Mon Sep 17 00:00:00 2001 From: Andreea Scorojitu Date: Mon, 4 Jun 2018 15:21:36 +0200 Subject: [PATCH 090/109] Update_changelog_3.4_Cura-5433 Updated the 3.4 change log to match the release notes --- plugins/ChangeLogPlugin/ChangeLog.txt | 129 +++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 9cb9a60e79..bdbbaf20d0 100755 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -1,3 +1,130 @@ + + +[3.4.0] + +*Toolbox +The plugin browser has been remodeled into the Toolbox. Navigation now involves graphical elements such as tiles, which can be clicked for further details. + +*Upgradable bundled resources +It is now possible to have multiple versions of bundled resources installed: the bundled version and the downloaded upgrade. If an upgrade in the form of a package is present, the bundled version will not be loaded. If it's not present, Ultimaker Cura will revert to the bundled version. + +*Package manager recognizes bundled resources +Bundled packages are now made visible to the CuraPackageMangager. This means the resources are included by default, as well as the "wrapping" of a package, (e.g. package.json) so that the CuraPackageManger and Toolbox recognize them as being installed. + +*Retraction combing max distance +New setting for maximum combing travel distance. Combing travel moves longer than this value will use retraction. Contributed by smartavionics. + +*Infill support +When enabled, infill will be generated only where it is needed using a specialized support generation algorithm for the internal support structures of a part. Contributed by BagelOrb. + +*Print outside perimeter before holes +This prioritizes outside perimeters before printing holes. By printing holes as late as possible, there is a reduced risk of travel moves dislodging them from the build plate. This setting should only have an effect if printing outer before inner walls. Contributed by smartavionics. + +*Disable omitting retractions in support +Previous versions had no option to disable omitting retraction moves when printing supports, which could cause issues with third-party machines or materials. An option has been added to disable this. Contributed by BagelOrb. + +*Support wall line count +Added setting to configure how many walls to print around supports. Contributed by BagelOrb. + +*Maximum combing resolution +Combing travel moves are kept at least 1.5 mm long to prevent buffer underruns. + +*Avoid supports when traveling +Added setting to avoid supports when performing travel moves. This minimizes the risk of the print head hitting support material. + +*Rewrite cross infill +Experimental setting that allows you to input a path to an image to manipulate the cross infill density. This will overlay that image on your model. Contributed by BagelOrb. + +*Backup and restore +Added functionality to backup and restore settings and profiles to cloud using the Cura Backups plugin. + +*Cryptography and update packages +Cloud login data is encrypted, so that someone who has access to a users computer can't steal login data. + +*Auto-select model after import +User can now set preferences for the behavior of selecting a newly imported model or not. + +*Settings filter timeout +The settings filter is triggered on enter or after a 500ms timeout when typing a setting to filter. + +*Event measurements +Added time measurement to logs for occurrences, including startup time, file load time, number of items on the build plate when slicing, slicing time, and time and performance when moving items on the build plate, for benchmarking purposes. + +*Send anonymous data +Disable button on the ‘Send anonymous data’ popup has changed to a ‘more info’ button, with further options to enable/disable anonymous data messages. + +*Configuration error assistant +Detect and show potential configuration file errors to users, e.g. incorrect files and duplicate files in material or quality profiles, there are several places to check. Information is stored and communicated to the user to prevent crashing in future. + +*Disable ensure models are kept apart +Disable "Ensure models are kept apart" by default due to to a change in preference files. + +*Prepare and monitor QML files +Created two separate QML files for the Prepare and Monitor stages. + +*Hide bed temperature +Option to hide bed temperature when no heated bed is present. Contributed by ngraziano. + +*Reprap/Marlin GCODE flavor +RepRap firmware now lists values for all extruders in the "Filament used" GCODE comment. Contributed by smartavionics. + +*AutoDesk Inventor integration +Open AutoDesk inventor files (parts, assemblies, drawings) directly into Ultimaker Cura. Contributed by thopiekar. + +*Blender integration +Open Blender files directly into Ultimaker Cura. Contributed by thopiekar. + +*OpenSCAD integration +Open OpenSCAD files directly into Ultimaker Cura. Contributed by thopiekar. + +*FreeCAD integration +Open FreeCAD files directly into Ultimaker Cura. Contributed by thopiekar. + +*OctoPrint plugin +New version of the OctoPrint plugin for Ultimaker Cura. Contributed by fieldOfView. + +*Cura Backups +Backup and restore your configuration, including settings, materials and plugins, for use across different systems. + +*MakePrintable +New version of the MakePrintable plugin. + +*Compact Prepare sidebar +Plugin that replaces the sidebar with a more compact variation of the original sidebar. Nozzle and material dropdowns are combined into a single line, the “Check compatibility” link is removed, extruder selection buttons are downsized, recommended and custom mode selection buttons are moved to a combobox at the top, and margins are tweaked. Contributed by fieldOfView. + +*PauseAtHeight plugin +Bug fixes and improvements for PauseAtHeight plugin. Plugin now accounts for raft layers when choosing “Pause of layer no.” Now positions the nozzle at x and y values of the next layer when resuming. Contributed by JPFrancoia. + +*Bug fixes +- Prime tower purge fix. Prime tower purge now starts away from the center, minimizing the chance of overextrusion and nozzle obstructions. Contributed by BagelOrb. +- Extruder 2 temp via USB. Fixed a bug where temperatures can’t be read for a second extruder via USB. Contributed by kirilledelman. +- Move to next object position before bed heat. Print one at a time mode caused waiting for the bed temperature to reach the first layer temperature while the nozzle was still positioned on the top of the last part. This has been fixed so that the nozzle moves to the location of the next part before waiting for heat up. Contributed by smartavionics. +- Non-GCODE USB. Fixed a bug where the USB port doesn’t open if printer doesn't support GCODE. Contributed by ohrn. +- Improved wall overlap compensation. Minimizes unexpected behavior on overlap lines, providing smoother results. Contributed by BagelOrb. +- Configuration/sync. Fixes minor issues with the configuration/sync menu, such as text rendering on some OSX systems and untranslatable text. Contributed by fieldOfView. +- Print job name reslice. Fixed behavior where print job name changes back to origin when reslicing. +- Discard/keep. Customized settings don't give an 'discard or keep' dialog when changing material. +- Message box styling. Fixed bugs related to message box styling, such as the progress bar overlapping the button in the ‘Sending Data’ popup. +- Curaproject naming. Fixed bug related to two "curaprojects" in the file name when saving a project. +- No support on first layers. Fixed a bug related to no support generated causing failed prints when model is floating above build plate. +- False incompatible configuration. Fixed a bug where PrintCore and materials were flagged even though the configurations are compatible. +- Spiralize contour overlaps. Fixed a bug related to spiralize contour overlaps. +- Model saved outside build volume. Fixed a bug that would saved a model to file (GCODE) outside the build volume. +- Filament diameter line width. Adjust filament diameter to calculate line width in the GCODE parser. +- Holes in model surfaces. Fixed a bug where illogical travel moves leave holes in the model surface. +- Nozzle legacy file variant. Fixed crashes caused by loading legacy nozzle variant files. +- Brim wall order. Fixed a bug related to brim wall order. Contributed by smartavionics. +- GCODE reader gaps. Fixed a GCODE reader bug that can create a gap at the start of a spiralized layer. +- Korean translation. Fixed some typos in Korean translation. +- ARM/Mali systems. Graphics pipeline for ARM/Mali fixed. Contributed by jwalt. +- NGC Writer. Fixed missing author for NGC Writer plugin. +- Support blocker legacy GPU. Fixes depth picking on older GPUs that do not support the 4.1 shading model which caused the support blocker to put cubes in unexpected locations. Contributed by fieldOfView. + +*Third-party printers +- Felix Tec4 printer. Updated definitions for Felix Tec4. Contributed by kerog777. +- Deltacomb. Updated definitions for Deltacomb. Contributed by kaleidoscopeit. +- Rigid3D Mucit. Added definitions for Rigid3D Mucit. Contributed by Rigid3D. + [3.3.0] *Profile for the Ultimaker S5 @@ -66,7 +193,7 @@ Generate a cube mesh to prevent support material generation in specific areas of *Real bridging - smartavionics New experimental feature that detects bridges, adjusting the print speed, slow and fan speed to enhance print quality on bridging parts. -*Updated CuraEngine executable - thopiekar & Ultimaker B.V. ❤️ +*Updated CuraEngine executable - thopiekar & Ultimaker B.V. The CuraEngine executable contains a dedicated icon, author and license info on Windows now. The icon has been designed by Ultimaker B.V. *Use RapidJSON and ClipperLib from system libraries From b45311fb34fd3282d38d0e5b5123350e275d47a6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 4 Jun 2018 15:39:46 +0200 Subject: [PATCH 091/109] Update 3.4 changelog CURA-5433 --- plugins/ChangeLogPlugin/ChangeLog.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index bdbbaf20d0..8da415df05 100755 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -38,9 +38,6 @@ Experimental setting that allows you to input a path to an image to manipulate t *Backup and restore Added functionality to backup and restore settings and profiles to cloud using the Cura Backups plugin. -*Cryptography and update packages -Cloud login data is encrypted, so that someone who has access to a users computer can't steal login data. - *Auto-select model after import User can now set preferences for the behavior of selecting a newly imported model or not. From bd145c451bf083cec19cd989409fcdac4e5b89d8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jun 2018 16:49:49 +0200 Subject: [PATCH 092/109] Brackets on new line As far as I've seen them when I scrolled through this. Contributes to issue CURA-5324. --- resources/qml/SidebarSimple.qml | 99 ++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 31 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index cbf2ac227a..26fbde7f18 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -57,7 +57,8 @@ Item interval: 50 running: false repeat: false - onTriggered: { + onTriggered: + { var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); Cura.MachineManager.activeQualityGroup = item.quality_group; } @@ -77,7 +78,8 @@ Item { // update needs to be called when the widgets are visible, otherwise the step width calculation // will fail because the width of an invisible item is 0. - if (visible) { + if (visible) + { qualityModel.update(); } } @@ -97,24 +99,30 @@ Item property var qualitySliderAvailableMax: 0 property var qualitySliderMarginRight: 0 - function update () { + function update () + { reset() var availableMin = -1 var availableMax = -1 - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) { + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) + { var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) // Add each quality item to the UI quality model qualityModel.append(qualityItem) // Set selected value - if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) { + if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) + { // set to -1 when switching to user created profile so all ticks are clickable - if (Cura.SimpleModeSettingsManager.isProfileUserCreated) { + if (Cura.SimpleModeSettingsManager.isProfileUserCreated) + { qualityModel.qualitySliderActiveIndex = -1 - } else { + } + else + { qualityModel.qualitySliderActiveIndex = i } @@ -122,18 +130,21 @@ Item } // Set min available - if (qualityItem.available && availableMin == -1) { + if (qualityItem.available && availableMin == -1) + { availableMin = i } // Set max available - if (qualityItem.available) { + if (qualityItem.available) + { availableMax = i } } // Set total available ticks for active slider part - if (availableMin != -1) { + if (availableMin != -1) + { qualityModel.availableTotalTicks = availableMax - availableMin + 1 } @@ -145,16 +156,23 @@ Item qualityModel.qualitySliderAvailableMax = availableMax } - function calculateSliderStepWidth (totalTicks) { + function calculateSliderStepWidth (totalTicks) + { qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((base.width * 0.55) / (totalTicks)) : 0 } - function calculateSliderMargins (availableMin, availableMax, totalTicks) { - if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) { + function calculateSliderMargins (availableMin, availableMax, totalTicks) + { + if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) + { qualityModel.qualitySliderMarginRight = Math.round(base.width * 0.55) - } else if (availableMin == availableMax) { + } + else if (availableMin == availableMax) + { qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) - } else { + } + else + { qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) } } @@ -215,16 +233,24 @@ Item return result } - x: { + x: + { // Make sure the text aligns correctly with each tick - if (qualityModel.totalTicks == 0) { + if (qualityModel.totalTicks == 0) + { // If there is only one tick, align it centrally return Math.round(((base.width * 0.55) - width) / 2) - } else if (index == 0) { + } + else if (index == 0) + { return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - } else if (index == qualityModel.totalTicks) { + } + else if (index == qualityModel.totalTicks) + { return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - width - } else { + } + else + { return Math.round((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2)) } } @@ -291,7 +317,8 @@ Item Rectangle { id: rightArea - width: { + width: + { if(qualityModel.availableTotalTicks == 0) return 0 @@ -299,8 +326,10 @@ Item } height: parent.height color: "transparent" - x: { - if (qualityModel.availableTotalTicks == 0) { + x: + { + if (qualityModel.availableTotalTicks == 0) + { return 0 } @@ -310,7 +339,8 @@ Item return totalGap } - MouseArea { + MouseArea + { anchors.fill: parent hoverEnabled: true enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false @@ -373,13 +403,16 @@ Item style: SliderStyle { //Draw Available line - groove: Rectangle { + groove: Rectangle + { implicitHeight: 2 * screenScaleFactor color: UM.Theme.getColor("quality_slider_available") radius: Math.round(height / 2) } - handle: Item { - Rectangle { + handle: Item + { + Rectangle + { id: qualityhandleButton anchors.centerIn: parent color: UM.Theme.getColor("quality_slider_available") @@ -391,11 +424,14 @@ Item } } - onValueChanged: { + onValueChanged: + { // only change if an active machine is set and the slider is visible at all. - if (Cura.MachineManager.activeMachine != null && visible) { + if (Cura.MachineManager.activeMachine != null && visible) + { // prevent updating during view initializing. Trigger only if the value changed by user - if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) { + if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) + { // start updating with short delay qualitySliderChangeTimer.start() } @@ -587,7 +623,8 @@ Item // same operation var active_mode = UM.Preferences.getValue("cura/active_mode") - if (active_mode == 0 || active_mode == "simple") { + if (active_mode == 0 || active_mode == "simple") + { Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) } } From 02a3067a0ca9b844eeeddc66740afabe10f2c215 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jun 2018 16:58:13 +0200 Subject: [PATCH 093/109] Reset infill line distance when changing density via recommended mode Otherwise the change has no effect. Contributes to issue CURA-5324. --- cura/Settings/MachineManager.py | 8 ++++++++ resources/qml/SidebarSimple.qml | 1 + 2 files changed, 9 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3ee14ca85b..f174df2e21 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -981,6 +981,14 @@ class MachineManager(QObject): container = extruder.userChanges container.setProperty(setting_name, property_name, property_value) + ## Reset all setting properties of a setting for all extruders. + # \param setting_name The ID of the setting to reset. + @pyqtSlot(str) + def resetSettingForAllExtruders(self, setting_name: str) -> None: + for key, extruder in self._global_container_stack.extruders.items(): + container = extruder.userChanges + container.removeInstance(setting_name) + @pyqtProperty("QVariantList", notify = globalContainerChanged) def currentExtruderPositions(self) -> List[str]: if self._global_container_stack is None: diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 26fbde7f18..4b229d9807 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -626,6 +626,7 @@ Item if (active_mode == 0 || active_mode == "simple") { Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) + Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") } } From 71f41b8ada7fd0cdaebcc0c6fdf54f1bdacd622d Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 4 Jun 2018 17:00:59 +0200 Subject: [PATCH 094/109] CURA-5442 Final fixes --- .../qml/ToolboxDetailTileActions.qml | 3 +- plugins/Toolbox/src/Toolbox.py | 29 +- resources/bundled_packages.json | 272 ++++++++++++++++++ 3 files changed, 302 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index f82fb049d8..cd1e4cdbda 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -34,6 +34,7 @@ Column // Don't allow installing while another download is running enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 + visible: !updateButton.visible // Don't show when the update button is visible } ToolboxProgressButton @@ -55,7 +56,7 @@ Column // Don't allow installing while another download is running enabled: !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 - visible: installed && canUpdate + visible: canUpdate } Connections { diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 9e3adcc789..b9264b89df 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -238,6 +238,23 @@ class Toolbox(QObject, Extension): dialog = Application.getInstance().createQmlComponent(path, {"toolbox": self}) return dialog + + def _convertPluginMetadata(self, plugin: dict) -> dict: + formatted = { + "package_id": plugin["id"], + "package_type": "plugin", + "display_name": plugin["plugin"]["name"], + "package_version": plugin["plugin"]["version"], + "sdk_version": plugin["plugin"]["api"], + "author": { + "author_id": plugin["plugin"]["author"], + "display_name": plugin["plugin"]["author"] + }, + "is_installed": True, + "description": plugin["plugin"]["description"] + } + return formatted + @pyqtSlot() def _updateInstalledModels(self) -> None: @@ -245,19 +262,28 @@ class Toolbox(QObject, Extension): # list of old plugins old_plugin_ids = self._plugin_registry.getInstalledPlugins() installed_package_ids = self._package_manager.getAllInstalledPackageIDs() + self._old_plugin_ids = [] + self._old_plugin_metadata = [] + for plugin_id in old_plugin_ids: if plugin_id not in installed_package_ids: Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) + + old_metadata = self._plugin_registry.getMetaData(plugin_id) + new_metadata = self._convertPluginMetadata(old_metadata) + self._old_plugin_ids.append(plugin_id) + self._old_plugin_metadata.append(new_metadata) all_packages = self._package_manager.getAllInstalledPackagesInfo() if "plugin" in all_packages: - self._metadata["plugins_installed"] = all_packages["plugin"] + self._metadata["plugins_installed"] = all_packages["plugin"] + self._old_plugin_metadata self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"]) self.metadataChanged.emit() if "material" in all_packages: self._metadata["materials_installed"] = all_packages["material"] + # TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE self._models["materials_installed"].setMetadata(self._metadata["materials_installed"]) self.metadataChanged.emit() @@ -383,6 +409,7 @@ class Toolbox(QObject, Extension): return False # Check for plugins that were installed with the old plugin browser + @pyqtSlot(str, result = bool) def isOldPlugin(self, plugin_id: str) -> bool: if plugin_id in self._old_plugin_ids: return True diff --git a/resources/bundled_packages.json b/resources/bundled_packages.json index ab6bf7e925..7f3ba2a92e 100644 --- a/resources/bundled_packages.json +++ b/resources/bundled_packages.json @@ -1132,5 +1132,277 @@ "website": "https://www.vellemanprojects.eu" } } + }, + "ConsoleLogger": { + "package_info": { + "package_id": "ConsoleLogger", + "package_type": "plugin", + "display_name": "Console Logger", + "description": "Outputs log information to the console.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "OBJReader": { + "package_info": { + "package_id": "OBJReader", + "package_type": "plugin", + "display_name": "Wavefront OBJ Reader", + "description": "Makes it possible to read Wavefront OBJ files.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "OBJWriter": { + "package_info": { + "package_id": "OBJWriter", + "package_type": "plugin", + "display_name": "Wavefront OBJ Writer", + "description": "Makes it possible to write Wavefront OBJ files.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "STLReader": { + "package_info": { + "package_id": "STLReader", + "package_type": "plugin", + "display_name": "STL Reader", + "description": "Provides support for reading STL files.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "STLWriter": { + "package_info": { + "package_id": "STLWriter", + "package_type": "plugin", + "display_name": "STL Writer", + "description": "Provides support for writing STL files.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "FileLogger": { + "package_info": { + "package_id": "FileLogger", + "package_type": "plugin", + "display_name": "File Logger", + "description": "Outputs log information to a file in your settings folder.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "LocalContainerProvider": { + "package_info": { + "package_id": "LocalContainerProvider", + "package_type": "plugin", + "display_name": "Local Container Provider", + "description": "Provides built-in setting containers that come with the installation of the application.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "LocalFileOutputDevice": { + "package_info": { + "package_id": "LocalFileOutputDevice", + "package_type": "plugin", + "display_name": "Local File Output Device", + "description": "Enables saving to local files.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "CameraTool": { + "package_info": { + "package_id": "CameraTool", + "package_type": "plugin", + "display_name": "Camera Tool", + "description": "Provides the tool to manipulate the camera.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "MirrorTool": { + "package_info": { + "package_id": "MirrorTool", + "package_type": "plugin", + "display_name": "Mirror Tool", + "description": "Provides the Mirror tool.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "RotateTool": { + "package_info": { + "package_id": "RotateTool", + "package_type": "plugin", + "display_name": "Rotate Tool", + "description": "Provides the Rotate tool.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "ScaleTool": { + "package_info": { + "package_id": "ScaleTool", + "package_type": "plugin", + "display_name": "Scale Tool", + "description": "Provides the Scale tool.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "SelectionTool": { + "package_info": { + "package_id": "SelectionTool", + "package_type": "plugin", + "display_name": "Selection Tool", + "description": "Provides the Selection tool.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "TranslateTool": { + "package_info": { + "package_id": "TranslateTool", + "package_type": "plugin", + "display_name": "Move Tool", + "description": "Provides the Move tool.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "UpdateChecker": { + "package_info": { + "package_id": "UpdateChecker", + "package_type": "plugin", + "display_name": "Update Checker", + "description": "Checks for updates of the software.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, + "SimpleView": { + "package_info": { + "package_id": "SimpleView", + "package_type": "plugin", + "display_name": "Simple View", + "description": "Provides a simple solid mesh view.", + "package_version": "1.0.0", + "sdk_version": 4, + "website": "https://ultimaker.com", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } } } From 98aaaba594a56e68f645288ad53aff1892ccb788 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 5 Jun 2018 08:33:57 +0200 Subject: [PATCH 095/109] Only update job_name to unnamed but not base_name CURA-5367 Only update job_name to "unnamed" if it's empty. Do not change base_name. --- cura/PrintInformation.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index d17720228f..1b4bf65beb 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -299,7 +299,7 @@ class PrintInformation(QObject): def _updateJobName(self): if self._base_name == "": - self._job_name = "" + self._job_name = "unnamed" self._is_user_specified_job_name = False self.jobNameChanged.emit() return @@ -351,18 +351,17 @@ class PrintInformation(QObject): if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != check_name)): # Only take the file name part, Note : file name might have 'dot' in name as well - data = '' + data = "" try: mime_type = MimeTypeDatabase.getMimeTypeForFile(name) data = mime_type.stripExtension(name) except: - Logger.log("w", "Unsupported Mime Type Database file extension") - data = 'unnamed' + Logger.log("w", "Unsupported Mime Type Database file extension %s", name) if data is not None and check_name is not None: self._base_name = data else: - self._base_name = 'unnamed' + self._base_name = "" self._updateJobName() From 39b71add073fbb23508adeed489b16b54fb7cac2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 5 Jun 2018 08:35:23 +0200 Subject: [PATCH 096/109] Reader classes are responsible for adding their mime types CURA-5367 --- plugins/3MFReader/ThreeMFReader.py | 12 ++++++++++++ plugins/3MFReader/ThreeMFWorkspaceReader.py | 12 ++++++++++-- plugins/3MFReader/__init__.py | 16 +--------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 6c2fb9a59d..0d1026181e 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -15,6 +15,7 @@ from UM.Math.Vector import Vector from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshReader import MeshReader from UM.Scene.GroupDecorator import GroupDecorator +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from cura.Settings.ExtruderManager import ExtruderManager from cura.Scene.CuraSceneNode import CuraSceneNode @@ -25,6 +26,7 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch MYPY = False + try: if not MYPY: import xml.etree.cElementTree as ET @@ -32,10 +34,20 @@ except ImportError: Logger.log("w", "Unable to load cElementTree, switching to slower version") import xml.etree.ElementTree as ET + ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): super().__init__() + + MimeTypeDatabase.addMimeType( + MimeType( + name = "application/vnd.ms-package.3dmanufacturing-3dmodel+xml", + comment="3MF", + suffixes=["3mf"] + ) + ) + self._supported_extensions = [".3mf"] self._root = None self._base_name = "" diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 5cd0ef5ced..76afb6b47e 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -4,7 +4,6 @@ from configparser import ConfigParser import zipfile import os -import threading from typing import List, Tuple @@ -21,7 +20,7 @@ from UM.Settings.ContainerStack import ContainerStack from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.MimeTypeDatabase import MimeTypeDatabase +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.Job import Job from UM.Preferences import Preferences @@ -84,6 +83,15 @@ class ExtruderInfo: class ThreeMFWorkspaceReader(WorkspaceReader): def __init__(self): super().__init__() + + MimeTypeDatabase.addMimeType( + MimeType( + name="application/x-cura-project-file", + comment="Cura Project File", + suffixes=["curaproject.3mf"] + ) + ) + self._supported_extensions = [".3mf"] self._dialog = WorkspaceDialog() self._3mf_mesh_reader = None diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 304cd8ef65..25f2505ab0 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -13,23 +13,9 @@ from . import ThreeMFWorkspaceReader from UM.i18n import i18nCatalog from UM.Platform import Platform -from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType + catalog = i18nCatalog("cura") -MimeTypeDatabase.addMimeType( - MimeType( - name = "application/x-cura-project-file", - comment = "Cura Project File", - suffixes = ["curaproject.3mf"] - ) -) -MimeTypeDatabase.addMimeType( - MimeType( - name = "application/x-cura-project-file", - comment = "Cura Project File", - suffixes = ["3mf"] - ) -) def getMetaData() -> Dict: # Workarround for osx not supporting double file extensions correctly. From bbd019f9e5917c44dcb3edc032a5a87ce705c827 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 5 Jun 2018 08:58:07 +0200 Subject: [PATCH 097/109] Fix job name UI binding CURA-5367 UI should always refer to PrintInformation for the job name. --- cura/CuraApplication.py | 1 - resources/qml/JobSpecs.qml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e416196775..05070e4432 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -302,7 +302,6 @@ class CuraApplication(QtApplication): self._platform_activity = False self._scene_bounding_box = AxisAlignedBox.Null - self._job_name = None self._center_after_select = False self._camera_animation = None self._cura_actions = None diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 6cd9999db7..3b10cf59d5 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -81,8 +81,8 @@ Item { text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight onEditingFinished: { - text = text == "" ? "unnamed" : text; - PrintInformation.setJobName(printJobTextfield.text, true); + var new_name = text == "" ? "unnamed" : text; + PrintInformation.setJobName(new_name, true); printJobTextfield.focus = false; } validator: RegExpValidator { From 70d78f3cab50daf4b6421bf591b2396afa41deb0 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 5 Jun 2018 10:36:34 +0200 Subject: [PATCH 098/109] CURA-5119 move settings out of wall overlap compensation --- resources/definitions/fdmprinter.def.json | 46 +++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c6642e8d94..8f3bfede03 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1274,32 +1274,32 @@ "value": "travel_compensate_overlapping_walls_enabled", "limit_to_extruder": "wall_x_extruder_nr", "settable_per_mesh": true - }, - "wall_min_flow": - { - "label": "Minimum Wall Flow", - "description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting, you must enable the wall overlap compensation and print the outer wall before inner walls.", - "unit": "%", - "minimum_value": "0", - "maximum_value": "100", - "default_value": 0, - "type": "float", - "enabled": "travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "wall_min_flow_retract": - { - "label": "Prefer Retract", - "description": "If enabled, retraction is used rather than combing for travel moves that replace walls whose flow is below the minimum flow threshold.", - "type": "bool", - "default_value": false, - "enabled": "(travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled) and wall_min_flow > 0", - "settable_per_mesh": false, - "settable_per_extruder": true } } }, + "wall_min_flow": + { + "label": "Minimum Wall Flow", + "description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting, you must enable the wall overlap compensation and print the outer wall before inner walls.", + "unit": "%", + "minimum_value": "0", + "maximum_value": "100", + "default_value": 0, + "type": "float", + "enabled": "travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "wall_min_flow_retract": + { + "label": "Prefer Retract", + "description": "If enabled, retraction is used rather than combing for travel moves that replace walls whose flow is below the minimum flow threshold.", + "type": "bool", + "default_value": false, + "enabled": "(travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled) and wall_min_flow > 0", + "settable_per_mesh": false, + "settable_per_extruder": true + }, "fill_perimeter_gaps": { "label": "Fill Gaps Between Walls", From 1285e1b5e5a3f44a4753ed33f1488f18fa48fa47 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 5 Jun 2018 11:00:05 +0200 Subject: [PATCH 099/109] CURA-5357 Move the plugin info in packages.json only when the installation succeeded. --- cura/CuraPackageManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 096bfc9065..09253a4c77 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -105,7 +105,6 @@ class CuraPackageManager(QObject): while self._to_install_package_dict: package_id, package_info = list(self._to_install_package_dict.items())[0] self._installPackage(package_info) - self._installed_package_dict[package_id] = self._to_install_package_dict[package_id] del self._to_install_package_dict[package_id] self._saveManagementData() @@ -328,6 +327,8 @@ class CuraPackageManager(QObject): # Remove the file os.remove(filename) + # Move the info to the installed list of packages only when it succeeds + self._installed_package_dict[package_id] = self._to_install_package_dict[package_id] def __installPackageFiles(self, package_id: str, src_dir: str, dst_dir: str) -> None: Logger.log("i", "Moving package {package_id} from {src_dir} to {dst_dir}".format(package_id=package_id, src_dir=src_dir, dst_dir=dst_dir)) From 59602d8013610f0fe3e6e792fa3db51c092431b4 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 5 Jun 2018 11:56:41 +0200 Subject: [PATCH 100/109] Revert "The prepare button slices, so it should be a slice button" This reverts commit a479176c63fe3c53a380b47d0d1abe3b2c80c4a0. --- resources/qml/SaveButton.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 77537b0af2..0369f492b4 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -100,8 +100,8 @@ Item { if (saveToButton.enabled) { saveToButton.clicked(); } - // slice button - if (sliceButton.enabled) { + // prepare button + if (prepareButton.enabled) { sliceOrStopSlicing(); } } @@ -131,7 +131,7 @@ Item { Row { id: additionalComponentsRow anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (sliceButton.visible ? sliceButton.left : parent.right) + anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -159,14 +159,14 @@ Item { onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice"); - sliceButton.autoSlice = autoSlice; + prepareButton.autoSlice = autoSlice; saveToButton.autoSlice = autoSlice; } } - // Slice button, only shows if auto_slice is off + // Prepare button, only shows if auto_slice is off Button { - id: sliceButton + id: prepareButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing @@ -180,7 +180,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Slice") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); From c5929ce26c73a46b17bae5f4e1a944432e56e18b Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 5 Jun 2018 12:00:21 +0200 Subject: [PATCH 101/109] Revert "The prepare button slices, so it should be a slice button" This reverts commit a0263676e1305c2cc4b778ed5cfb7215e938705a. --- resources/qml/SaveButton.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 77537b0af2..0369f492b4 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -100,8 +100,8 @@ Item { if (saveToButton.enabled) { saveToButton.clicked(); } - // slice button - if (sliceButton.enabled) { + // prepare button + if (prepareButton.enabled) { sliceOrStopSlicing(); } } @@ -131,7 +131,7 @@ Item { Row { id: additionalComponentsRow anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (sliceButton.visible ? sliceButton.left : parent.right) + anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -159,14 +159,14 @@ Item { onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice"); - sliceButton.autoSlice = autoSlice; + prepareButton.autoSlice = autoSlice; saveToButton.autoSlice = autoSlice; } } - // Slice button, only shows if auto_slice is off + // Prepare button, only shows if auto_slice is off Button { - id: sliceButton + id: prepareButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing @@ -180,7 +180,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Slice") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); From 4652c3be0f32927e6a7755f42cc8edd63ab8d389 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 5 Jun 2018 12:46:32 +0200 Subject: [PATCH 102/109] Always try to remove old package files before installing new CURA-5442 --- cura/CuraPackageManager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 3d4a360c7e..1ec9e2d993 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -306,9 +306,8 @@ class CuraPackageManager(QObject): Logger.log("i", "Installing package [%s] from file [%s]", package_id, filename) - # If it's installed, remove it first and then install - if package_id in self._installed_package_dict: - self._purgePackage(package_id) + # remove it first and then install + self._purgePackage(package_id) # Install the package with zipfile.ZipFile(filename, "r") as archive: From 753101a9880459829869ed47a35d0f2417908186 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 5 Jun 2018 15:43:36 +0200 Subject: [PATCH 103/109] Uncomment the code that hides the material tab in the toolbox. Just for master.<< --- plugins/Toolbox/resources/qml/ToolboxHeader.qml | 2 -- plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml | 2 -- 2 files changed, 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index ee4241beaf..9c9f967d54 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -34,7 +34,6 @@ Item } } - /* Hide for 3.4 ToolboxTabButton { text: catalog.i18nc("@title:tab", "Materials") @@ -47,7 +46,6 @@ Item toolbox.viewPage = "overview" } } - */ } ToolboxTabButton { diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index bb0f6fe346..9d916182f6 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -65,7 +65,6 @@ ScrollView } } } - /* Hidden in 3.4 Label { visible: toolbox.materialsInstalledModel.items.length > 0 @@ -103,6 +102,5 @@ ScrollView } } } - */ } } From e152fd641bee0558275afbce2acba7ae397c9519 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Jun 2018 09:57:44 +0200 Subject: [PATCH 104/109] Remove trailing slash in Windows configuration path It shouldn't matter, but we're getting crash reports from this path so let's try this. --- cura_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura_app.py b/cura_app.py index 8a04b8fe09..d540c8a8c4 100755 --- a/cura_app.py +++ b/cura_app.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import argparse @@ -27,7 +27,7 @@ known_args = vars(parser.parse_known_args()[0]) if not known_args["debug"]: def get_cura_dir_path(): if Platform.isWindows(): - return os.path.expanduser("~/AppData/Roaming/cura/") + return os.path.expanduser("~/AppData/Roaming/cura") elif Platform.isLinux(): return os.path.expanduser("~/.local/share/cura") elif Platform.isOSX(): From dbd286e1cc1ad2404199b8daaaa598e3638f4f41 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 6 Jun 2018 10:47:12 +0200 Subject: [PATCH 105/109] Save as project the extruder counter starts from 1 --- resources/qml/WorkspaceSummaryDialog.qml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 0869d7e698..079d840ae7 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -148,9 +148,22 @@ UM.Dialog { height: childrenRect.height width: parent.width - Label + Label { - text: catalog.i18nc("@action:label", "Extruder %1").arg(modelData) + text: { + var extruder = Number(modelData) + var extruder_id = "" + if(!isNaN(extruder)) + { + extruder_id = extruder + 1 // The extruder counter start from One and not Zero + } + else + { + extruder_id = modelData + } + + return catalog.i18nc("@action:label", "Extruder %1").arg(extruder_id) + } font.bold: true } Row From da4932435c138c18feb8c0b0b572b43459654baa Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Jun 2018 10:56:42 +0200 Subject: [PATCH 106/109] Add fan speed to basic setting visibility It may not be what the users often want to change, but it is something that they may be expecting to see in the custom settings mode by default. It's something that you often think of as a typical setting but in reality you don't often want to change it. Let's get closer to what the user expects. Requested by the support team of Ultimaker. --- resources/setting_visibility/basic.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/setting_visibility/basic.cfg b/resources/setting_visibility/basic.cfg index 4196a3a9e7..82045db93b 100644 --- a/resources/setting_visibility/basic.cfg +++ b/resources/setting_visibility/basic.cfg @@ -34,6 +34,7 @@ retraction_hop_enabled [cooling] cool_fan_enabled +cool_fan_speed [support] support_enable From 17b127e8c13a2ad98f082b0c18b4b739fd5286a4 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Jun 2018 11:00:09 +0200 Subject: [PATCH 107/109] CURA-5249 replaced the extruder value of -1 by the more explicit defaultExtruderPosition, so now Cura is aware of the actual extruder position being used. Fixes linked icon next to support settings, like support_angle. --- cura/CuraApplication.py | 1 + cura/Settings/ExtruderManager.py | 5 +++++ plugins/CuraEngineBackend/StartSliceJob.py | 5 ----- resources/definitions/fdmprinter.def.json | 4 ++-- resources/qml/Settings/SettingExtruder.qml | 9 +-------- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 205968121f..cdca4298ee 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -324,6 +324,7 @@ class CuraApplication(QtApplication): SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues) SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue) SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue) + SettingFunction.registerOperator("defaultExtruderPosition", ExtruderManager.getDefaultExtruderPosition) # Adds all resources and container related resources. def __addAllResourcesAndContainerResources(self) -> None: diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 76f98c5a8a..0f8cb9ae23 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -540,6 +540,11 @@ class ExtruderManager(QObject): return result + ## Return the default extruder position from the machine manager + @staticmethod + def getDefaultExtruderPosition() -> str: + return Application.getInstance().getMachineManager().defaultExtruderPosition + ## Get all extruder values for a certain setting. # # This is exposed to qml for display purposes diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 0297a34385..467351dd4b 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -287,14 +287,9 @@ class StartSliceJob(Job): # \return A dictionary of replacement tokens to the values they should be # replaced with. def _buildReplacementTokens(self, stack) -> dict: - default_extruder_position = int(Application.getInstance().getMachineManager().defaultExtruderPosition) result = {} for key in stack.getAllKeys(): - setting_type = stack.definition.getProperty(key, "type") value = stack.getProperty(key, "value") - if setting_type == "extruder" and value == -1: - # replace with the default value - value = default_extruder_position result[key] = value Job.yieldThread() diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 8f3bfede03..73d842a077 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3644,7 +3644,7 @@ "description": "The extruder train to use for printing the support. This is used in multi-extrusion.", "type": "extruder", "default_value": "0", - "value": "-1", + "value": "defaultExtruderPosition()", "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1", "settable_per_mesh": false, "settable_per_extruder": false, @@ -4385,7 +4385,7 @@ "description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.", "type": "extruder", "default_value": "0", - "value": "-1", + "value": "defaultExtruderPosition()", "enabled": "extruders_enabled_count > 1 and resolveOrValue('adhesion_type') != 'none'", "settable_per_mesh": false, "settable_per_extruder": false diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index 38b1c2cab0..2239628e3f 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -93,14 +93,7 @@ SettingItem { target: control property: "currentIndex" - value: - { - if(propertyProvider.properties.value == -1) - { - return control.getIndexByPosition(Cura.MachineManager.defaultExtruderPosition); - } - return propertyProvider.properties.value - } + value: control.getIndexByPosition(propertyProvider.properties.value) // Sometimes when the value is already changed, the model is still being built. // The when clause ensures that the current index is not updated when this happens. when: control.model.items.length > 0 From 9fd9bd35dc10326de3b9e54105921964c6de06bd Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Jun 2018 13:41:53 +0200 Subject: [PATCH 108/109] CURA-5357 show showcased items in downloads page --- plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml index 69e508cd55..3f9173c69b 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml @@ -19,7 +19,6 @@ ScrollView padding: UM.Theme.getSize("wide_margin").height height: childrenRect.height + 2 * padding - /* Hide for 3.4 ToolboxDownloadsShowcase { id: showcase @@ -31,7 +30,6 @@ ScrollView width: parent.width height: UM.Theme.getSize("default_lining").height } - */ ToolboxDownloadsGrid { From 702d23ad0e0c6144c16686555615205ea661f49a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Jun 2018 13:48:04 +0200 Subject: [PATCH 109/109] CURA-5357 set showcase to max 3 items per row --- plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml index d3ce443704..5a8128b51e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml @@ -19,10 +19,11 @@ Column color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } - Row + Grid { height: childrenRect.height spacing: UM.Theme.getSize("wide_margin").width + columns: 3 anchors { horizontalCenter: parent.horizontalCenter