Browse Source

Merge branch 'master' into CURA-6440_exclude_support_blocks_in_used_extruders

Ghostkeeper 6 years ago
parent
commit
93ef8f6c98

+ 5 - 4
cura/Backups/Backup.py

@@ -116,12 +116,13 @@ class Backup:
 
         current_version = self._application.getVersion()
         version_to_restore = self.meta_data.get("cura_release", "master")
-        if current_version != version_to_restore:
-            # Cannot restore version older or newer than current because settings might have changed.
-            # Restoring this will cause a lot of issues so we don't allow this for now.
+
+        if current_version < version_to_restore:
+            # Cannot restore version newer than current because settings might have changed.
+            Logger.log("d", "Tried to restore a Cura backup of version {version_to_restore} with cura version {current_version}".format(version_to_restore = version_to_restore, current_version = current_version))
             self._showMessage(
                 self.catalog.i18nc("@info:backup_failed",
-                                   "Tried to restore a Cura backup that does not match your current version."))
+                                   "Tried to restore a Cura backup that is higher than the current version."))
             return False
 
         version_data_dir = Resources.getDataStoragePath()

+ 112 - 69
cura/CuraApplication.py

@@ -13,113 +13,117 @@ from PyQt5.QtGui import QColor, QIcon
 from PyQt5.QtWidgets import QMessageBox
 from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
 
+from UM.i18n import i18nCatalog
 from UM.Application import Application
+from UM.Decorators import override
+from UM.FlameProfiler import pyqtSlot
+from UM.Logger import Logger
+from UM.Message import Message
+from UM.Platform import Platform
 from UM.PluginError import PluginNotFoundError
-from UM.Scene.SceneNode import SceneNode
-from UM.Scene.Camera import Camera
-from UM.Math.Vector import Vector
-from UM.Math.Quaternion import Quaternion
+from UM.Resources import Resources
+from UM.Preferences import Preferences
+from UM.Qt.QtApplication import QtApplication  # The class we're inheriting from.
+import UM.Util
+from UM.View.SelectionPass import SelectionPass  # For typing.
+
 from UM.Math.AxisAlignedBox import AxisAlignedBox
 from UM.Math.Matrix import Matrix
-from UM.Platform import Platform
-from UM.Resources import Resources
-from UM.Scene.ToolHandle import ToolHandle
-from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Math.Quaternion import Quaternion
+from UM.Math.Vector import Vector
+
 from UM.Mesh.ReadMeshJob import ReadMeshJob
-from UM.Logger import Logger
-from UM.Preferences import Preferences
-from UM.Qt.QtApplication import QtApplication #The class we're inheriting from.
-from UM.View.SelectionPass import SelectionPass #For typing.
-from UM.Scene.Selection import Selection
+
+from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
+from UM.Operations.GroupedOperation import GroupedOperation
+from UM.Operations.SetTransformOperation import SetTransformOperation
+
+from UM.Scene.Camera import Camera
 from UM.Scene.GroupDecorator import GroupDecorator
+from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Scene.SceneNode import SceneNode
+from UM.Scene.Selection import Selection
+from UM.Scene.ToolHandle import ToolHandle
+
+from UM.Settings.ContainerRegistry import ContainerRegistry
 from UM.Settings.ContainerStack import ContainerStack
 from UM.Settings.InstanceContainer import InstanceContainer
+from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
+from UM.Settings.SettingFunction import SettingFunction
 from UM.Settings.Validator import Validator
-from UM.Message import Message
-from UM.i18n import i18nCatalog
-from UM.Workspace.WorkspaceReader import WorkspaceReader
 
-from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
-from UM.Operations.GroupedOperation import GroupedOperation
-from UM.Operations.SetTransformOperation import SetTransformOperation
+from UM.Workspace.WorkspaceReader import WorkspaceReader
 
 from cura.API import CuraAPI
+
 from cura.Arranging.Arrange import Arrange
 from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
 from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
 from cura.Arranging.ShapeArray import ShapeArray
-from cura.MultiplyObjectsJob import MultiplyObjectsJob
-from cura.GlobalStacksModel import GlobalStacksModel
-from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
+
 from cura.Operations.SetParentOperation import SetParentOperation
-from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
+
 from cura.Scene.BlockSlicingDecorator import BlockSlicingDecorator
 from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
-from cura.Scene.CuraSceneNode import CuraSceneNode
-
+from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
 from cura.Scene.CuraSceneController import CuraSceneController
+from cura.Scene.CuraSceneNode import CuraSceneNode
+from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
+from cura.Scene import ZOffsetDecorator
 
-from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.SettingFunction import SettingFunction
-from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
-from cura.Settings.MachineNameValidator import MachineNameValidator
+from cura.Machines.MachineErrorChecker import MachineErrorChecker
+from cura.Machines.VariantManager import VariantManager
 
 from cura.Machines.Models.BuildPlateModel import BuildPlateModel
-from cura.Machines.Models.NozzleModel import NozzleModel
-from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
 from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel
-from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
+from cura.Machines.Models.DiscoveredPrintersModel import DiscoveredPrintersModel
+from cura.Machines.Models.ExtrudersModel import ExtrudersModel
 from cura.Machines.Models.FavoriteMaterialsModel import FavoriteMaterialsModel
+from cura.Machines.Models.FirstStartMachineActionsModel import FirstStartMachineActionsModel
 from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
+from cura.Machines.Models.GlobalStacksModel import GlobalStacksModel
 from cura.Machines.Models.MaterialBrandsModel import MaterialBrandsModel
+from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
+from cura.Machines.Models.NozzleModel import NozzleModel
 from cura.Machines.Models.QualityManagementModel import QualityManagementModel
+from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
 from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
-from cura.Machines.Models.MachineManagementModel import MachineManagementModel
-
 from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel
+from cura.Machines.Models.UserChangesModel import UserChangesModel
 
-from cura.Machines.MachineErrorChecker import MachineErrorChecker
+from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
+from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage
 
+import cura.Settings.cura_empty_instance_containers
+from cura.Settings.ContainerManager import ContainerManager
+from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
+from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
+from cura.Settings.ExtruderManager import ExtruderManager
+from cura.Settings.MachineManager import MachineManager
+from cura.Settings.MachineNameValidator import MachineNameValidator
+from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
 from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
+from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 
-from cura.Machines.VariantManager import VariantManager
+from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
+
+from cura.UI import CuraSplashScreen, MachineActionManager, PrintInformation
+from cura.UI.MachineSettingsManager import MachineSettingsManager
+from cura.UI.ObjectsModel import ObjectsModel
+from cura.UI.TextManager import TextManager
+from cura.UI.WelcomePagesModel import WelcomePagesModel
 
 from .SingleInstance import SingleInstance
 from .AutoSave import AutoSave
 from . import PlatformPhysics
 from . import BuildVolume
 from . import CameraAnimation
-from . import PrintInformation
 from . import CuraActions
-from cura.Scene import ZOffsetDecorator
-from . import CuraSplashScreen
 from . import PrintJobPreviewImageProvider
-from . import MachineActionManager
-
-from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
-
-from cura.Settings.MachineManager import MachineManager
-from cura.Settings.ExtruderManager import ExtruderManager
-from cura.Settings.UserChangesModel import UserChangesModel
-from cura.Settings.ExtrudersModel import ExtrudersModel
-from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
-from cura.Settings.ContainerManager import ContainerManager
-from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
-import cura.Settings.cura_empty_instance_containers
-from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
-
-from cura.ObjectsModel import ObjectsModel
-
-from cura.PrinterOutputDevice import PrinterOutputDevice
-from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage
 
 from cura import ApplicationMetadata, UltimakerCloudAuthentication
 
-from UM.FlameProfiler import pyqtSlot
-from UM.Decorators import override
-
 if TYPE_CHECKING:
     from cura.Machines.MaterialManager import MaterialManager
     from cura.Machines.QualityManager import QualityManager
@@ -208,6 +212,13 @@ class CuraApplication(QtApplication):
         self._cura_scene_controller = None
         self._machine_error_checker = None
 
+        self._machine_settings_manager = MachineSettingsManager(self)
+
+        self._discovered_printer_model = DiscoveredPrintersModel(self)
+        self._first_start_machine_actions_model = FirstStartMachineActionsModel(self)
+        self._welcome_pages_model = WelcomePagesModel(self)
+        self._text_manager = TextManager(self)
+
         self._quality_profile_drop_down_menu_model = None
         self._custom_quality_profile_drop_down_menu_model = None
         self._cura_API = CuraAPI(self)
@@ -237,8 +248,6 @@ class CuraApplication(QtApplication):
 
         self._update_platform_activity_timer = None
 
-        self._need_to_show_user_agreement = True
-
         self._sidebar_custom_menu_items = []  # type: list # Keeps list of custom menu items for the side bar
 
         self._plugins_loaded = False
@@ -450,7 +459,6 @@ class CuraApplication(QtApplication):
             # Misc.:
             "ConsoleLogger", #You want to be able to read the log if something goes wrong.
             "CuraEngineBackend", #Cura is useless without this one since you can't slice.
-            "UserAgreement", #Our lawyers want every user to see this at least once.
             "FileLogger", #You want to be able to read the log if something goes wrong.
             "XmlMaterialProfile", #Cura crashes without this one.
             "Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back.
@@ -522,7 +530,7 @@ class CuraApplication(QtApplication):
         preferences.addPreference("cura/expanded_brands", "")
         preferences.addPreference("cura/expanded_types", "")
 
-        self._need_to_show_user_agreement = not preferences.getValue("general/accepted_user_agreement")
+        preferences.addPreference("general/accepted_user_agreement", False)
 
         for key in [
             "dialog_load_path",  # dialog_save_path is in LocalFileOutputDevicePlugin
@@ -545,13 +553,20 @@ class CuraApplication(QtApplication):
 
     @pyqtProperty(bool)
     def needToShowUserAgreement(self) -> bool:
-        return self._need_to_show_user_agreement
+        return not UM.Util.parseBool(self.getPreferences().getValue("general/accepted_user_agreement"))
+
+    @pyqtSlot(bool)
+    def setNeedToShowUserAgreement(self, set_value: bool = True) -> None:
+        self.getPreferences().setValue("general/accepted_user_agreement", str(not set_value))
 
-    def setNeedToShowUserAgreement(self, set_value = True) -> None:
-        self._need_to_show_user_agreement = set_value
+    @pyqtSlot(str, str)
+    def writeToLog(self, severity: str, message: str) -> None:
+        Logger.log(severity, message)
 
     # DO NOT call this function to close the application, use checkAndExitApplication() instead which will perform
     # pre-exit checks such as checking for in-progress USB printing, etc.
+    # Except for the 'Decline and close' in the 'User Agreement'-step in the Welcome-pages, that should be a hard exit.
+    @pyqtSlot()
     def closeApplication(self) -> None:
         Logger.log("i", "Close application")
         main_window = self.getMainWindow()
@@ -745,6 +760,9 @@ class CuraApplication(QtApplication):
         # Initialize Cura API
         self._cura_API.initialize()
 
+        self._output_device_manager.start()
+        self._welcome_pages_model.initialize()
+
         # Detect in which mode to run and execute that mode
         if self._is_headless:
             self.runWithoutGUI()
@@ -839,10 +857,30 @@ class CuraApplication(QtApplication):
         # Hide the splash screen
         self.closeSplash()
 
+    @pyqtSlot(result = QObject)
+    def getDiscoveredPrintersModel(self, *args) -> "DiscoveredPrintersModel":
+        return self._discovered_printer_model
+
+    @pyqtSlot(result = QObject)
+    def getFirstStartMachineActionsModel(self, *args) -> "FirstStartMachineActionsModel":
+        return self._first_start_machine_actions_model
+
     @pyqtSlot(result = QObject)
     def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel:
         return self._setting_visibility_presets_model
 
+    @pyqtSlot(result = QObject)
+    def getWelcomePagesModel(self, *args) -> "WelcomePagesModel":
+        return self._welcome_pages_model
+
+    @pyqtSlot(result = QObject)
+    def getMachineSettingsManager(self, *args) -> "MachineSettingsManager":
+        return self._machine_settings_manager
+
+    @pyqtSlot(result = QObject)
+    def getTextManager(self, *args) -> "TextManager":
+        return self._text_manager
+
     def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions":
         if self._cura_formula_functions is None:
             self._cura_formula_functions = CuraFormulaFunctions(self)
@@ -975,6 +1013,9 @@ class CuraApplication(QtApplication):
         qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
         qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
 
+        qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel")
+        qmlRegisterType(TextManager, "Cura", 1, 0, "TextManager")
+
         qmlRegisterType(NetworkMJPGImage, "Cura", 1, 0, "NetworkMJPGImage")
 
         qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 0, "ObjectsModel", self.getObjectsModel)
@@ -988,7 +1029,8 @@ class CuraApplication(QtApplication):
         qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")
         qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel")
         qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
-        qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
+
+        qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel")
 
         qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
                                  "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)
@@ -999,6 +1041,7 @@ class CuraApplication(QtApplication):
         qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
         qmlRegisterType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel")
         qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
+        qmlRegisterType(FirstStartMachineActionsModel, "Cura", 1, 0, "FirstStartMachineActionsModel")
         qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
         qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
         qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance)

+ 6 - 0
cura/MachineAction.py

@@ -33,6 +33,12 @@ class MachineAction(QObject, PluginObject):
     def getKey(self) -> str:
         return self._key
 
+    ## Whether this action needs to ask the user anything.
+    #  If not, we shouldn't present the user with certain screens which otherwise show up.
+    #  Defaults to true to be in line with the old behaviour.
+    def needsUserInteraction(self) -> bool:
+        return True
+
     @pyqtProperty(str, notify = labelChanged)
     def label(self) -> str:
         return self._label

+ 140 - 0
cura/Machines/Models/DiscoveredPrintersModel.py

@@ -0,0 +1,140 @@
+# Copyright (c) 2019 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Callable, Dict, List, Optional, TYPE_CHECKING
+
+from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject
+
+from UM.i18n import i18nCatalog
+from UM.Logger import Logger
+
+if TYPE_CHECKING:
+    from PyQt5.QtCore import QObject
+
+    from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice
+
+
+catalog = i18nCatalog("cura")
+
+
+class DiscoveredPrinter(QObject):
+
+    def __init__(self, ip_address: str, key: str, name: str, create_callback: Callable[[str], None], machine_type: str,
+                 device: "NetworkedPrinterOutputDevice", parent: Optional["QObject"] = None) -> None:
+        super().__init__(parent)
+
+        self._ip_address = ip_address
+        self._key = key
+        self._name = name
+        self.create_callback = create_callback
+        self._machine_type = machine_type
+        self._device = device
+
+    nameChanged = pyqtSignal()
+
+    def getKey(self) -> str:
+        return self._key
+
+    @pyqtProperty(str, notify = nameChanged)
+    def name(self) -> str:
+        return self._name
+
+    def setName(self, name: str) -> None:
+        if self._name != name:
+            self._name = name
+            self.nameChanged.emit()
+
+    machineTypeChanged = pyqtSignal()
+
+    @pyqtProperty(str, notify = machineTypeChanged)
+    def machineType(self) -> str:
+        return self._machine_type
+
+    def setMachineType(self, machine_type: str) -> None:
+        if self._machine_type != machine_type:
+            self._machine_type = machine_type
+            self.machineTypeChanged.emit()
+
+    # Human readable machine type string
+    @pyqtProperty(str, notify = machineTypeChanged)
+    def readableMachineType(self) -> str:
+        from cura.CuraApplication import CuraApplication
+        readable_type = CuraApplication.getInstance().getMachineManager().getMachineTypeNameFromId(self._machine_type)
+        if not readable_type:
+            readable_type = catalog.i18nc("@label", "Unknown")
+        return readable_type
+
+    @pyqtProperty(bool, notify = machineTypeChanged)
+    def isUnknownMachineType(self) -> bool:
+        return self.readableMachineType.lower() == "unknown"
+
+    @pyqtProperty(QObject, constant = True)
+    def device(self) -> "NetworkedPrinterOutputDevice":
+        return self._device
+
+
+#
+# Discovered printers are all the printers that were found on the network, which provide a more convenient way
+# to add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then
+# add that printer to Cura as the active one).
+#
+class DiscoveredPrintersModel(QObject):
+
+    def __init__(self, parent: Optional["QObject"] = None) -> None:
+        super().__init__(parent)
+
+        self._discovered_printer_by_ip_dict = dict()  # type: Dict[str, DiscoveredPrinter]
+
+    discoveredPrintersChanged = pyqtSignal()
+
+    @pyqtProperty(list, notify = discoveredPrintersChanged)
+    def discoveredPrinters(self) -> List["DiscoveredPrinter"]:
+        item_list = list(x for x in self._discovered_printer_by_ip_dict.values())
+        item_list.sort(key = lambda x: x.device.name)
+        return item_list
+
+    def addDiscoveredPrinter(self, ip_address: str, key: str, name: str, create_callback: Callable[[str], None],
+                             machine_type: str, device: "NetworkedPrinterOutputDevice") -> None:
+        if ip_address in self._discovered_printer_by_ip_dict:
+            Logger.log("e", "Printer with ip [%s] has already been added", ip_address)
+            return
+
+        discovered_printer = DiscoveredPrinter(ip_address, key, name, create_callback, machine_type, device, parent = self)
+        self._discovered_printer_by_ip_dict[ip_address] = discovered_printer
+        self.discoveredPrintersChanged.emit()
+
+    def updateDiscoveredPrinter(self, ip_address: str,
+                                name: Optional[str] = None,
+                                machine_type: Optional[str] = None) -> None:
+        if ip_address not in self._discovered_printer_by_ip_dict:
+            Logger.log("w", "Printer with ip [%s] is not known", ip_address)
+            return
+
+        item = self._discovered_printer_by_ip_dict[ip_address]
+
+        if name is not None:
+            item.setName(name)
+        if machine_type is not None:
+            item.setMachineType(machine_type)
+
+    def removeDiscoveredPrinter(self, ip_address: str) -> None:
+        if ip_address not in self._discovered_printer_by_ip_dict:
+            Logger.log("w", "Key [%s] does not exist in the discovered printers list.", ip_address)
+            return
+
+        del self._discovered_printer_by_ip_dict[ip_address]
+        self.discoveredPrintersChanged.emit()
+
+    # A convenience function for QML to create a machine (GlobalStack) out of the given discovered printer.
+    # This function invokes the given discovered printer's "create_callback" to do this.
+    @pyqtSlot("QVariant")
+    def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None:
+        discovered_printer.create_callback(discovered_printer.getKey())
+
+    @pyqtSlot(str)
+    def createMachineFromDiscoveredPrinterAddress(self, ip_address: str) -> None:
+        if ip_address not in self._discovered_printer_by_ip_dict:
+            Logger.log("i", "Key [%s] does not exist in the discovered printers list.", ip_address)
+            return
+
+        self.createMachineFromDiscoveredPrinter(self._discovered_printer_by_ip_dict[ip_address])

+ 6 - 4
cura/Settings/ExtrudersModel.py → cura/Machines/Models/ExtrudersModel.py

@@ -2,23 +2,25 @@
 # Cura is released under the terms of the LGPLv3 or higher.
 
 from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer
-from typing import Iterable
+from typing import Iterable, TYPE_CHECKING
 
 from UM.i18n import i18nCatalog
-import UM.Qt.ListModel
+from UM.Qt.ListModel import ListModel
 from UM.Application import Application
 import UM.FlameProfiler
 
-from cura.Settings.ExtruderStack import ExtruderStack  # To listen to changes on the extruders.
+if TYPE_CHECKING:
+    from cura.Settings.ExtruderStack import ExtruderStack  # To listen to changes on the extruders.
 
 catalog = i18nCatalog("cura")
 
+
 ##  Model that holds extruders.
 #
 #   This model is designed for use by any list of extruders, but specifically
 #   intended for drop-down lists of the current machine's extruders in place of
 #   settings.
-class ExtrudersModel(UM.Qt.ListModel.ListModel):
+class ExtrudersModel(ListModel):
     # The ID of the container stack for the extruder.
     IdRole = Qt.UserRole + 1
 

+ 104 - 0
cura/Machines/Models/FirstStartMachineActionsModel.py

@@ -0,0 +1,104 @@
+# Copyright (c) 2019 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Optional, Dict, Any, TYPE_CHECKING
+
+from PyQt5.QtCore import QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot
+
+from UM.Qt.ListModel import ListModel
+
+if TYPE_CHECKING:
+    from cura.CuraApplication import CuraApplication
+
+
+#
+# This model holds all first-start machine actions for the currently active machine. It has 2 roles:
+#   - title   : the title/name of the action
+#   - content : the QObject of the QML content of the action
+#   - action  : the MachineAction object itself
+#
+class FirstStartMachineActionsModel(ListModel):
+
+    TitleRole = Qt.UserRole + 1
+    ContentRole = Qt.UserRole + 2
+    ActionRole = Qt.UserRole + 3
+
+    def __init__(self, application: "CuraApplication", parent: Optional[QObject] = None) -> None:
+        super().__init__(parent)
+
+        self.addRoleName(self.TitleRole, "title")
+        self.addRoleName(self.ContentRole, "content")
+        self.addRoleName(self.ActionRole, "action")
+
+        self._current_action_index = 0
+
+        self._application = application
+        self._application.initializationFinished.connect(self._initialize)
+
+    def _initialize(self) -> None:
+        self._application.getMachineManager().globalContainerChanged.connect(self._update)
+        self._update()
+
+    currentActionIndexChanged = pyqtSignal()
+    allFinished = pyqtSignal()  # Emitted when all actions have been finished.
+
+    @pyqtProperty(int, notify = currentActionIndexChanged)
+    def currentActionIndex(self) -> int:
+        return self._current_action_index
+
+    @pyqtProperty("QVariantMap", notify = currentActionIndexChanged)
+    def currentItem(self) -> Optional[Dict[str, Any]]:
+        if self._current_action_index >= self.count:
+            return dict()
+        else:
+            return self.getItem(self._current_action_index)
+
+    @pyqtProperty(bool, notify = currentActionIndexChanged)
+    def hasMoreActions(self) -> bool:
+        return self._current_action_index < self.count - 1
+
+    @pyqtSlot()
+    def goToNextAction(self) -> None:
+        # finish the current item
+        if "action" in self.currentItem:
+            self.currentItem["action"].setFinished()
+
+        if not self.hasMoreActions:
+            self.allFinished.emit()
+            self.reset()
+            return
+
+        self._current_action_index += 1
+        self.currentActionIndexChanged.emit()
+
+    # Resets the current action index to 0 so the wizard panel can show actions from the beginning.
+    @pyqtSlot()
+    def reset(self) -> None:
+        self._current_action_index = 0
+        self.currentActionIndexChanged.emit()
+
+        if self.count == 0:
+            self.allFinished.emit()
+
+    def _update(self) -> None:
+        global_stack = self._application.getMachineManager().activeMachine
+        if global_stack is None:
+            self.setItems([])
+            return
+
+        definition_id = global_stack.definition.getId()
+        first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id)
+
+        item_list = []
+        for item in first_start_actions:
+            item_list.append({"title": item.label,
+                              "content": item.displayItem,
+                              "action": item,
+                              })
+            item.reset()
+
+        self.setItems(item_list)
+        self.reset()
+
+
+__all__ = ["FirstStartMachineActionsModel"]

+ 0 - 1
cura/Machines/Models/GenericMaterialsModel.py

@@ -1,7 +1,6 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from UM.Logger import Logger
 from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
 
 class GenericMaterialsModel(BaseMaterialsModel):

+ 19 - 12
cura/GlobalStacksModel.py → cura/Machines/Models/GlobalStacksModel.py

@@ -1,14 +1,13 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
+from PyQt5.QtCore import Qt, QTimer
 
 from UM.Qt.ListModel import ListModel
+from UM.i18n import i18nCatalog
 
-from PyQt5.QtCore import pyqtProperty, Qt, QTimer
-
-from cura.PrinterOutputDevice import ConnectionType
+from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
 from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
-
 from cura.Settings.GlobalStack import GlobalStack
 
 
@@ -18,14 +17,18 @@ class GlobalStacksModel(ListModel):
     HasRemoteConnectionRole = Qt.UserRole + 3
     ConnectionTypeRole = Qt.UserRole + 4
     MetaDataRole = Qt.UserRole + 5
+    DiscoverySourceRole = Qt.UserRole + 6  # For separating local and remote printers in the machine management page
 
-    def __init__(self, parent = None):
+    def __init__(self, parent = None) -> None:
         super().__init__(parent)
+
+        self._catalog = i18nCatalog("cura")
+
         self.addRoleName(self.NameRole, "name")
         self.addRoleName(self.IdRole, "id")
         self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection")
         self.addRoleName(self.MetaDataRole, "metadata")
-        self._container_stacks = []
+        self.addRoleName(self.DiscoverySourceRole, "discoverySource")
 
         self._change_timer = QTimer()
         self._change_timer.setInterval(200)
@@ -36,16 +39,15 @@ class GlobalStacksModel(ListModel):
         CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
         CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
         CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
-        self._filter_dict = {}
         self._updateDelayed()
 
     ##  Handler for container added/removed events from registry
-    def _onContainerChanged(self, container):
+    def _onContainerChanged(self, container) -> None:
         # We only need to update when the added / removed container GlobalStack
         if isinstance(container, GlobalStack):
             self._updateDelayed()
 
-    def _updateDelayed(self):
+    def _updateDelayed(self) -> None:
         self._change_timer.start()
 
     def _update(self) -> None:
@@ -57,14 +59,19 @@ class GlobalStacksModel(ListModel):
             has_remote_connection = False
 
             for connection_type in container_stack.configuredConnectionTypes:
-                has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]
+                has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value,
+                                                             ConnectionType.CloudConnection.value]
 
             if container_stack.getMetaDataEntry("hidden", False) in ["True", True]:
                 continue
 
+            section_name = "Network enabled printers" if has_remote_connection else "Local printers"
+            section_name = self._catalog.i18nc("@info:title", section_name)
+
             items.append({"name": container_stack.getMetaDataEntry("group_name", container_stack.getName()),
                           "id": container_stack.getId(),
                           "hasRemoteConnection": has_remote_connection,
-                          "metadata": container_stack.getMetaData().copy()})
-        items.sort(key=lambda i: not i["hasRemoteConnection"])
+                          "metadata": container_stack.getMetaData().copy(),
+                          "discoverySource": section_name})
+        items.sort(key = lambda i: not i["hasRemoteConnection"])
         self.setItems(items)

+ 0 - 82
cura/Machines/Models/MachineManagementModel.py

@@ -1,82 +0,0 @@
-# Copyright (c) 2018 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from UM.Qt.ListModel import ListModel
-
-from PyQt5.QtCore import Qt
-
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.ContainerStack import ContainerStack
-
-from UM.i18n import i18nCatalog
-catalog = i18nCatalog("cura")
-
-
-#
-# This the QML model for the quality management page.
-#
-class MachineManagementModel(ListModel):
-    NameRole = Qt.UserRole + 1
-    IdRole = Qt.UserRole + 2
-    MetaDataRole = Qt.UserRole + 3
-    GroupRole = Qt.UserRole + 4
-
-    def __init__(self, parent = None):
-        super().__init__(parent)
-        self.addRoleName(self.NameRole, "name")
-        self.addRoleName(self.IdRole, "id")
-        self.addRoleName(self.MetaDataRole, "metadata")
-        self.addRoleName(self.GroupRole, "group")
-        self._local_container_stacks = []
-        self._network_container_stacks = []
-
-        # Listen to changes
-        ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
-        ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
-        ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
-        self._filter_dict = {}
-        self._update()
-
-    ##  Handler for container added/removed events from registry
-    def _onContainerChanged(self, container):
-        # We only need to update when the added / removed container is a stack.
-        if isinstance(container, ContainerStack) and container.getMetaDataEntry("type") == "machine":
-            self._update()
-
-    ##  Private convenience function to reset & repopulate the model.
-    def _update(self):
-        items = []
-
-        # Get first the network enabled printers
-        network_filter_printers = {"type": "machine",
-                                   "um_network_key": "*",
-                                   "hidden": "False"}
-        self._network_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**network_filter_printers)
-        self._network_container_stacks.sort(key = lambda i: i.getMetaDataEntry("group_name", ""))
-
-        for container in self._network_container_stacks:
-            metadata = container.getMetaData().copy()
-            if container.getBottom():
-                metadata["definition_name"] = container.getBottom().getName()
-
-            items.append({"name": metadata.get("group_name", ""),
-                          "id": container.getId(),
-                          "metadata": metadata,
-                          "group": catalog.i18nc("@info:title", "Network enabled printers")})
-
-        # Get now the local printers
-        local_filter_printers = {"type": "machine", "um_network_key": None}
-        self._local_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**local_filter_printers)
-        self._local_container_stacks.sort(key = lambda i: i.getName())
-
-        for container in self._local_container_stacks:
-            metadata = container.getMetaData().copy()
-            if container.getBottom():
-                metadata["definition_name"] = container.getBottom().getName()
-
-            items.append({"name": container.getName(),
-                          "id": container.getId(),
-                          "metadata": metadata,
-                          "group": catalog.i18nc("@info:title", "Local printers")})
-
-        self.setItems(items)

+ 1 - 2
cura/Machines/Models/MaterialBrandsModel.py

@@ -1,9 +1,8 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
+from PyQt5.QtCore import Qt, pyqtSignal
 from UM.Qt.ListModel import ListModel
-from UM.Logger import Logger
 from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
 
 class MaterialTypesModel(ListModel):

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