Browse Source

Merge branch 'master' of github.com:Ultimaker/Cura

Jaime van Kessel 6 years ago
parent
commit
91a66d1290

+ 3 - 4
cura/AutoSave.py

@@ -3,21 +3,20 @@
 
 from PyQt5.QtCore import QTimer
 
-from UM.Preferences import Preferences
 from UM.Logger import Logger
 
 
 class AutoSave:
     def __init__(self, application):
         self._application = application
-        Preferences.getInstance().preferenceChanged.connect(self._triggerTimer)
+        self._application.getPreferences().preferenceChanged.connect(self._triggerTimer)
 
         self._global_stack = None
 
-        Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10)
+        self._application.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(self._application.getPreferences().getValue("cura/autosave_delay"))
         self._change_timer.setSingleShot(True)
 
         self._saving = False

+ 17 - 17
cura/BuildVolume.py

@@ -3,12 +3,10 @@
 
 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
@@ -37,8 +35,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
@@ -82,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()
@@ -107,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 = []
@@ -124,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.
@@ -186,7 +186,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()))
@@ -206,7 +206,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 = []
 
@@ -294,11 +294,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())
@@ -470,7 +470,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()
 
@@ -523,7 +523,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)
@@ -566,7 +566,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()

+ 0 - 1
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

+ 262 - 298
cura/CuraApplication.py

@@ -1,9 +1,18 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from PyQt5.QtCore import QObject, QTimer
-from PyQt5.QtNetwork import QLocalServer
-from PyQt5.QtNetwork import QLocalSocket
+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.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 +83,7 @@ from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 
 from cura.Machines.VariantManager import VariantManager
 
+from .SingleInstance import SingleInstance
 from .AutoSave import AutoSave
 from . import PlatformPhysics
 from . import BuildVolume
@@ -94,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:
@@ -144,20 +142,166 @@ 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()
+
+        # 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
+
+        # Backups
+        self._auto_save = None
+        self._save_data_enabled = 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()
+
+        self.__sendCommandToSingleInstance()
+        self.__addExpectedResourceDirsAndSearchPaths()
+        self.__initializeSettingDefinitionsAndFunctions()
+        self.__addAllResourcesAndContainerResources()
+        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()
+
+    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"):
             resource_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")
             Resources.addSearchPath(resource_path)
 
-        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)
@@ -182,7 +326,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")
@@ -193,20 +338,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")
+
+        Resources.addType(self.ResourceTypes.QmlFiles, "qml")
+        Resources.addType(self.ResourceTypes.Firmware, "firmware")
+
+    # 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
 
-        ##  Initialise the version upgrade manager with Cura's storage paths.
-        #   Needs to be here to prevent circular dependencies.
-        import UM.VersionUpgradeManager
+        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
 
-        UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
+    # 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", InstanceContainer.Version * 1000000 + self.SettingVersion):            (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
                 ("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion):    (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"),
@@ -219,48 +408,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._auto_save = None
-        self._save_data_enabled = True
-
-        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 startSplashWindowPhase(self):
+        super().startSplashWindowPhase()
 
         self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
 
@@ -294,23 +444,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()
@@ -323,56 +456,13 @@ 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()
+        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.
 
@@ -398,7 +488,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
@@ -417,13 +507,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):
@@ -463,14 +550,14 @@ 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")
         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
@@ -509,24 +596,19 @@ 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)
 
     @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())
-
-    @classmethod
-    def getStaticVersion(cls):
-        return CuraVersion
+        self.getPreferences().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
 
     ##  Handle loading of all plugin types (and the backend explicitly)
     #   \sa PluginRegistry
@@ -552,127 +634,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)
@@ -691,29 +654,34 @@ 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
         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.getCommandLineOption("headless", False):
+        if self._is_headless:
             self.runWithoutGUI()
         else:
             self.runWithGUI()
@@ -722,7 +690,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.
@@ -737,8 +704,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)
@@ -747,13 +718,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()
@@ -803,9 +771,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
@@ -820,7 +785,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)
@@ -947,7 +912,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")))
@@ -981,7 +946,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():
@@ -1328,15 +1293,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):
@@ -1487,8 +1452,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()
 
@@ -1607,8 +1571,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()

+ 2 - 2
cura/CuraPackageManager.py

@@ -27,8 +27,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):

+ 12 - 8
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 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.ContainerRegistry import ContainerRegistry
 from UM.Settings.DefinitionContainer import DefinitionContainer
 
-from PyQt5.QtCore import QObject
-from UM.FlameProfiler import pyqtSlot
 
 ##  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

+ 2 - 2
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)

+ 2 - 3
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()):

+ 4 - 5
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", False)
-        Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
+        Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False)
+        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.

+ 14 - 14
cura/PrintInformation.py

@@ -1,25 +1,25 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from typing import Dict
+import json
 import math
-import os.path
+import os
 import unicodedata
-import json
 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.i18n import i18nCatalog
 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.MimeTypeDatabase import MimeTypeDatabase
 
 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
@@ -48,8 +48,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()
 
@@ -60,10 +61,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._is_user_specified_job_name = False
         self._base_name = ""
@@ -73,7 +74,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)
@@ -82,7 +82,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()
@@ -201,7 +201,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
 
@@ -210,7 +210,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():
@@ -311,7 +311,7 @@ class PrintInformation(QObject):
         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"):
+            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
@@ -372,7 +372,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

Some files were not shown because too many files changed in this diff