mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-10 07:15:03 -06:00
Merge branch 'ui_rework_4_0' into cura4.0_header
Conflicts: plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml resources/qml/Menus/ViewMenu.qml resources/themes/cura-dark/theme.json resources/themes/cura-light/theme.json
This commit is contained in:
commit
5de367bcc4
107 changed files with 15797 additions and 14749 deletions
|
@ -45,7 +45,7 @@ class Account(QObject):
|
|||
CALLBACK_PORT=self._callback_port,
|
||||
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
|
||||
CLIENT_ID="um---------------ultimaker_cura_drive_plugin",
|
||||
CLIENT_SCOPES="user.read drive.backups.read drive.backups.write",
|
||||
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
|
||||
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
|
||||
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
|
||||
AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import cast, TYPE_CHECKING, Optional
|
||||
from typing import cast, TYPE_CHECKING, Optional, Callable
|
||||
|
||||
import numpy
|
||||
|
||||
|
@ -13,6 +13,7 @@ from PyQt5.QtGui import QColor, QIcon
|
|||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.PluginError import PluginNotFoundError
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Camera import Camera
|
||||
|
@ -114,12 +115,13 @@ from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
|
|||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
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
|
||||
from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
|
||||
numpy.seterr(all = "ignore")
|
||||
|
@ -166,6 +168,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.default_theme = "cura-light"
|
||||
|
||||
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
|
||||
|
||||
self._boot_loading_time = time.time()
|
||||
|
||||
self._on_exit_callback_manager = OnExitCallbackManager(self)
|
||||
|
@ -302,8 +306,6 @@ class CuraApplication(QtApplication):
|
|||
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
||||
self._machine_action_manager.initialize()
|
||||
|
||||
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
|
||||
|
||||
def __sendCommandToSingleInstance(self):
|
||||
self._single_instance = SingleInstance(self, self._files_to_open)
|
||||
|
||||
|
@ -419,7 +421,7 @@ class CuraApplication(QtApplication):
|
|||
)
|
||||
|
||||
# Runs preparations that needs to be done before the starting process.
|
||||
def startSplashWindowPhase(self):
|
||||
def startSplashWindowPhase(self) -> None:
|
||||
super().startSplashWindowPhase()
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
@ -525,15 +527,15 @@ class CuraApplication(QtApplication):
|
|||
self._qml_engine.addImageProvider("print_job_preview", PrintJobPreviewImageProvider.PrintJobPreviewImageProvider())
|
||||
|
||||
@pyqtProperty(bool)
|
||||
def needToShowUserAgreement(self):
|
||||
def needToShowUserAgreement(self) -> bool:
|
||||
return self._need_to_show_user_agreement
|
||||
|
||||
def setNeedToShowUserAgreement(self, set_value = True):
|
||||
def setNeedToShowUserAgreement(self, set_value = True) -> None:
|
||||
self._need_to_show_user_agreement = set_value
|
||||
|
||||
# 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.
|
||||
def closeApplication(self):
|
||||
def closeApplication(self) -> None:
|
||||
Logger.log("i", "Close application")
|
||||
main_window = self.getMainWindow()
|
||||
if main_window is not None:
|
||||
|
@ -560,11 +562,11 @@ class CuraApplication(QtApplication):
|
|||
|
||||
showConfirmExitDialog = pyqtSignal(str, arguments = ["message"])
|
||||
|
||||
def setConfirmExitDialogCallback(self, callback):
|
||||
def setConfirmExitDialogCallback(self, callback: Callable) -> None:
|
||||
self._confirm_exit_dialog_callback = callback
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def callConfirmExitDialogCallback(self, yes_or_no: bool):
|
||||
def callConfirmExitDialogCallback(self, yes_or_no: bool) -> None:
|
||||
self._confirm_exit_dialog_callback(yes_or_no)
|
||||
|
||||
## Signal to connect preferences action in QML
|
||||
|
@ -572,9 +574,17 @@ class CuraApplication(QtApplication):
|
|||
|
||||
## Show the preferences window
|
||||
@pyqtSlot()
|
||||
def showPreferences(self):
|
||||
def showPreferences(self) -> None:
|
||||
self.showPreferencesWindow.emit()
|
||||
|
||||
@override(Application)
|
||||
def getGlobalContainerStack(self) -> Optional["GlobalStack"]:
|
||||
return self._global_container_stack
|
||||
|
||||
@override(Application)
|
||||
def setGlobalContainerStack(self, stack: "GlobalStack") -> None:
|
||||
super().setGlobalContainerStack(stack)
|
||||
|
||||
## A reusable dialogbox
|
||||
#
|
||||
showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"])
|
||||
|
@ -586,7 +596,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
showDiscardOrKeepProfileChanges = pyqtSignal()
|
||||
|
||||
def discardOrKeepProfileChanges(self):
|
||||
def discardOrKeepProfileChanges(self) -> bool:
|
||||
has_user_interaction = False
|
||||
choice = self.getPreferences().getValue("cura/choice_on_profile_override")
|
||||
if choice == "always_discard":
|
||||
|
@ -602,7 +612,7 @@ class CuraApplication(QtApplication):
|
|||
return has_user_interaction
|
||||
|
||||
@pyqtSlot(str)
|
||||
def discardOrKeepProfileChangesClosed(self, option):
|
||||
def discardOrKeepProfileChangesClosed(self, option: str) -> None:
|
||||
global_stack = self.getGlobalContainerStack()
|
||||
if option == "discard":
|
||||
for extruder in global_stack.extruders.values():
|
||||
|
@ -689,7 +699,7 @@ class CuraApplication(QtApplication):
|
|||
self._quality_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing machine manager")
|
||||
self._machine_manager = MachineManager(self)
|
||||
self._machine_manager = MachineManager(self, parent = self)
|
||||
|
||||
Logger.log("i", "Initializing container manager")
|
||||
self._container_manager = ContainerManager(self)
|
||||
|
@ -947,6 +957,9 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
|
||||
qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
|
||||
|
||||
from cura.PrinterOutput.CameraView import CameraView
|
||||
qmlRegisterType(CameraView, "Cura", 1, 0, "CameraView")
|
||||
|
||||
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
|
||||
"QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)
|
||||
qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginObject import PluginObject
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Application import Application
|
||||
|
||||
import os
|
||||
|
||||
|
||||
## Machine actions are actions that are added to a specific machine type. Examples of such actions are
|
||||
|
@ -19,7 +19,7 @@ class MachineAction(QObject, PluginObject):
|
|||
## Create a new Machine action.
|
||||
# \param key unique key of the machine action
|
||||
# \param label Human readable label used to identify the machine action.
|
||||
def __init__(self, key, label = ""):
|
||||
def __init__(self, key: str, label: str = "") -> None:
|
||||
super().__init__()
|
||||
self._key = key
|
||||
self._label = label
|
||||
|
@ -30,14 +30,14 @@ class MachineAction(QObject, PluginObject):
|
|||
labelChanged = pyqtSignal()
|
||||
onFinished = pyqtSignal()
|
||||
|
||||
def getKey(self):
|
||||
def getKey(self) -> str:
|
||||
return self._key
|
||||
|
||||
@pyqtProperty(str, notify = labelChanged)
|
||||
def label(self):
|
||||
def label(self) -> str:
|
||||
return self._label
|
||||
|
||||
def setLabel(self, label):
|
||||
def setLabel(self, label: str) -> None:
|
||||
if self._label != label:
|
||||
self._label = label
|
||||
self.labelChanged.emit()
|
||||
|
@ -46,29 +46,35 @@ class MachineAction(QObject, PluginObject):
|
|||
# This should not be re-implemented by child classes, instead re-implement _reset.
|
||||
# /sa _reset
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
def reset(self) -> None:
|
||||
self._finished = False
|
||||
self._reset()
|
||||
|
||||
## Protected implementation of reset.
|
||||
# /sa reset()
|
||||
def _reset(self):
|
||||
def _reset(self) -> None:
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def setFinished(self):
|
||||
def setFinished(self) -> None:
|
||||
self._finished = True
|
||||
self._reset()
|
||||
self.onFinished.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = onFinished)
|
||||
def finished(self):
|
||||
def finished(self) -> bool:
|
||||
return self._finished
|
||||
|
||||
## Protected helper to create a view object based on provided QML.
|
||||
def _createViewFromQML(self):
|
||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url)
|
||||
self._view = Application.getInstance().createQmlComponent(path, {"manager": self})
|
||||
def _createViewFromQML(self) -> None:
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||
return
|
||||
path = os.path.join(plugin_path, self._qml_url)
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
self._view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
||||
|
||||
@pyqtProperty(QObject, constant = True)
|
||||
def displayItem(self):
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, List, Set, Dict
|
||||
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from .MachineAction import MachineAction
|
||||
|
||||
|
||||
## Raised when trying to add an unknown machine action as a required action
|
||||
|
@ -20,46 +26,54 @@ class NotUniqueMachineActionError(Exception):
|
|||
|
||||
|
||||
class MachineActionManager(QObject):
|
||||
def __init__(self, application, parent = None):
|
||||
super().__init__(parent)
|
||||
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent = parent)
|
||||
self._application = application
|
||||
self._container_registry = self._application.getContainerRegistry()
|
||||
|
||||
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
|
||||
# Keeps track of which machines have already been processed so we don't do that again.
|
||||
self._definition_ids_with_default_actions_added = set() # type: Set[str]
|
||||
|
||||
# Dict of all known machine actions
|
||||
self._machine_actions = {} # type: Dict[str, MachineAction]
|
||||
# Dict of all required actions by definition ID
|
||||
self._required_actions = {} # type: Dict[str, List[MachineAction]]
|
||||
# Dict of all supported actions by definition ID
|
||||
self._supported_actions = {} # type: Dict[str, List[MachineAction]]
|
||||
# Dict of all actions that need to be done when first added by definition ID
|
||||
self._first_start_actions = {} # type: Dict[str, List[MachineAction]]
|
||||
|
||||
def initialize(self):
|
||||
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 container_registry.findDefinitionContainers():
|
||||
self._onContainerAdded(container)
|
||||
# Adds all default machine actions that are defined in the machine definition for the given machine.
|
||||
def addDefaultMachineActions(self, global_stack: "GlobalStack") -> None:
|
||||
definition_id = global_stack.definition.getId()
|
||||
|
||||
container_registry.containerAdded.connect(self._onContainerAdded)
|
||||
if definition_id in self._definition_ids_with_default_actions_added:
|
||||
Logger.log("i", "Default machine actions have been added for machine definition [%s], do nothing.",
|
||||
definition_id)
|
||||
return
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
## Ensure that the actions are added to this manager
|
||||
if isinstance(container, DefinitionContainer):
|
||||
supported_actions = container.getMetaDataEntry("supported_actions", [])
|
||||
for action in supported_actions:
|
||||
self.addSupportedAction(container.getId(), action)
|
||||
supported_actions = global_stack.getMetaDataEntry("supported_actions", [])
|
||||
for action_key in supported_actions:
|
||||
self.addSupportedAction(definition_id, action_key)
|
||||
|
||||
required_actions = container.getMetaDataEntry("required_actions", [])
|
||||
for action in required_actions:
|
||||
self.addRequiredAction(container.getId(), action)
|
||||
required_actions = global_stack.getMetaDataEntry("required_actions", [])
|
||||
for action_key in required_actions:
|
||||
self.addRequiredAction(definition_id, action_key)
|
||||
|
||||
first_start_actions = container.getMetaDataEntry("first_start_actions", [])
|
||||
for action in first_start_actions:
|
||||
self.addFirstStartAction(container.getId(), action)
|
||||
first_start_actions = global_stack.getMetaDataEntry("first_start_actions", [])
|
||||
for action_key in first_start_actions:
|
||||
self.addFirstStartAction(definition_id, action_key)
|
||||
|
||||
self._definition_ids_with_default_actions_added.add(definition_id)
|
||||
Logger.log("i", "Default machine actions added for machine definition [%s]", definition_id)
|
||||
|
||||
## Add a required action to a machine
|
||||
# Raises an exception when the action is not recognised.
|
||||
def addRequiredAction(self, definition_id, action_key):
|
||||
def addRequiredAction(self, definition_id: str, action_key: str) -> None:
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._required_actions:
|
||||
if self._machine_actions[action_key] not in self._required_actions[definition_id]:
|
||||
|
@ -70,7 +84,7 @@ class MachineActionManager(QObject):
|
|||
raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
|
||||
|
||||
## Add a supported action to a machine.
|
||||
def addSupportedAction(self, definition_id, action_key):
|
||||
def addSupportedAction(self, definition_id: str, action_key: str) -> None:
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._supported_actions:
|
||||
if self._machine_actions[action_key] not in self._supported_actions[definition_id]:
|
||||
|
@ -81,13 +95,10 @@ class MachineActionManager(QObject):
|
|||
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
|
||||
|
||||
## Add an action to the first start list of a machine.
|
||||
def addFirstStartAction(self, definition_id, action_key, index = None):
|
||||
def addFirstStartAction(self, definition_id: str, action_key: str) -> None:
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._first_start_actions:
|
||||
if index is not None:
|
||||
self._first_start_actions[definition_id].insert(index, self._machine_actions[action_key])
|
||||
else:
|
||||
self._first_start_actions[definition_id].append(self._machine_actions[action_key])
|
||||
self._first_start_actions[definition_id].append(self._machine_actions[action_key])
|
||||
else:
|
||||
self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
|
||||
else:
|
||||
|
@ -95,7 +106,7 @@ class MachineActionManager(QObject):
|
|||
|
||||
## Add a (unique) MachineAction
|
||||
# if the Key of the action is not unique, an exception is raised.
|
||||
def addMachineAction(self, action):
|
||||
def addMachineAction(self, action: "MachineAction") -> None:
|
||||
if action.getKey() not in self._machine_actions:
|
||||
self._machine_actions[action.getKey()] = action
|
||||
else:
|
||||
|
@ -105,7 +116,7 @@ class MachineActionManager(QObject):
|
|||
# \param definition_id The ID of the definition you want the supported actions of
|
||||
# \returns set of supported actions.
|
||||
@pyqtSlot(str, result = "QVariantList")
|
||||
def getSupportedActions(self, definition_id):
|
||||
def getSupportedActions(self, definition_id: str) -> List["MachineAction"]:
|
||||
if definition_id in self._supported_actions:
|
||||
return list(self._supported_actions[definition_id])
|
||||
else:
|
||||
|
@ -114,11 +125,11 @@ class MachineActionManager(QObject):
|
|||
## Get all actions required by given machine
|
||||
# \param definition_id The ID of the definition you want the required actions of
|
||||
# \returns set of required actions.
|
||||
def getRequiredActions(self, definition_id):
|
||||
def getRequiredActions(self, definition_id: str) -> List["MachineAction"]:
|
||||
if definition_id in self._required_actions:
|
||||
return self._required_actions[definition_id]
|
||||
else:
|
||||
return set()
|
||||
return list()
|
||||
|
||||
## Get all actions that need to be performed upon first start of a given machine.
|
||||
# Note that contrary to required / supported actions a list is returned (as it could be required to run the same
|
||||
|
@ -126,7 +137,7 @@ class MachineActionManager(QObject):
|
|||
# \param definition_id The ID of the definition that you want to get the "on added" actions for.
|
||||
# \returns List of actions.
|
||||
@pyqtSlot(str, result="QVariantList")
|
||||
def getFirstStartActions(self, definition_id):
|
||||
def getFirstStartActions(self, definition_id: str) -> List["MachineAction"]:
|
||||
if definition_id in self._first_start_actions:
|
||||
return self._first_start_actions[definition_id]
|
||||
else:
|
||||
|
@ -134,7 +145,7 @@ class MachineActionManager(QObject):
|
|||
|
||||
## Remove Machine action from manager
|
||||
# \param action to remove
|
||||
def removeMachineAction(self, action):
|
||||
def removeMachineAction(self, action: "MachineAction") -> None:
|
||||
try:
|
||||
del self._machine_actions[action.getKey()]
|
||||
except KeyError:
|
||||
|
@ -143,7 +154,7 @@ class MachineActionManager(QObject):
|
|||
## Get MachineAction by key
|
||||
# \param key String of key to select
|
||||
# \return Machine action if found, None otherwise
|
||||
def getMachineAction(self, key):
|
||||
def getMachineAction(self, key: str) -> Optional["MachineAction"]:
|
||||
if key in self._machine_actions:
|
||||
return self._machine_actions[key]
|
||||
else:
|
||||
|
|
|
@ -365,7 +365,7 @@ class MaterialManager(QObject):
|
|||
nozzle_name = None
|
||||
if extruder_stack.variant.getId() != "empty_variant":
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
diameter = extruder_stack.approximateMaterialDiameter
|
||||
diameter = extruder_stack.getApproximateMaterialDiameter()
|
||||
|
||||
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
|
||||
return self.getAvailableMaterials(machine.definition, nozzle_name, buildplate_name, diameter)
|
||||
|
@ -478,12 +478,22 @@ class MaterialManager(QObject):
|
|||
|
||||
buildplate_name = global_stack.getBuildplateName()
|
||||
machine_definition = global_stack.definition
|
||||
if extruder_definition is None:
|
||||
extruder_definition = global_stack.extruders[position].definition
|
||||
|
||||
if extruder_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||
# At this point the extruder_definition is not None
|
||||
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
||||
# The extruder-compatible material diameter in the extruder definition may not be the correct value because
|
||||
# the user can change it in the definition_changes container.
|
||||
if extruder_definition is None:
|
||||
extruder_stack_or_definition = global_stack.extruders[position]
|
||||
is_extruder_stack = True
|
||||
else:
|
||||
extruder_stack_or_definition = extruder_definition
|
||||
is_extruder_stack = False
|
||||
|
||||
if extruder_stack_or_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||
if is_extruder_stack:
|
||||
material_diameter = extruder_stack_or_definition.getCompatibleMaterialDiameter()
|
||||
else:
|
||||
material_diameter = extruder_stack_or_definition.getProperty("material_diameter", "value")
|
||||
|
||||
if isinstance(material_diameter, SettingFunction):
|
||||
material_diameter = material_diameter(global_stack)
|
||||
approximate_material_diameter = str(round(material_diameter))
|
||||
|
|
|
@ -64,9 +64,11 @@ class BaseMaterialsModel(ListModel):
|
|||
|
||||
if self._extruder_stack is not None:
|
||||
self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
|
||||
self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._update)
|
||||
self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
|
||||
if self._extruder_stack is not None:
|
||||
self._extruder_stack.pyqtContainersChanged.connect(self._update)
|
||||
self._extruder_stack.approximateMaterialDiameterChanged.connect(self._update)
|
||||
# Force update the model when the extruder stack changes
|
||||
self._update()
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import math
|
|||
import os
|
||||
import unicodedata
|
||||
import re # To create abbreviations for printer names.
|
||||
from typing import Dict
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
||||
|
||||
|
@ -16,55 +16,41 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.i18n import i18nCatalog
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
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
|
||||
# time/quality slider concept. It is a rather tricky combination of event handling
|
||||
# and state management. The logic behind this is as follows:
|
||||
#
|
||||
# - A scene change or setting change event happens.
|
||||
# We track what the source was of the change, either a scene change, a setting change, an active machine change or something else.
|
||||
# - This triggers a new slice with the current settings - this is the "current settings pass".
|
||||
# - When the slice is done, we update the current print time and material amount.
|
||||
# - If the source of the slice was not a Setting change, we start the second slice pass, the "low quality settings pass". Otherwise we stop here.
|
||||
# - When that is done, we update the minimum print time and start the final slice pass, the "Extra Fine settings pass".
|
||||
# - When the Extra Fine pass is done, we update the maximum print time.
|
||||
## A class for processing and the print times per build plate as well as managing the job name
|
||||
#
|
||||
# This class also mangles the current machine name and the filename of the first loaded mesh into a job name.
|
||||
# This job name is requested by the JobSpecs qml file.
|
||||
class PrintInformation(QObject):
|
||||
class SlicePass:
|
||||
CurrentSettings = 1
|
||||
LowQualitySettings = 2
|
||||
HighQualitySettings = 3
|
||||
|
||||
class SliceReason:
|
||||
SceneChanged = 1
|
||||
SettingChanged = 2
|
||||
ActiveMachineChanged = 3
|
||||
Other = 4
|
||||
UNTITLED_JOB_NAME = "Untitled"
|
||||
|
||||
def __init__(self, application, parent = None):
|
||||
def __init__(self, application: "CuraApplication", parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._application = application
|
||||
|
||||
self.UNTITLED_JOB_NAME = "Untitled"
|
||||
|
||||
self.initializeCuraMessagePrintTimeProperties()
|
||||
|
||||
self._material_lengths = {} # indexed by build plate number
|
||||
self._material_weights = {}
|
||||
self._material_costs = {}
|
||||
self._material_names = {}
|
||||
# Indexed by build plate number
|
||||
self._material_lengths = {} # type: Dict[int, List[float]]
|
||||
self._material_weights = {} # type: Dict[int, List[float]]
|
||||
self._material_costs = {} # type: Dict[int, List[float]]
|
||||
self._material_names = {} # type: Dict[int, List[str]]
|
||||
|
||||
self._pre_sliced = False
|
||||
|
||||
self._backend = self._application.getBackend()
|
||||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
|
||||
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
self._is_user_specified_job_name = False
|
||||
|
@ -72,29 +58,25 @@ class PrintInformation(QObject):
|
|||
self._abbr_machine = ""
|
||||
self._job_name = ""
|
||||
self._active_build_plate = 0
|
||||
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||
self._initVariablesByBuildPlate(self._active_build_plate)
|
||||
|
||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||
|
||||
ss = self._multi_build_plate_model.maxBuildPlate
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
||||
self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation)
|
||||
self._application.fileLoaded.connect(self.setBaseName)
|
||||
self._application.workspaceLoaded.connect(self.setProjectName)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
||||
|
||||
self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
|
||||
self._application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
||||
|
||||
self._onActiveMaterialsChanged()
|
||||
|
||||
self._material_amounts = []
|
||||
self._material_amounts = [] # type: List[float]
|
||||
|
||||
# Crate cura message translations and using translation keys initialize empty time Duration object for total time
|
||||
# and time for each feature
|
||||
def initializeCuraMessagePrintTimeProperties(self):
|
||||
self._current_print_time = {} # Duration(None, self)
|
||||
def initializeCuraMessagePrintTimeProperties(self) -> None:
|
||||
self._current_print_time = {} # type: Dict[int, Duration]
|
||||
|
||||
self._print_time_message_translations = {
|
||||
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
||||
|
@ -110,17 +92,17 @@ class PrintInformation(QObject):
|
|||
"none": catalog.i18nc("@tooltip", "Other")
|
||||
}
|
||||
|
||||
self._print_time_message_values = {}
|
||||
self._print_times_per_feature = {} # type: Dict[int, Dict[str, Duration]]
|
||||
|
||||
def _initPrintTimeMessageValues(self, build_plate_number):
|
||||
def _initPrintTimesPerFeature(self, build_plate_number: int) -> None:
|
||||
# Full fill message values using keys from _print_time_message_translations
|
||||
self._print_time_message_values[build_plate_number] = {}
|
||||
self._print_times_per_feature[build_plate_number] = {}
|
||||
for key in self._print_time_message_translations.keys():
|
||||
self._print_time_message_values[build_plate_number][key] = Duration(None, self)
|
||||
self._print_times_per_feature[build_plate_number][key] = Duration(None, self)
|
||||
|
||||
def _initVariablesWithBuildPlate(self, build_plate_number):
|
||||
if build_plate_number not in self._print_time_message_values:
|
||||
self._initPrintTimeMessageValues(build_plate_number)
|
||||
def _initVariablesByBuildPlate(self, build_plate_number: int) -> None:
|
||||
if build_plate_number not in self._print_times_per_feature:
|
||||
self._initPrintTimesPerFeature(build_plate_number)
|
||||
if self._active_build_plate not in self._material_lengths:
|
||||
self._material_lengths[self._active_build_plate] = []
|
||||
if self._active_build_plate not in self._material_weights:
|
||||
|
@ -130,23 +112,24 @@ class PrintInformation(QObject):
|
|||
if self._active_build_plate not in self._material_names:
|
||||
self._material_names[self._active_build_plate] = []
|
||||
if self._active_build_plate not in self._current_print_time:
|
||||
self._current_print_time[self._active_build_plate] = Duration(None, self)
|
||||
self._current_print_time[self._active_build_plate] = Duration(parent = self)
|
||||
|
||||
currentPrintTimeChanged = pyqtSignal()
|
||||
|
||||
preSlicedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=preSlicedChanged)
|
||||
def preSliced(self):
|
||||
def preSliced(self) -> bool:
|
||||
return self._pre_sliced
|
||||
|
||||
def setPreSliced(self, pre_sliced):
|
||||
self._pre_sliced = pre_sliced
|
||||
self._updateJobName()
|
||||
self.preSlicedChanged.emit()
|
||||
def setPreSliced(self, pre_sliced: bool) -> None:
|
||||
if self._pre_sliced != pre_sliced:
|
||||
self._pre_sliced = pre_sliced
|
||||
self._updateJobName()
|
||||
self.preSlicedChanged.emit()
|
||||
|
||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||
def currentPrintTime(self):
|
||||
def currentPrintTime(self) -> Duration:
|
||||
return self._current_print_time[self._active_build_plate]
|
||||
|
||||
materialLengthsChanged = pyqtSignal()
|
||||
|
@ -173,36 +156,41 @@ class PrintInformation(QObject):
|
|||
def materialNames(self):
|
||||
return self._material_names[self._active_build_plate]
|
||||
|
||||
def printTimes(self):
|
||||
return self._print_time_message_values[self._active_build_plate]
|
||||
# Get all print times (by feature) of the active buildplate.
|
||||
def printTimes(self) -> Dict[str, Duration]:
|
||||
return self._print_times_per_feature[self._active_build_plate]
|
||||
|
||||
def _onPrintDurationMessage(self, build_plate_number, print_time: Dict[str, int], material_amounts: list):
|
||||
self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
|
||||
def _onPrintDurationMessage(self, build_plate_number: int, print_times_per_feature: Dict[str, int], material_amounts: List[float]) -> None:
|
||||
self._updateTotalPrintTimePerFeature(build_plate_number, print_times_per_feature)
|
||||
self.currentPrintTimeChanged.emit()
|
||||
|
||||
self._material_amounts = material_amounts
|
||||
self._calculateInformation(build_plate_number)
|
||||
|
||||
def _updateTotalPrintTimePerFeature(self, build_plate_number, print_time: Dict[str, int]):
|
||||
def _updateTotalPrintTimePerFeature(self, build_plate_number: int, print_times_per_feature: Dict[str, int]) -> None:
|
||||
total_estimated_time = 0
|
||||
|
||||
if build_plate_number not in self._print_time_message_values:
|
||||
self._initPrintTimeMessageValues(build_plate_number)
|
||||
if build_plate_number not in self._print_times_per_feature:
|
||||
self._initPrintTimesPerFeature(build_plate_number)
|
||||
|
||||
for feature, time in print_times_per_feature.items():
|
||||
if feature not in self._print_times_per_feature[build_plate_number]:
|
||||
self._print_times_per_feature[build_plate_number][feature] = Duration(parent=self)
|
||||
duration = self._print_times_per_feature[build_plate_number][feature]
|
||||
|
||||
for feature, time in print_time.items():
|
||||
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
||||
self._print_time_message_values[build_plate_number].get(feature).setDuration(0)
|
||||
duration.setDuration(0)
|
||||
Logger.log("w", "Received NaN for print duration message")
|
||||
continue
|
||||
|
||||
total_estimated_time += time
|
||||
self._print_time_message_values[build_plate_number].get(feature).setDuration(time)
|
||||
duration.setDuration(time)
|
||||
|
||||
if build_plate_number not in self._current_print_time:
|
||||
self._current_print_time[build_plate_number] = Duration(None, self)
|
||||
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
||||
|
||||
def _calculateInformation(self, build_plate_number):
|
||||
def _calculateInformation(self, build_plate_number: int) -> None:
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return
|
||||
|
@ -215,39 +203,45 @@ class PrintInformation(QObject):
|
|||
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():
|
||||
|
||||
for position in extruder_stacks:
|
||||
extruder_stack = extruder_stacks[position]
|
||||
index = int(position)
|
||||
if index >= len(self._material_amounts):
|
||||
continue
|
||||
amount = self._material_amounts[index]
|
||||
## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
|
||||
# list comprehension filtering to solve this for us.
|
||||
# Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
|
||||
# list comprehension filtering to solve this for us.
|
||||
density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
|
||||
material = extruder_stack.findContainer({"type": "material"})
|
||||
material = extruder_stack.material
|
||||
radius = extruder_stack.getProperty("material_diameter", "value") / 2
|
||||
|
||||
weight = float(amount) * float(density) / 1000
|
||||
cost = 0
|
||||
material_name = catalog.i18nc("@label unknown material", "Unknown")
|
||||
if material:
|
||||
material_guid = material.getMetaDataEntry("GUID")
|
||||
material_name = material.getName()
|
||||
if material_guid in material_preference_values:
|
||||
material_values = material_preference_values[material_guid]
|
||||
cost = 0.
|
||||
|
||||
weight_per_spool = float(material_values["spool_weight"] if material_values and "spool_weight" in material_values else 0)
|
||||
cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0)
|
||||
material_guid = material.getMetaDataEntry("GUID")
|
||||
material_name = material.getName()
|
||||
if material_guid in material_preference_values:
|
||||
material_values = material_preference_values[material_guid]
|
||||
|
||||
if weight_per_spool != 0:
|
||||
cost = cost_per_spool * weight / weight_per_spool
|
||||
else:
|
||||
cost = 0
|
||||
if material_values and "spool_weight" in material_values:
|
||||
weight_per_spool = float(material_values["spool_weight"])
|
||||
else:
|
||||
weight_per_spool = float(extruder_stack.getMetaDataEntry("properties", {}).get("weight", 0))
|
||||
|
||||
cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0)
|
||||
|
||||
if weight_per_spool != 0:
|
||||
cost = cost_per_spool * weight / weight_per_spool
|
||||
else:
|
||||
cost = 0
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
if radius != 0:
|
||||
length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
|
||||
else:
|
||||
length = 0
|
||||
|
||||
self._material_weights[build_plate_number].append(weight)
|
||||
self._material_lengths[build_plate_number].append(length)
|
||||
self._material_costs[build_plate_number].append(cost)
|
||||
|
@ -258,20 +252,20 @@ class PrintInformation(QObject):
|
|||
self.materialCostsChanged.emit()
|
||||
self.materialNamesChanged.emit()
|
||||
|
||||
def _onPreferencesChanged(self, preference):
|
||||
def _onPreferencesChanged(self, preference: str) -> None:
|
||||
if preference != "cura/material_settings":
|
||||
return
|
||||
|
||||
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
||||
self._calculateInformation(build_plate_number)
|
||||
|
||||
def _onActiveBuildPlateChanged(self):
|
||||
def _onActiveBuildPlateChanged(self) -> None:
|
||||
new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
|
||||
if new_active_build_plate != self._active_build_plate:
|
||||
self._active_build_plate = new_active_build_plate
|
||||
self._updateJobName()
|
||||
|
||||
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||
self._initVariablesByBuildPlate(self._active_build_plate)
|
||||
|
||||
self.materialLengthsChanged.emit()
|
||||
self.materialWeightsChanged.emit()
|
||||
|
@ -279,14 +273,14 @@ class PrintInformation(QObject):
|
|||
self.materialNamesChanged.emit()
|
||||
self.currentPrintTimeChanged.emit()
|
||||
|
||||
def _onActiveMaterialsChanged(self, *args, **kwargs):
|
||||
def _onActiveMaterialsChanged(self, *args, **kwargs) -> None:
|
||||
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
||||
self._calculateInformation(build_plate_number)
|
||||
|
||||
# Manual override of job name should also set the base name so that when the printer prefix is updated, it the
|
||||
# prefix can be added to the manually added name, not the old base name
|
||||
@pyqtSlot(str, bool)
|
||||
def setJobName(self, name, is_user_specified_job_name = False):
|
||||
def setJobName(self, name: str, is_user_specified_job_name = False) -> None:
|
||||
self._is_user_specified_job_name = is_user_specified_job_name
|
||||
self._job_name = name
|
||||
self._base_name = name.replace(self._abbr_machine + "_", "")
|
||||
|
@ -300,7 +294,7 @@ class PrintInformation(QObject):
|
|||
def jobName(self):
|
||||
return self._job_name
|
||||
|
||||
def _updateJobName(self):
|
||||
def _updateJobName(self) -> None:
|
||||
if self._base_name == "":
|
||||
self._job_name = self.UNTITLED_JOB_NAME
|
||||
self._is_user_specified_job_name = False
|
||||
|
@ -335,12 +329,12 @@ class PrintInformation(QObject):
|
|||
self.jobNameChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setProjectName(self, name):
|
||||
def setProjectName(self, name: str) -> None:
|
||||
self.setBaseName(name, is_project_file = True)
|
||||
|
||||
baseNameChanged = pyqtSignal()
|
||||
|
||||
def setBaseName(self, base_name: str, is_project_file: bool = False):
|
||||
def setBaseName(self, base_name: str, is_project_file: bool = False) -> None:
|
||||
self._is_user_specified_job_name = False
|
||||
|
||||
# Ensure that we don't use entire path but only filename
|
||||
|
@ -384,7 +378,7 @@ class PrintInformation(QObject):
|
|||
## Created an acronym-like abbreviated machine name from the currently
|
||||
# active machine name.
|
||||
# Called each time the global stack is switched.
|
||||
def _defineAbbreviatedMachineName(self):
|
||||
def _defineAbbreviatedMachineName(self) -> None:
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
self._abbr_machine = ""
|
||||
|
@ -408,15 +402,15 @@ class PrintInformation(QObject):
|
|||
self._abbr_machine = abbr_machine
|
||||
|
||||
## Utility method that strips accents from characters (eg: â -> a)
|
||||
def _stripAccents(self, str):
|
||||
return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
|
||||
def _stripAccents(self, to_strip: str) -> str:
|
||||
return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
|
||||
|
||||
@pyqtSlot(result = "QVariantMap")
|
||||
def getFeaturePrintTimes(self):
|
||||
result = {}
|
||||
if self._active_build_plate not in self._print_time_message_values:
|
||||
self._initPrintTimeMessageValues(self._active_build_plate)
|
||||
for feature, time in self._print_time_message_values[self._active_build_plate].items():
|
||||
if self._active_build_plate not in self._print_times_per_feature:
|
||||
self._initPrintTimesPerFeature(self._active_build_plate)
|
||||
for feature, time in self._print_times_per_feature[self._active_build_plate].items():
|
||||
if feature in self._print_time_message_translations:
|
||||
result[self._print_time_message_translations[feature]] = time
|
||||
else:
|
||||
|
@ -424,22 +418,22 @@ class PrintInformation(QObject):
|
|||
return result
|
||||
|
||||
# Simulate message with zero time duration
|
||||
def setToZeroPrintInformation(self, build_plate = None):
|
||||
def setToZeroPrintInformation(self, build_plate: Optional[int] = None) -> None:
|
||||
if build_plate is None:
|
||||
build_plate = self._active_build_plate
|
||||
|
||||
# Construct the 0-time message
|
||||
temp_message = {}
|
||||
if build_plate not in self._print_time_message_values:
|
||||
self._print_time_message_values[build_plate] = {}
|
||||
for key in self._print_time_message_values[build_plate].keys():
|
||||
if build_plate not in self._print_times_per_feature:
|
||||
self._print_times_per_feature[build_plate] = {}
|
||||
for key in self._print_times_per_feature[build_plate].keys():
|
||||
temp_message[key] = 0
|
||||
temp_material_amounts = [0]
|
||||
temp_material_amounts = [0.]
|
||||
|
||||
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
|
||||
|
||||
## Listen to scene changes to check if we need to reset the print information
|
||||
def _onSceneChanged(self, scene_node):
|
||||
def _onSceneChanged(self, scene_node: SceneNode) -> None:
|
||||
# Ignore any changes that are not related to sliceable objects
|
||||
if not isinstance(scene_node, SceneNode)\
|
||||
or not scene_node.callDecoration("isSliceable")\
|
||||
|
|
41
cura/PrinterOutput/CameraView.py
Normal file
41
cura/PrinterOutput/CameraView.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtGui import QImage
|
||||
from PyQt5.QtQuick import QQuickPaintedItem
|
||||
|
||||
|
||||
#
|
||||
# A custom camera view that uses QQuickPaintedItem to present (or "paint") the image frames from a printer's
|
||||
# network camera feed.
|
||||
#
|
||||
class CameraView(QQuickPaintedItem):
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._image = QImage()
|
||||
|
||||
imageChanged = pyqtSignal()
|
||||
|
||||
def setImage(self, image: "QImage") -> None:
|
||||
self._image = image
|
||||
self.imageChanged.emit()
|
||||
self.update()
|
||||
|
||||
def getImage(self) -> "QImage":
|
||||
return self._image
|
||||
|
||||
image = pyqtProperty(QImage, fget = getImage, fset = setImage, notify = imageChanged)
|
||||
|
||||
@pyqtProperty(int, notify = imageChanged)
|
||||
def imageWidth(self) -> int:
|
||||
return self._image.width()
|
||||
|
||||
@pyqtProperty(int, notify = imageChanged)
|
||||
def imageHeight(self) -> int:
|
||||
return self._image.height()
|
||||
|
||||
def paint(self, painter):
|
||||
painter.drawImage(self.contentsBoundingRect(), self._image)
|
|
@ -16,7 +16,6 @@ class NetworkCamera(QObject):
|
|||
self._image_request = None
|
||||
self._image_reply = None
|
||||
self._image = QImage()
|
||||
self._image_id = 0
|
||||
|
||||
self._target = target
|
||||
self._started = False
|
||||
|
@ -33,15 +32,9 @@ class NetworkCamera(QObject):
|
|||
if restart_required:
|
||||
self.start()
|
||||
|
||||
@pyqtProperty(QUrl, notify=newImage)
|
||||
@pyqtProperty(QImage, notify=newImage)
|
||||
def latestImage(self):
|
||||
self._image_id += 1
|
||||
# There is an image provider that is called "camera". In order to ensure that the image qml object, that
|
||||
# requires a QUrl to function, updates correctly we add an increasing number. This causes to see the QUrl
|
||||
# as new (instead of relying on cached version and thus forces an update.
|
||||
temp = "image://camera/" + str(self._image_id)
|
||||
|
||||
return QUrl(temp, QUrl.TolerantMode)
|
||||
return self._image
|
||||
|
||||
@pyqtSlot()
|
||||
def start(self):
|
||||
|
@ -116,4 +109,4 @@ class NetworkCamera(QObject):
|
|||
self._stream_buffer_start_index = -1
|
||||
self._image.loadFromData(jpg_data)
|
||||
|
||||
self.newImage.emit()
|
||||
self.newImage.emit()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
|
||||
|
@ -12,7 +12,6 @@ if TYPE_CHECKING:
|
|||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
|
||||
|
||||
class PrintJobOutputModel(QObject):
|
||||
stateChanged = pyqtSignal()
|
||||
timeTotalChanged = pyqtSignal()
|
||||
|
@ -147,4 +146,4 @@ class PrintJobOutputModel(QObject):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def setState(self, state):
|
||||
self._output_controller.setJobState(self, state)
|
||||
self._output_controller.setJobState(self, state)
|
||||
|
|
|
@ -50,7 +50,7 @@ class PrinterOutputModel(QObject):
|
|||
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
|
||||
self._extruders]
|
||||
|
||||
self._camera = None
|
||||
self._camera = None # type: Optional[NetworkCamera]
|
||||
|
||||
@pyqtProperty(str, constant = True)
|
||||
def firmwareVersion(self) -> str:
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class BlockSlicingDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def isBlockSlicing(self):
|
||||
def isBlockSlicing(self) -> bool:
|
||||
return True
|
||||
|
|
|
@ -145,13 +145,11 @@ class CuraContainerStack(ContainerStack):
|
|||
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
|
||||
|
||||
## Get the definition container.
|
||||
#
|
||||
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
|
||||
@pyqtProperty(QObject, fset = setDefinition, notify = pyqtContainersChanged)
|
||||
def definition(self) -> DefinitionContainer:
|
||||
def getDefinition(self) -> "DefinitionContainer":
|
||||
return cast(DefinitionContainer, self._containers[_ContainerIndexes.Definition])
|
||||
|
||||
definition = pyqtProperty(QObject, fget = getDefinition, fset = setDefinition, notify = pyqtContainersChanged)
|
||||
|
||||
@override(ContainerStack)
|
||||
def getBottom(self) -> "DefinitionContainer":
|
||||
return self.definition
|
||||
|
|
|
@ -129,7 +129,7 @@ class CuraStackBuilder:
|
|||
|
||||
# get material container for extruders
|
||||
material_container = application.empty_material_container
|
||||
material_node = material_manager.getDefaultMaterial(global_stack, extruder_position, extruder_variant_name,
|
||||
material_node = material_manager.getDefaultMaterial(global_stack, str(extruder_position), extruder_variant_name,
|
||||
extruder_definition = extruder_definition)
|
||||
if material_node and material_node.getContainer():
|
||||
material_container = material_node.getContainer()
|
||||
|
@ -145,7 +145,6 @@ class CuraStackBuilder:
|
|||
quality_container = application.empty_quality_container
|
||||
)
|
||||
new_extruder.setNextStack(global_stack)
|
||||
global_stack.addExtruder(new_extruder)
|
||||
|
||||
registry.addContainer(new_extruder)
|
||||
|
||||
|
|
|
@ -374,8 +374,6 @@ class ExtruderManager(QObject):
|
|||
extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0]
|
||||
extruder_stack_0.definition = extruder_definition
|
||||
|
||||
extruder_stack_0.setNextStack(global_stack)
|
||||
|
||||
## Get all extruder values for a certain setting.
|
||||
#
|
||||
# This is exposed to qml for display purposes
|
||||
|
|
|
@ -65,16 +65,33 @@ class ExtruderStack(CuraContainerStack):
|
|||
def getLoadingPriority(cls) -> int:
|
||||
return 3
|
||||
|
||||
compatibleMaterialDiameterChanged = pyqtSignal()
|
||||
|
||||
## Return the filament diameter that the machine requires.
|
||||
#
|
||||
# If the machine has no requirement for the diameter, -1 is returned.
|
||||
# \return The filament diameter for the printer
|
||||
@property
|
||||
def materialDiameter(self) -> float:
|
||||
def getCompatibleMaterialDiameter(self) -> float:
|
||||
context = PropertyEvaluationContext(self)
|
||||
context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant
|
||||
|
||||
return self.getProperty("material_diameter", "value", context = context)
|
||||
return float(self.getProperty("material_diameter", "value", context = context))
|
||||
|
||||
def setCompatibleMaterialDiameter(self, value: float) -> None:
|
||||
old_approximate_diameter = self.getApproximateMaterialDiameter()
|
||||
if self.getCompatibleMaterialDiameter() != value:
|
||||
self.definitionChanges.setProperty("material_diameter", "value", value)
|
||||
self.compatibleMaterialDiameterChanged.emit()
|
||||
|
||||
# Emit approximate diameter changed signal if needed
|
||||
if old_approximate_diameter != self.getApproximateMaterialDiameter():
|
||||
self.approximateMaterialDiameterChanged.emit()
|
||||
|
||||
compatibleMaterialDiameter = pyqtProperty(float, fset = setCompatibleMaterialDiameter,
|
||||
fget = getCompatibleMaterialDiameter,
|
||||
notify = compatibleMaterialDiameterChanged)
|
||||
|
||||
approximateMaterialDiameterChanged = pyqtSignal()
|
||||
|
||||
## Return the approximate filament diameter that the machine requires.
|
||||
#
|
||||
|
@ -84,9 +101,11 @@ class ExtruderStack(CuraContainerStack):
|
|||
# If the machine has no requirement for the diameter, -1 is returned.
|
||||
#
|
||||
# \return The approximate filament diameter for the printer
|
||||
@pyqtProperty(float)
|
||||
def approximateMaterialDiameter(self) -> float:
|
||||
return round(float(self.materialDiameter))
|
||||
def getApproximateMaterialDiameter(self) -> float:
|
||||
return round(self.getCompatibleMaterialDiameter())
|
||||
|
||||
approximateMaterialDiameter = pyqtProperty(float, fget = getApproximateMaterialDiameter,
|
||||
notify = approximateMaterialDiameterChanged)
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
|
|
|
@ -20,7 +20,6 @@ from UM.Message import Message
|
|||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
|
@ -29,6 +28,9 @@ from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
|
|||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container,
|
||||
empty_material_container, empty_quality_container,
|
||||
empty_quality_changes_container)
|
||||
|
||||
from .CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
|
@ -36,6 +38,7 @@ from UM.i18n import i18nCatalog
|
|||
catalog = i18nCatalog("cura")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.CuraContainerStack import CuraContainerStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
|
@ -47,7 +50,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class MachineManager(QObject):
|
||||
def __init__(self, parent: QObject = None) -> None:
|
||||
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._active_container_stack = None # type: Optional[ExtruderStack]
|
||||
|
@ -66,9 +69,10 @@ class MachineManager(QObject):
|
|||
self._instance_container_timer.setSingleShot(True)
|
||||
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
|
||||
|
||||
self._application = cura.CuraApplication.CuraApplication.getInstance() #type: cura.CuraApplication.CuraApplication
|
||||
self._application = application
|
||||
self._container_registry = self._application.getContainerRegistry()
|
||||
self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._application.getContainerRegistry().containerLoadComplete.connect(self._onContainersChanged)
|
||||
self._container_registry.containerLoadComplete.connect(self._onContainersChanged)
|
||||
|
||||
## When the global container is changed, active material probably needs to be updated.
|
||||
self.globalContainerChanged.connect(self.activeMaterialChanged)
|
||||
|
@ -80,13 +84,6 @@ class MachineManager(QObject):
|
|||
|
||||
self._stacks_have_errors = None # type: Optional[bool]
|
||||
|
||||
self._empty_container = CuraContainerRegistry.getInstance().getEmptyInstanceContainer() #type: InstanceContainer
|
||||
self._empty_definition_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0] #type: InstanceContainer
|
||||
self._empty_variant_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] #type: InstanceContainer
|
||||
self._empty_material_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_material")[0] #type: InstanceContainer
|
||||
self._empty_quality_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] #type: InstanceContainer
|
||||
self._empty_quality_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
|
||||
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
|
@ -192,19 +189,21 @@ class MachineManager(QObject):
|
|||
for extruder in self._global_container_stack.extruders.values():
|
||||
extruder_configuration = ExtruderConfigurationModel()
|
||||
# For compare just the GUID is needed at this moment
|
||||
mat_type = extruder.material.getMetaDataEntry("material") if extruder.material != self._empty_material_container else None
|
||||
mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != self._empty_material_container else None
|
||||
mat_color = extruder.material.getMetaDataEntry("color_name") if extruder.material != self._empty_material_container else None
|
||||
mat_brand = extruder.material.getMetaDataEntry("brand") if extruder.material != self._empty_material_container else None
|
||||
mat_name = extruder.material.getMetaDataEntry("name") if extruder.material != self._empty_material_container else None
|
||||
mat_type = extruder.material.getMetaDataEntry("material") if extruder.material != empty_material_container else None
|
||||
mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != empty_material_container else None
|
||||
mat_color = extruder.material.getMetaDataEntry("color_name") if extruder.material != empty_material_container else None
|
||||
mat_brand = extruder.material.getMetaDataEntry("brand") if extruder.material != empty_material_container else None
|
||||
mat_name = extruder.material.getMetaDataEntry("name") if extruder.material != empty_material_container else None
|
||||
material_model = MaterialOutputModel(mat_guid, mat_type, mat_color, mat_brand, mat_name)
|
||||
|
||||
extruder_configuration.position = int(extruder.getMetaDataEntry("position"))
|
||||
extruder_configuration.material = material_model
|
||||
extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != self._empty_variant_container else None
|
||||
extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != empty_variant_container else None
|
||||
self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
|
||||
|
||||
self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != self._empty_variant_container else None
|
||||
# an empty build plate configuration from the network printer is presented as an empty string, so use "" for an
|
||||
# empty build plate.
|
||||
self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != empty_variant_container else ""
|
||||
self.currentConfigurationChanged.emit()
|
||||
|
||||
@pyqtSlot(QObject, result = bool)
|
||||
|
@ -258,14 +257,14 @@ class MachineManager(QObject):
|
|||
|
||||
# Global stack can have only a variant if it is a buildplate
|
||||
global_variant = self._global_container_stack.variant
|
||||
if global_variant != self._empty_variant_container:
|
||||
if global_variant != empty_variant_container:
|
||||
if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
|
||||
self._global_container_stack.setVariant(self._empty_variant_container)
|
||||
self._global_container_stack.setVariant(empty_variant_container)
|
||||
|
||||
# set the global material to empty as we now use the extruder stack at all times - CURA-4482
|
||||
global_material = self._global_container_stack.material
|
||||
if global_material != self._empty_material_container:
|
||||
self._global_container_stack.setMaterial(self._empty_material_container)
|
||||
if global_material != empty_material_container:
|
||||
self._global_container_stack.setMaterial(empty_material_container)
|
||||
|
||||
# Listen for changes on all extruder stacks
|
||||
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
||||
|
@ -367,6 +366,10 @@ class MachineManager(QObject):
|
|||
return
|
||||
|
||||
global_stack = containers[0]
|
||||
|
||||
# Make sure that the default machine actions for this machine have been added
|
||||
self._application.getMachineActionManager().addDefaultMachineActions(global_stack)
|
||||
|
||||
ExtruderManager.getInstance()._fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
if not global_stack.isValid():
|
||||
# Mark global stack as invalid
|
||||
|
@ -593,7 +596,7 @@ class MachineManager(QObject):
|
|||
def globalVariantName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
variant = self._global_container_stack.variant
|
||||
if variant and not isinstance(variant, type(self._empty_variant_container)):
|
||||
if variant and not isinstance(variant, type(empty_variant_container)):
|
||||
return variant.getName()
|
||||
return ""
|
||||
|
||||
|
@ -781,7 +784,7 @@ class MachineManager(QObject):
|
|||
if not stack.isEnabled:
|
||||
continue
|
||||
material_container = stack.material
|
||||
if material_container == self._empty_material_container:
|
||||
if material_container == empty_material_container:
|
||||
continue
|
||||
if material_container.getMetaDataEntry("buildplate_compatible"):
|
||||
buildplate_compatible = buildplate_compatible and material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName]
|
||||
|
@ -803,7 +806,7 @@ class MachineManager(QObject):
|
|||
extruder_stacks = self._global_container_stack.extruders.values()
|
||||
for stack in extruder_stacks:
|
||||
material_container = stack.material
|
||||
if material_container == self._empty_material_container:
|
||||
if material_container == empty_material_container:
|
||||
continue
|
||||
buildplate_compatible = material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_compatible") else True
|
||||
buildplate_usable = material_container.getMetaDataEntry("buildplate_recommended")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_recommended") else True
|
||||
|
@ -873,7 +876,7 @@ class MachineManager(QObject):
|
|||
extruder_manager = self._application.getExtruderManager()
|
||||
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container:
|
||||
if not self._global_container_stack or definition_changes_container == empty_definition_changes_container:
|
||||
return
|
||||
|
||||
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||
|
@ -1072,7 +1075,7 @@ class MachineManager(QObject):
|
|||
for stack in active_stacks:
|
||||
variant_container = stack.variant
|
||||
position = stack.getMetaDataEntry("position")
|
||||
if variant_container and variant_container != self._empty_variant_container:
|
||||
if variant_container and variant_container != empty_variant_container:
|
||||
result[position] = variant_container.getName()
|
||||
|
||||
return result
|
||||
|
@ -1086,11 +1089,11 @@ class MachineManager(QObject):
|
|||
return
|
||||
self._current_quality_group = None
|
||||
self._current_quality_changes_group = None
|
||||
self._global_container_stack.quality = self._empty_quality_container
|
||||
self._global_container_stack.qualityChanges = self._empty_quality_changes_container
|
||||
self._global_container_stack.quality = empty_quality_container
|
||||
self._global_container_stack.qualityChanges = empty_quality_changes_container
|
||||
for extruder in self._global_container_stack.extruders.values():
|
||||
extruder.quality = self._empty_quality_container
|
||||
extruder.qualityChanges = self._empty_quality_changes_container
|
||||
extruder.quality = empty_quality_container
|
||||
extruder.qualityChanges = empty_quality_changes_container
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
@ -1115,13 +1118,13 @@ class MachineManager(QObject):
|
|||
# Set quality and quality_changes for the GlobalStack
|
||||
self._global_container_stack.quality = quality_group.node_for_global.getContainer()
|
||||
if empty_quality_changes:
|
||||
self._global_container_stack.qualityChanges = self._empty_quality_changes_container
|
||||
self._global_container_stack.qualityChanges = empty_quality_changes_container
|
||||
|
||||
# Set quality and quality_changes for each ExtruderStack
|
||||
for position, node in quality_group.nodes_for_extruders.items():
|
||||
self._global_container_stack.extruders[str(position)].quality = node.getContainer()
|
||||
if empty_quality_changes:
|
||||
self._global_container_stack.extruders[str(position)].qualityChanges = self._empty_quality_changes_container
|
||||
self._global_container_stack.extruders[str(position)].qualityChanges = empty_quality_changes_container
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
@ -1147,8 +1150,8 @@ class MachineManager(QObject):
|
|||
if quality_group is None:
|
||||
self._fixQualityChangesGroupToNotSupported(quality_changes_group)
|
||||
|
||||
quality_changes_container = self._empty_quality_changes_container
|
||||
quality_container = self._empty_quality_container # type: Optional[InstanceContainer]
|
||||
quality_changes_container = empty_quality_changes_container
|
||||
quality_container = empty_quality_container # type: Optional[InstanceContainer]
|
||||
if quality_changes_group.node_for_global and quality_changes_group.node_for_global.getContainer():
|
||||
quality_changes_container = cast(InstanceContainer, quality_changes_group.node_for_global.getContainer())
|
||||
if quality_group is not None and quality_group.node_for_global and quality_group.node_for_global.getContainer():
|
||||
|
@ -1163,8 +1166,8 @@ class MachineManager(QObject):
|
|||
if quality_group is not None:
|
||||
quality_node = quality_group.nodes_for_extruders.get(position)
|
||||
|
||||
quality_changes_container = self._empty_quality_changes_container
|
||||
quality_container = self._empty_quality_container
|
||||
quality_changes_container = empty_quality_changes_container
|
||||
quality_container = empty_quality_container
|
||||
if quality_changes_node and quality_changes_node.getContainer():
|
||||
quality_changes_container = cast(InstanceContainer, quality_changes_node.getContainer())
|
||||
if quality_node and quality_node.getContainer():
|
||||
|
@ -1198,7 +1201,7 @@ class MachineManager(QObject):
|
|||
self._global_container_stack.extruders[position].material = container_node.getContainer()
|
||||
root_material_id = container_node.getMetaDataEntry("base_file", None)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
self._global_container_stack.extruders[position].material = empty_material_container
|
||||
root_material_id = None
|
||||
# The _current_root_material_id is used in the MaterialMenu to see which material is selected
|
||||
if root_material_id != self._current_root_material_id[position]:
|
||||
|
@ -1273,14 +1276,10 @@ class MachineManager(QObject):
|
|||
|
||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_nozzle_name = None
|
||||
if extruder.variant.getId() != self._empty_variant_container.getId():
|
||||
if extruder.variant.getId() != empty_variant_container.getId():
|
||||
current_nozzle_name = extruder.variant.getMetaDataEntry("name")
|
||||
|
||||
from UM.Settings.Interfaces import PropertyEvaluationContext
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
context = PropertyEvaluationContext(extruder)
|
||||
context.context["evaluate_from_container_index"] = _ContainerIndexes.DefinitionChanges
|
||||
material_diameter = extruder.getProperty("material_diameter", "value", context)
|
||||
material_diameter = extruder.getCompatibleMaterialDiameter()
|
||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||
self._global_container_stack.definition,
|
||||
current_nozzle_name,
|
||||
|
@ -1348,12 +1347,12 @@ class MachineManager(QObject):
|
|||
if variant_container_node:
|
||||
self._setVariantNode(position, variant_container_node)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].variant = self._empty_variant_container
|
||||
self._global_container_stack.extruders[position].variant = empty_variant_container
|
||||
|
||||
if material_container_node:
|
||||
self._setMaterial(position, material_container_node)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
self._global_container_stack.extruders[position].material = empty_material_container
|
||||
self.updateMaterialWithVariant(position)
|
||||
|
||||
if configuration.buildplateConfiguration is not None:
|
||||
|
@ -1361,9 +1360,9 @@ class MachineManager(QObject):
|
|||
if global_variant_container_node:
|
||||
self._setGlobalVariant(global_variant_container_node)
|
||||
else:
|
||||
self._global_container_stack.variant = self._empty_variant_container
|
||||
self._global_container_stack.variant = empty_variant_container
|
||||
else:
|
||||
self._global_container_stack.variant = self._empty_variant_container
|
||||
self._global_container_stack.variant = empty_variant_container
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
|
@ -1415,7 +1414,7 @@ class MachineManager(QObject):
|
|||
position = str(position)
|
||||
extruder_stack = self._global_container_stack.extruders[position]
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
material_diameter = extruder_stack.approximateMaterialDiameter
|
||||
material_diameter = extruder_stack.getApproximateMaterialDiameter()
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
self.setMaterial(position, material_node)
|
||||
|
@ -1481,7 +1480,7 @@ class MachineManager(QObject):
|
|||
# This is not changing the quality for the active machine !!!!!!!!
|
||||
global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
for extruder_nr, extruder_stack in global_stack.extruders.items():
|
||||
quality_container = self._empty_quality_container
|
||||
quality_container = empty_quality_container
|
||||
if extruder_nr in quality_group.nodes_for_extruders:
|
||||
container = quality_group.nodes_for_extruders[extruder_nr].getContainer()
|
||||
quality_container = container if container is not None else quality_container
|
||||
|
@ -1525,7 +1524,7 @@ class MachineManager(QObject):
|
|||
|
||||
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
||||
def activeQualityOrQualityChangesName(self) -> str:
|
||||
name = self._empty_quality_container.getName()
|
||||
name = empty_quality_container.getName()
|
||||
if self._current_quality_changes_group:
|
||||
name = self._current_quality_changes_group.name
|
||||
elif self._current_quality_group:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue