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:
Diego Prado Gesto 2018-10-29 11:10:25 +01:00
commit 5de367bcc4
107 changed files with 15797 additions and 14749 deletions

View file

@ -45,7 +45,7 @@ class Account(QObject):
CALLBACK_PORT=self._callback_port, CALLBACK_PORT=self._callback_port,
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
CLIENT_ID="um---------------ultimaker_cura_drive_plugin", 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_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root) AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root)

View file

@ -4,7 +4,7 @@
import os import os
import sys import sys
import time import time
from typing import cast, TYPE_CHECKING, Optional from typing import cast, TYPE_CHECKING, Optional, Callable
import numpy import numpy
@ -13,6 +13,7 @@ from PyQt5.QtGui import QColor, QIcon
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
from UM.Application import Application
from UM.PluginError import PluginNotFoundError from UM.PluginError import PluginNotFoundError
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera from UM.Scene.Camera import Camera
@ -114,12 +115,13 @@ from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
from cura.ObjectsModel import ObjectsModel from cura.ObjectsModel import ObjectsModel
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from UM.Decorators import override
if TYPE_CHECKING: if TYPE_CHECKING:
from cura.Machines.MaterialManager import MaterialManager from cura.Machines.MaterialManager import MaterialManager
from cura.Machines.QualityManager import QualityManager from cura.Machines.QualityManager import QualityManager
from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
from cura.Settings.GlobalStack import GlobalStack
numpy.seterr(all = "ignore") numpy.seterr(all = "ignore")
@ -166,6 +168,8 @@ class CuraApplication(QtApplication):
self.default_theme = "cura-light" self.default_theme = "cura-light"
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
self._boot_loading_time = time.time() self._boot_loading_time = time.time()
self._on_exit_callback_manager = OnExitCallbackManager(self) 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 = MachineActionManager.MachineActionManager(self)
self._machine_action_manager.initialize() self._machine_action_manager.initialize()
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
def __sendCommandToSingleInstance(self): def __sendCommandToSingleInstance(self):
self._single_instance = SingleInstance(self, self._files_to_open) 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. # Runs preparations that needs to be done before the starting process.
def startSplashWindowPhase(self): def startSplashWindowPhase(self) -> None:
super().startSplashWindowPhase() super().startSplashWindowPhase()
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) 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()) self._qml_engine.addImageProvider("print_job_preview", PrintJobPreviewImageProvider.PrintJobPreviewImageProvider())
@pyqtProperty(bool) @pyqtProperty(bool)
def needToShowUserAgreement(self): def needToShowUserAgreement(self) -> bool:
return self._need_to_show_user_agreement 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 self._need_to_show_user_agreement = set_value
# DO NOT call this function to close the application, use checkAndExitApplication() instead which will perform # 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. # pre-exit checks such as checking for in-progress USB printing, etc.
def closeApplication(self): def closeApplication(self) -> None:
Logger.log("i", "Close application") Logger.log("i", "Close application")
main_window = self.getMainWindow() main_window = self.getMainWindow()
if main_window is not None: if main_window is not None:
@ -560,11 +562,11 @@ class CuraApplication(QtApplication):
showConfirmExitDialog = pyqtSignal(str, arguments = ["message"]) showConfirmExitDialog = pyqtSignal(str, arguments = ["message"])
def setConfirmExitDialogCallback(self, callback): def setConfirmExitDialogCallback(self, callback: Callable) -> None:
self._confirm_exit_dialog_callback = callback self._confirm_exit_dialog_callback = callback
@pyqtSlot(bool) @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) self._confirm_exit_dialog_callback(yes_or_no)
## Signal to connect preferences action in QML ## Signal to connect preferences action in QML
@ -572,9 +574,17 @@ class CuraApplication(QtApplication):
## Show the preferences window ## Show the preferences window
@pyqtSlot() @pyqtSlot()
def showPreferences(self): def showPreferences(self) -> None:
self.showPreferencesWindow.emit() 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 ## A reusable dialogbox
# #
showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"])
@ -586,7 +596,7 @@ class CuraApplication(QtApplication):
showDiscardOrKeepProfileChanges = pyqtSignal() showDiscardOrKeepProfileChanges = pyqtSignal()
def discardOrKeepProfileChanges(self): def discardOrKeepProfileChanges(self) -> bool:
has_user_interaction = False has_user_interaction = False
choice = self.getPreferences().getValue("cura/choice_on_profile_override") choice = self.getPreferences().getValue("cura/choice_on_profile_override")
if choice == "always_discard": if choice == "always_discard":
@ -602,7 +612,7 @@ class CuraApplication(QtApplication):
return has_user_interaction return has_user_interaction
@pyqtSlot(str) @pyqtSlot(str)
def discardOrKeepProfileChangesClosed(self, option): def discardOrKeepProfileChangesClosed(self, option: str) -> None:
global_stack = self.getGlobalContainerStack() global_stack = self.getGlobalContainerStack()
if option == "discard": if option == "discard":
for extruder in global_stack.extruders.values(): for extruder in global_stack.extruders.values():
@ -689,7 +699,7 @@ class CuraApplication(QtApplication):
self._quality_manager.initialize() self._quality_manager.initialize()
Logger.log("i", "Initializing machine manager") Logger.log("i", "Initializing machine manager")
self._machine_manager = MachineManager(self) self._machine_manager = MachineManager(self, parent = self)
Logger.log("i", "Initializing container manager") Logger.log("i", "Initializing container manager")
self._container_manager = ContainerManager(self) self._container_manager = ContainerManager(self)
@ -947,6 +957,9 @@ class CuraApplication(QtApplication):
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel") qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
from cura.PrinterOutput.CameraView import CameraView
qmlRegisterType(CameraView, "Cura", 1, 0, "CameraView")
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
"QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel) "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)
qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0,

View file

@ -1,13 +1,13 @@
# Copyright (c) 2016 Ultimaker B.V. # Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
from UM.Logger import Logger
from UM.PluginObject import PluginObject from UM.PluginObject import PluginObject
from UM.PluginRegistry import PluginRegistry 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 ## 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. ## Create a new Machine action.
# \param key unique key of the machine action # \param key unique key of the machine action
# \param label Human readable label used to identify 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__() super().__init__()
self._key = key self._key = key
self._label = label self._label = label
@ -30,14 +30,14 @@ class MachineAction(QObject, PluginObject):
labelChanged = pyqtSignal() labelChanged = pyqtSignal()
onFinished = pyqtSignal() onFinished = pyqtSignal()
def getKey(self): def getKey(self) -> str:
return self._key return self._key
@pyqtProperty(str, notify = labelChanged) @pyqtProperty(str, notify = labelChanged)
def label(self): def label(self) -> str:
return self._label return self._label
def setLabel(self, label): def setLabel(self, label: str) -> None:
if self._label != label: if self._label != label:
self._label = label self._label = label
self.labelChanged.emit() self.labelChanged.emit()
@ -46,29 +46,35 @@ class MachineAction(QObject, PluginObject):
# This should not be re-implemented by child classes, instead re-implement _reset. # This should not be re-implemented by child classes, instead re-implement _reset.
# /sa _reset # /sa _reset
@pyqtSlot() @pyqtSlot()
def reset(self): def reset(self) -> None:
self._finished = False self._finished = False
self._reset() self._reset()
## Protected implementation of reset. ## Protected implementation of reset.
# /sa reset() # /sa reset()
def _reset(self): def _reset(self) -> None:
pass pass
@pyqtSlot() @pyqtSlot()
def setFinished(self): def setFinished(self) -> None:
self._finished = True self._finished = True
self._reset() self._reset()
self.onFinished.emit() self.onFinished.emit()
@pyqtProperty(bool, notify = onFinished) @pyqtProperty(bool, notify = onFinished)
def finished(self): def finished(self) -> bool:
return self._finished return self._finished
## Protected helper to create a view object based on provided QML. ## Protected helper to create a view object based on provided QML.
def _createViewFromQML(self): def _createViewFromQML(self) -> None:
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url) plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
self._view = Application.getInstance().createQmlComponent(path, {"manager": self}) 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) @pyqtProperty(QObject, constant = True)
def displayItem(self): def displayItem(self):

View file

@ -1,12 +1,18 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # 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 PyQt5.QtCore import QObject
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type 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 ## Raised when trying to add an unknown machine action as a required action
@ -20,46 +26,54 @@ class NotUniqueMachineActionError(Exception):
class MachineActionManager(QObject): class MachineActionManager(QObject):
def __init__(self, application, parent = None): def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
super().__init__(parent) super().__init__(parent = parent)
self._application = application self._application = application
self._container_registry = self._application.getContainerRegistry()
self._machine_actions = {} # Dict of all known machine actions # Keeps track of which machines have already been processed so we don't do that again.
self._required_actions = {} # Dict of all required actions by definition ID self._definition_ids_with_default_actions_added = set() # type: Set[str]
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 # 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): def initialize(self):
container_registry = self._application.getContainerRegistry()
# Add machine_action as plugin type # Add machine_action as plugin type
PluginRegistry.addType("machine_action", self.addMachineAction) PluginRegistry.addType("machine_action", self.addMachineAction)
# Ensure that all containers that were registered before creation of this registry are also handled. # Adds all default machine actions that are defined in the machine definition for the given machine.
# This should not have any effect, but it makes it safer if we ever refactor the order of things. def addDefaultMachineActions(self, global_stack: "GlobalStack") -> None:
for container in container_registry.findDefinitionContainers(): definition_id = global_stack.definition.getId()
self._onContainerAdded(container)
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): supported_actions = global_stack.getMetaDataEntry("supported_actions", [])
## Ensure that the actions are added to this manager for action_key in supported_actions:
if isinstance(container, DefinitionContainer): self.addSupportedAction(definition_id, action_key)
supported_actions = container.getMetaDataEntry("supported_actions", [])
for action in supported_actions:
self.addSupportedAction(container.getId(), action)
required_actions = container.getMetaDataEntry("required_actions", []) required_actions = global_stack.getMetaDataEntry("required_actions", [])
for action in required_actions: for action_key in required_actions:
self.addRequiredAction(container.getId(), action) self.addRequiredAction(definition_id, action_key)
first_start_actions = container.getMetaDataEntry("first_start_actions", []) first_start_actions = global_stack.getMetaDataEntry("first_start_actions", [])
for action in first_start_actions: for action_key in first_start_actions:
self.addFirstStartAction(container.getId(), action) 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 ## Add a required action to a machine
# Raises an exception when the action is not recognised. # 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 action_key in self._machine_actions:
if definition_id in self._required_actions: if definition_id in self._required_actions:
if self._machine_actions[action_key] not in self._required_actions[definition_id]: 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)) raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
## Add a supported action to a machine. ## 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 action_key in self._machine_actions:
if definition_id in self._supported_actions: if definition_id in self._supported_actions:
if self._machine_actions[action_key] not in self._supported_actions[definition_id]: if self._machine_actions[action_key] not in self._supported_actions[definition_id]:
@ -81,12 +95,9 @@ class MachineActionManager(QObject):
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id) 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. ## 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 action_key in self._machine_actions:
if definition_id in self._first_start_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: else:
self._first_start_actions[definition_id] = [self._machine_actions[action_key]] self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
@ -95,7 +106,7 @@ class MachineActionManager(QObject):
## Add a (unique) MachineAction ## Add a (unique) MachineAction
# if the Key of the action is not unique, an exception is raised. # 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: if action.getKey() not in self._machine_actions:
self._machine_actions[action.getKey()] = action self._machine_actions[action.getKey()] = action
else: else:
@ -105,7 +116,7 @@ class MachineActionManager(QObject):
# \param definition_id The ID of the definition you want the supported actions of # \param definition_id The ID of the definition you want the supported actions of
# \returns set of supported actions. # \returns set of supported actions.
@pyqtSlot(str, result = "QVariantList") @pyqtSlot(str, result = "QVariantList")
def getSupportedActions(self, definition_id): def getSupportedActions(self, definition_id: str) -> List["MachineAction"]:
if definition_id in self._supported_actions: if definition_id in self._supported_actions:
return list(self._supported_actions[definition_id]) return list(self._supported_actions[definition_id])
else: else:
@ -114,11 +125,11 @@ class MachineActionManager(QObject):
## Get all actions required by given machine ## Get all actions required by given machine
# \param definition_id The ID of the definition you want the required actions of # \param definition_id The ID of the definition you want the required actions of
# \returns set of required actions. # \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: if definition_id in self._required_actions:
return self._required_actions[definition_id] return self._required_actions[definition_id]
else: else:
return set() return list()
## Get all actions that need to be performed upon first start of a given machine. ## 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 # 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. # \param definition_id The ID of the definition that you want to get the "on added" actions for.
# \returns List of actions. # \returns List of actions.
@pyqtSlot(str, result="QVariantList") @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: if definition_id in self._first_start_actions:
return self._first_start_actions[definition_id] return self._first_start_actions[definition_id]
else: else:
@ -134,7 +145,7 @@ class MachineActionManager(QObject):
## Remove Machine action from manager ## Remove Machine action from manager
# \param action to remove # \param action to remove
def removeMachineAction(self, action): def removeMachineAction(self, action: "MachineAction") -> None:
try: try:
del self._machine_actions[action.getKey()] del self._machine_actions[action.getKey()]
except KeyError: except KeyError:
@ -143,7 +154,7 @@ class MachineActionManager(QObject):
## Get MachineAction by key ## Get MachineAction by key
# \param key String of key to select # \param key String of key to select
# \return Machine action if found, None otherwise # \return Machine action if found, None otherwise
def getMachineAction(self, key): def getMachineAction(self, key: str) -> Optional["MachineAction"]:
if key in self._machine_actions: if key in self._machine_actions:
return self._machine_actions[key] return self._machine_actions[key]
else: else:

View file

@ -365,7 +365,7 @@ class MaterialManager(QObject):
nozzle_name = None nozzle_name = None
if extruder_stack.variant.getId() != "empty_variant": if extruder_stack.variant.getId() != "empty_variant":
nozzle_name = extruder_stack.variant.getName() 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. # Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
return self.getAvailableMaterials(machine.definition, nozzle_name, buildplate_name, diameter) return self.getAvailableMaterials(machine.definition, nozzle_name, buildplate_name, diameter)
@ -478,12 +478,22 @@ class MaterialManager(QObject):
buildplate_name = global_stack.getBuildplateName() buildplate_name = global_stack.getBuildplateName()
machine_definition = global_stack.definition 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)): # The extruder-compatible material diameter in the extruder definition may not be the correct value because
# At this point the extruder_definition is not None # the user can change it in the definition_changes container.
material_diameter = extruder_definition.getProperty("material_diameter", "value") 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): if isinstance(material_diameter, SettingFunction):
material_diameter = material_diameter(global_stack) material_diameter = material_diameter(global_stack)
approximate_material_diameter = str(round(material_diameter)) approximate_material_diameter = str(round(material_diameter))

View file

@ -64,9 +64,11 @@ class BaseMaterialsModel(ListModel):
if self._extruder_stack is not None: if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.disconnect(self._update) 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)) self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
if self._extruder_stack is not None: if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.connect(self._update) self._extruder_stack.pyqtContainersChanged.connect(self._update)
self._extruder_stack.approximateMaterialDiameterChanged.connect(self._update)
# Force update the model when the extruder stack changes # Force update the model when the extruder stack changes
self._update() self._update()

View file

@ -6,7 +6,7 @@ import math
import os import os
import unicodedata import unicodedata
import re # To create abbreviations for printer names. 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 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.i18n import i18nCatalog
from UM.MimeTypeDatabase import MimeTypeDatabase from UM.MimeTypeDatabase import MimeTypeDatabase
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name ## A class for processing and the print times per build plate 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.
# #
# This class also mangles the current machine name and the filename of the first loaded mesh into a 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. # This job name is requested by the JobSpecs qml file.
class PrintInformation(QObject): class PrintInformation(QObject):
class SlicePass:
CurrentSettings = 1
LowQualitySettings = 2
HighQualitySettings = 3
class SliceReason: UNTITLED_JOB_NAME = "Untitled"
SceneChanged = 1
SettingChanged = 2
ActiveMachineChanged = 3
Other = 4
def __init__(self, application, parent = None): def __init__(self, application: "CuraApplication", parent = None) -> None:
super().__init__(parent) super().__init__(parent)
self._application = application self._application = application
self.UNTITLED_JOB_NAME = "Untitled"
self.initializeCuraMessagePrintTimeProperties() self.initializeCuraMessagePrintTimeProperties()
self._material_lengths = {} # indexed by build plate number # Indexed by build plate number
self._material_weights = {} self._material_lengths = {} # type: Dict[int, List[float]]
self._material_costs = {} self._material_weights = {} # type: Dict[int, List[float]]
self._material_names = {} self._material_costs = {} # type: Dict[int, List[float]]
self._material_names = {} # type: Dict[int, List[str]]
self._pre_sliced = False self._pre_sliced = False
self._backend = self._application.getBackend() self._backend = self._application.getBackend()
if self._backend: if self._backend:
self._backend.printDurationMessage.connect(self._onPrintDurationMessage) self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
self._is_user_specified_job_name = False self._is_user_specified_job_name = False
@ -72,29 +58,25 @@ class PrintInformation(QObject):
self._abbr_machine = "" self._abbr_machine = ""
self._job_name = "" self._job_name = ""
self._active_build_plate = 0 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() 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._updateJobName)
self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation) self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation)
self._application.fileLoaded.connect(self.setBaseName) self._application.fileLoaded.connect(self.setBaseName)
self._application.workspaceLoaded.connect(self.setProjectName) 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.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._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 def initializeCuraMessagePrintTimeProperties(self) -> None:
# and time for each feature self._current_print_time = {} # type: Dict[int, Duration]
def initializeCuraMessagePrintTimeProperties(self):
self._current_print_time = {} # Duration(None, self)
self._print_time_message_translations = { self._print_time_message_translations = {
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"), "inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
@ -110,17 +92,17 @@ class PrintInformation(QObject):
"none": catalog.i18nc("@tooltip", "Other") "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 # 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(): 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): def _initVariablesByBuildPlate(self, build_plate_number: int) -> None:
if build_plate_number not in self._print_time_message_values: if build_plate_number not in self._print_times_per_feature:
self._initPrintTimeMessageValues(build_plate_number) self._initPrintTimesPerFeature(build_plate_number)
if self._active_build_plate not in self._material_lengths: if self._active_build_plate not in self._material_lengths:
self._material_lengths[self._active_build_plate] = [] self._material_lengths[self._active_build_plate] = []
if self._active_build_plate not in self._material_weights: 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: if self._active_build_plate not in self._material_names:
self._material_names[self._active_build_plate] = [] self._material_names[self._active_build_plate] = []
if self._active_build_plate not in self._current_print_time: 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() currentPrintTimeChanged = pyqtSignal()
preSlicedChanged = pyqtSignal() preSlicedChanged = pyqtSignal()
@pyqtProperty(bool, notify=preSlicedChanged) @pyqtProperty(bool, notify=preSlicedChanged)
def preSliced(self): def preSliced(self) -> bool:
return self._pre_sliced return self._pre_sliced
def setPreSliced(self, pre_sliced): def setPreSliced(self, pre_sliced: bool) -> None:
if self._pre_sliced != pre_sliced:
self._pre_sliced = pre_sliced self._pre_sliced = pre_sliced
self._updateJobName() self._updateJobName()
self.preSlicedChanged.emit() self.preSlicedChanged.emit()
@pyqtProperty(Duration, notify = currentPrintTimeChanged) @pyqtProperty(Duration, notify = currentPrintTimeChanged)
def currentPrintTime(self): def currentPrintTime(self) -> Duration:
return self._current_print_time[self._active_build_plate] return self._current_print_time[self._active_build_plate]
materialLengthsChanged = pyqtSignal() materialLengthsChanged = pyqtSignal()
@ -173,36 +156,41 @@ class PrintInformation(QObject):
def materialNames(self): def materialNames(self):
return self._material_names[self._active_build_plate] return self._material_names[self._active_build_plate]
def printTimes(self): # Get all print times (by feature) of the active buildplate.
return self._print_time_message_values[self._active_build_plate] 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): 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_time) self._updateTotalPrintTimePerFeature(build_plate_number, print_times_per_feature)
self.currentPrintTimeChanged.emit() self.currentPrintTimeChanged.emit()
self._material_amounts = material_amounts self._material_amounts = material_amounts
self._calculateInformation(build_plate_number) 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 total_estimated_time = 0
if build_plate_number not in self._print_time_message_values: if build_plate_number not in self._print_times_per_feature:
self._initPrintTimeMessageValues(build_plate_number) 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. 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") Logger.log("w", "Received NaN for print duration message")
continue continue
total_estimated_time += time 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: 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] = Duration(None, self)
self._current_print_time[build_plate_number].setDuration(total_estimated_time) 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() global_stack = self._application.getGlobalContainerStack()
if global_stack is None: if global_stack is None:
return return
@ -215,27 +203,32 @@ class PrintInformation(QObject):
material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings")) material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings"))
extruder_stacks = global_stack.extruders 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) index = int(position)
if index >= len(self._material_amounts): if index >= len(self._material_amounts):
continue continue
amount = self._material_amounts[index] 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 # 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. # list comprehension filtering to solve this for us.
density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0) 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 radius = extruder_stack.getProperty("material_diameter", "value") / 2
weight = float(amount) * float(density) / 1000 weight = float(amount) * float(density) / 1000
cost = 0 cost = 0.
material_name = catalog.i18nc("@label unknown material", "Unknown")
if material:
material_guid = material.getMetaDataEntry("GUID") material_guid = material.getMetaDataEntry("GUID")
material_name = material.getName() material_name = material.getName()
if material_guid in material_preference_values: if material_guid in material_preference_values:
material_values = material_preference_values[material_guid] material_values = material_preference_values[material_guid]
weight_per_spool = float(material_values["spool_weight"] if material_values and "spool_weight" in material_values else 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) cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0)
if weight_per_spool != 0: if weight_per_spool != 0:
@ -248,6 +241,7 @@ class PrintInformation(QObject):
length = round((amount / (math.pi * radius ** 2)) / 1000, 2) length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
else: else:
length = 0 length = 0
self._material_weights[build_plate_number].append(weight) self._material_weights[build_plate_number].append(weight)
self._material_lengths[build_plate_number].append(length) self._material_lengths[build_plate_number].append(length)
self._material_costs[build_plate_number].append(cost) self._material_costs[build_plate_number].append(cost)
@ -258,20 +252,20 @@ class PrintInformation(QObject):
self.materialCostsChanged.emit() self.materialCostsChanged.emit()
self.materialNamesChanged.emit() self.materialNamesChanged.emit()
def _onPreferencesChanged(self, preference): def _onPreferencesChanged(self, preference: str) -> None:
if preference != "cura/material_settings": if preference != "cura/material_settings":
return return
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1): for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
self._calculateInformation(build_plate_number) self._calculateInformation(build_plate_number)
def _onActiveBuildPlateChanged(self): def _onActiveBuildPlateChanged(self) -> None:
new_active_build_plate = self._multi_build_plate_model.activeBuildPlate new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
if new_active_build_plate != self._active_build_plate: if new_active_build_plate != self._active_build_plate:
self._active_build_plate = new_active_build_plate self._active_build_plate = new_active_build_plate
self._updateJobName() self._updateJobName()
self._initVariablesWithBuildPlate(self._active_build_plate) self._initVariablesByBuildPlate(self._active_build_plate)
self.materialLengthsChanged.emit() self.materialLengthsChanged.emit()
self.materialWeightsChanged.emit() self.materialWeightsChanged.emit()
@ -279,14 +273,14 @@ class PrintInformation(QObject):
self.materialNamesChanged.emit() self.materialNamesChanged.emit()
self.currentPrintTimeChanged.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): for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
self._calculateInformation(build_plate_number) 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 # 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 # prefix can be added to the manually added name, not the old base name
@pyqtSlot(str, bool) @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._is_user_specified_job_name = is_user_specified_job_name
self._job_name = name self._job_name = name
self._base_name = name.replace(self._abbr_machine + "_", "") self._base_name = name.replace(self._abbr_machine + "_", "")
@ -300,7 +294,7 @@ class PrintInformation(QObject):
def jobName(self): def jobName(self):
return self._job_name return self._job_name
def _updateJobName(self): def _updateJobName(self) -> None:
if self._base_name == "": if self._base_name == "":
self._job_name = self.UNTITLED_JOB_NAME self._job_name = self.UNTITLED_JOB_NAME
self._is_user_specified_job_name = False self._is_user_specified_job_name = False
@ -335,12 +329,12 @@ class PrintInformation(QObject):
self.jobNameChanged.emit() self.jobNameChanged.emit()
@pyqtSlot(str) @pyqtSlot(str)
def setProjectName(self, name): def setProjectName(self, name: str) -> None:
self.setBaseName(name, is_project_file = True) self.setBaseName(name, is_project_file = True)
baseNameChanged = pyqtSignal() 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 self._is_user_specified_job_name = False
# Ensure that we don't use entire path but only filename # 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 ## Created an acronym-like abbreviated machine name from the currently
# active machine name. # active machine name.
# Called each time the global stack is switched. # Called each time the global stack is switched.
def _defineAbbreviatedMachineName(self): def _defineAbbreviatedMachineName(self) -> None:
global_container_stack = self._application.getGlobalContainerStack() global_container_stack = self._application.getGlobalContainerStack()
if not global_container_stack: if not global_container_stack:
self._abbr_machine = "" self._abbr_machine = ""
@ -408,15 +402,15 @@ class PrintInformation(QObject):
self._abbr_machine = abbr_machine self._abbr_machine = abbr_machine
## Utility method that strips accents from characters (eg: â -> a) ## Utility method that strips accents from characters (eg: â -> a)
def _stripAccents(self, str): def _stripAccents(self, to_strip: str) -> str:
return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn') return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
@pyqtSlot(result = "QVariantMap") @pyqtSlot(result = "QVariantMap")
def getFeaturePrintTimes(self): def getFeaturePrintTimes(self):
result = {} result = {}
if self._active_build_plate not in self._print_time_message_values: if self._active_build_plate not in self._print_times_per_feature:
self._initPrintTimeMessageValues(self._active_build_plate) self._initPrintTimesPerFeature(self._active_build_plate)
for feature, time in self._print_time_message_values[self._active_build_plate].items(): for feature, time in self._print_times_per_feature[self._active_build_plate].items():
if feature in self._print_time_message_translations: if feature in self._print_time_message_translations:
result[self._print_time_message_translations[feature]] = time result[self._print_time_message_translations[feature]] = time
else: else:
@ -424,22 +418,22 @@ class PrintInformation(QObject):
return result return result
# Simulate message with zero time duration # 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: if build_plate is None:
build_plate = self._active_build_plate build_plate = self._active_build_plate
# Construct the 0-time message # Construct the 0-time message
temp_message = {} temp_message = {}
if build_plate not in self._print_time_message_values: if build_plate not in self._print_times_per_feature:
self._print_time_message_values[build_plate] = {} self._print_times_per_feature[build_plate] = {}
for key in self._print_time_message_values[build_plate].keys(): for key in self._print_times_per_feature[build_plate].keys():
temp_message[key] = 0 temp_message[key] = 0
temp_material_amounts = [0] temp_material_amounts = [0.]
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts) self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
## Listen to scene changes to check if we need to reset the print information ## 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 # Ignore any changes that are not related to sliceable objects
if not isinstance(scene_node, SceneNode)\ if not isinstance(scene_node, SceneNode)\
or not scene_node.callDecoration("isSliceable")\ or not scene_node.callDecoration("isSliceable")\

View 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)

View file

@ -16,7 +16,6 @@ class NetworkCamera(QObject):
self._image_request = None self._image_request = None
self._image_reply = None self._image_reply = None
self._image = QImage() self._image = QImage()
self._image_id = 0
self._target = target self._target = target
self._started = False self._started = False
@ -33,15 +32,9 @@ class NetworkCamera(QObject):
if restart_required: if restart_required:
self.start() self.start()
@pyqtProperty(QUrl, notify=newImage) @pyqtProperty(QImage, notify=newImage)
def latestImage(self): def latestImage(self):
self._image_id += 1 return self._image
# 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)
@pyqtSlot() @pyqtSlot()
def start(self): def start(self):

View file

@ -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. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
@ -12,7 +12,6 @@ if TYPE_CHECKING:
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
class PrintJobOutputModel(QObject): class PrintJobOutputModel(QObject):
stateChanged = pyqtSignal() stateChanged = pyqtSignal()
timeTotalChanged = pyqtSignal() timeTotalChanged = pyqtSignal()

View file

@ -50,7 +50,7 @@ class PrinterOutputModel(QObject):
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
self._extruders] self._extruders]
self._camera = None self._camera = None # type: Optional[NetworkCamera]
@pyqtProperty(str, constant = True) @pyqtProperty(str, constant = True)
def firmwareVersion(self) -> str: def firmwareVersion(self) -> str:

View file

@ -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 from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
class BlockSlicingDecorator(SceneNodeDecorator): class BlockSlicingDecorator(SceneNodeDecorator):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
def isBlockSlicing(self): def isBlockSlicing(self) -> bool:
return True return True

View file

@ -145,13 +145,11 @@ class CuraContainerStack(ContainerStack):
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None: def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
self.replaceContainer(_ContainerIndexes.Definition, new_definition) self.replaceContainer(_ContainerIndexes.Definition, new_definition)
## Get the definition container. def getDefinition(self) -> "DefinitionContainer":
#
# \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:
return cast(DefinitionContainer, self._containers[_ContainerIndexes.Definition]) return cast(DefinitionContainer, self._containers[_ContainerIndexes.Definition])
definition = pyqtProperty(QObject, fget = getDefinition, fset = setDefinition, notify = pyqtContainersChanged)
@override(ContainerStack) @override(ContainerStack)
def getBottom(self) -> "DefinitionContainer": def getBottom(self) -> "DefinitionContainer":
return self.definition return self.definition

View file

@ -129,7 +129,7 @@ class CuraStackBuilder:
# get material container for extruders # get material container for extruders
material_container = application.empty_material_container 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) extruder_definition = extruder_definition)
if material_node and material_node.getContainer(): if material_node and material_node.getContainer():
material_container = material_node.getContainer() material_container = material_node.getContainer()
@ -145,7 +145,6 @@ class CuraStackBuilder:
quality_container = application.empty_quality_container quality_container = application.empty_quality_container
) )
new_extruder.setNextStack(global_stack) new_extruder.setNextStack(global_stack)
global_stack.addExtruder(new_extruder)
registry.addContainer(new_extruder) registry.addContainer(new_extruder)

View file

@ -374,8 +374,6 @@ class ExtruderManager(QObject):
extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0] extruder_definition = container_registry.findDefinitionContainers(id = expected_extruder_definition_0_id)[0]
extruder_stack_0.definition = extruder_definition extruder_stack_0.definition = extruder_definition
extruder_stack_0.setNextStack(global_stack)
## Get all extruder values for a certain setting. ## Get all extruder values for a certain setting.
# #
# This is exposed to qml for display purposes # This is exposed to qml for display purposes

View file

@ -65,16 +65,33 @@ class ExtruderStack(CuraContainerStack):
def getLoadingPriority(cls) -> int: def getLoadingPriority(cls) -> int:
return 3 return 3
compatibleMaterialDiameterChanged = pyqtSignal()
## Return the filament diameter that the machine requires. ## Return the filament diameter that the machine requires.
# #
# If the machine has no requirement for the diameter, -1 is returned. # If the machine has no requirement for the diameter, -1 is returned.
# \return The filament diameter for the printer # \return The filament diameter for the printer
@property def getCompatibleMaterialDiameter(self) -> float:
def materialDiameter(self) -> float:
context = PropertyEvaluationContext(self) context = PropertyEvaluationContext(self)
context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant 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. ## 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. # If the machine has no requirement for the diameter, -1 is returned.
# #
# \return The approximate filament diameter for the printer # \return The approximate filament diameter for the printer
@pyqtProperty(float) def getApproximateMaterialDiameter(self) -> float:
def approximateMaterialDiameter(self) -> float: return round(self.getCompatibleMaterialDiameter())
return round(float(self.materialDiameter))
approximateMaterialDiameter = pyqtProperty(float, fget = getApproximateMaterialDiameter,
notify = approximateMaterialDiameterChanged)
## Overridden from ContainerStack ## Overridden from ContainerStack
# #

View file

@ -20,7 +20,6 @@ from UM.Message import Message
from UM.Settings.SettingFunction import SettingFunction from UM.Settings.SettingFunction import SettingFunction
from UM.Signal import postponeSignals, CompressTechnique from UM.Signal import postponeSignals, CompressTechnique
import cura.CuraApplication
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.PrinterOutputDevice import PrinterOutputDevice from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel 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.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack 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 from .CuraStackBuilder import CuraStackBuilder
@ -36,6 +38,7 @@ from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
if TYPE_CHECKING: if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
from cura.Settings.CuraContainerStack import CuraContainerStack from cura.Settings.CuraContainerStack import CuraContainerStack
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from cura.Machines.MaterialManager import MaterialManager from cura.Machines.MaterialManager import MaterialManager
@ -47,7 +50,7 @@ if TYPE_CHECKING:
class MachineManager(QObject): class MachineManager(QObject):
def __init__(self, parent: QObject = None) -> None: def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
super().__init__(parent) super().__init__(parent)
self._active_container_stack = None # type: Optional[ExtruderStack] 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.setSingleShot(True)
self._instance_container_timer.timeout.connect(self.__emitChangedSignals) 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.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. ## When the global container is changed, active material probably needs to be updated.
self.globalContainerChanged.connect(self.activeMaterialChanged) self.globalContainerChanged.connect(self.activeMaterialChanged)
@ -80,13 +84,6 @@ class MachineManager(QObject):
self._stacks_have_errors = None # type: Optional[bool] 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() self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
@ -192,19 +189,21 @@ class MachineManager(QObject):
for extruder in self._global_container_stack.extruders.values(): for extruder in self._global_container_stack.extruders.values():
extruder_configuration = ExtruderConfigurationModel() extruder_configuration = ExtruderConfigurationModel()
# For compare just the GUID is needed at this moment # 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_type = extruder.material.getMetaDataEntry("material") if extruder.material != empty_material_container else None
mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != self._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 != self._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 != self._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 != self._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) material_model = MaterialOutputModel(mat_guid, mat_type, mat_color, mat_brand, mat_name)
extruder_configuration.position = int(extruder.getMetaDataEntry("position")) extruder_configuration.position = int(extruder.getMetaDataEntry("position"))
extruder_configuration.material = material_model 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.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() self.currentConfigurationChanged.emit()
@pyqtSlot(QObject, result = bool) @pyqtSlot(QObject, result = bool)
@ -258,14 +257,14 @@ class MachineManager(QObject):
# Global stack can have only a variant if it is a buildplate # Global stack can have only a variant if it is a buildplate
global_variant = self._global_container_stack.variant 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": 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 # 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 global_material = self._global_container_stack.material
if global_material != self._empty_material_container: if global_material != empty_material_container:
self._global_container_stack.setMaterial(self._empty_material_container) self._global_container_stack.setMaterial(empty_material_container)
# Listen for changes on all extruder stacks # Listen for changes on all extruder stacks
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
@ -367,6 +366,10 @@ class MachineManager(QObject):
return return
global_stack = containers[0] 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) ExtruderManager.getInstance()._fixSingleExtrusionMachineExtruderDefinition(global_stack)
if not global_stack.isValid(): if not global_stack.isValid():
# Mark global stack as invalid # Mark global stack as invalid
@ -593,7 +596,7 @@ class MachineManager(QObject):
def globalVariantName(self) -> str: def globalVariantName(self) -> str:
if self._global_container_stack: if self._global_container_stack:
variant = self._global_container_stack.variant 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 variant.getName()
return "" return ""
@ -781,7 +784,7 @@ class MachineManager(QObject):
if not stack.isEnabled: if not stack.isEnabled:
continue continue
material_container = stack.material material_container = stack.material
if material_container == self._empty_material_container: if material_container == empty_material_container:
continue continue
if material_container.getMetaDataEntry("buildplate_compatible"): if material_container.getMetaDataEntry("buildplate_compatible"):
buildplate_compatible = buildplate_compatible and material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] 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() extruder_stacks = self._global_container_stack.extruders.values()
for stack in extruder_stacks: for stack in extruder_stacks:
material_container = stack.material material_container = stack.material
if material_container == self._empty_material_container: if material_container == empty_material_container:
continue continue
buildplate_compatible = material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_compatible") else True 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 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() extruder_manager = self._application.getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges 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 return
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
@ -1072,7 +1075,7 @@ class MachineManager(QObject):
for stack in active_stacks: for stack in active_stacks:
variant_container = stack.variant variant_container = stack.variant
position = stack.getMetaDataEntry("position") 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() result[position] = variant_container.getName()
return result return result
@ -1086,11 +1089,11 @@ class MachineManager(QObject):
return return
self._current_quality_group = None self._current_quality_group = None
self._current_quality_changes_group = None self._current_quality_changes_group = None
self._global_container_stack.quality = self._empty_quality_container self._global_container_stack.quality = empty_quality_container
self._global_container_stack.qualityChanges = self._empty_quality_changes_container self._global_container_stack.qualityChanges = empty_quality_changes_container
for extruder in self._global_container_stack.extruders.values(): for extruder in self._global_container_stack.extruders.values():
extruder.quality = self._empty_quality_container extruder.quality = empty_quality_container
extruder.qualityChanges = self._empty_quality_changes_container extruder.qualityChanges = empty_quality_changes_container
self.activeQualityGroupChanged.emit() self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit() self.activeQualityChangesGroupChanged.emit()
@ -1115,13 +1118,13 @@ class MachineManager(QObject):
# Set quality and quality_changes for the GlobalStack # Set quality and quality_changes for the GlobalStack
self._global_container_stack.quality = quality_group.node_for_global.getContainer() self._global_container_stack.quality = quality_group.node_for_global.getContainer()
if empty_quality_changes: 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 # Set quality and quality_changes for each ExtruderStack
for position, node in quality_group.nodes_for_extruders.items(): for position, node in quality_group.nodes_for_extruders.items():
self._global_container_stack.extruders[str(position)].quality = node.getContainer() self._global_container_stack.extruders[str(position)].quality = node.getContainer()
if empty_quality_changes: 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.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit() self.activeQualityChangesGroupChanged.emit()
@ -1147,8 +1150,8 @@ class MachineManager(QObject):
if quality_group is None: if quality_group is None:
self._fixQualityChangesGroupToNotSupported(quality_changes_group) self._fixQualityChangesGroupToNotSupported(quality_changes_group)
quality_changes_container = self._empty_quality_changes_container quality_changes_container = empty_quality_changes_container
quality_container = self._empty_quality_container # type: Optional[InstanceContainer] quality_container = empty_quality_container # type: Optional[InstanceContainer]
if quality_changes_group.node_for_global and quality_changes_group.node_for_global.getContainer(): 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()) 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(): 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: if quality_group is not None:
quality_node = quality_group.nodes_for_extruders.get(position) quality_node = quality_group.nodes_for_extruders.get(position)
quality_changes_container = self._empty_quality_changes_container quality_changes_container = empty_quality_changes_container
quality_container = self._empty_quality_container quality_container = empty_quality_container
if quality_changes_node and quality_changes_node.getContainer(): if quality_changes_node and quality_changes_node.getContainer():
quality_changes_container = cast(InstanceContainer, quality_changes_node.getContainer()) quality_changes_container = cast(InstanceContainer, quality_changes_node.getContainer())
if quality_node and quality_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() self._global_container_stack.extruders[position].material = container_node.getContainer()
root_material_id = container_node.getMetaDataEntry("base_file", None) root_material_id = container_node.getMetaDataEntry("base_file", None)
else: 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 root_material_id = None
# The _current_root_material_id is used in the MaterialMenu to see which material is selected # 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]: 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_material_base_name = extruder.material.getMetaDataEntry("base_file")
current_nozzle_name = None 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") current_nozzle_name = extruder.variant.getMetaDataEntry("name")
from UM.Settings.Interfaces import PropertyEvaluationContext material_diameter = extruder.getCompatibleMaterialDiameter()
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)
candidate_materials = self._material_manager.getAvailableMaterials( candidate_materials = self._material_manager.getAvailableMaterials(
self._global_container_stack.definition, self._global_container_stack.definition,
current_nozzle_name, current_nozzle_name,
@ -1348,12 +1347,12 @@ class MachineManager(QObject):
if variant_container_node: if variant_container_node:
self._setVariantNode(position, variant_container_node) self._setVariantNode(position, variant_container_node)
else: 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: if material_container_node:
self._setMaterial(position, material_container_node) self._setMaterial(position, material_container_node)
else: 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) self.updateMaterialWithVariant(position)
if configuration.buildplateConfiguration is not None: if configuration.buildplateConfiguration is not None:
@ -1361,9 +1360,9 @@ class MachineManager(QObject):
if global_variant_container_node: if global_variant_container_node:
self._setGlobalVariant(global_variant_container_node) self._setGlobalVariant(global_variant_container_node)
else: else:
self._global_container_stack.variant = self._empty_variant_container self._global_container_stack.variant = empty_variant_container
else: else:
self._global_container_stack.variant = self._empty_variant_container self._global_container_stack.variant = empty_variant_container
self._updateQualityWithMaterial() self._updateQualityWithMaterial()
# See if we need to show the Discard or Keep changes screen # See if we need to show the Discard or Keep changes screen
@ -1415,7 +1414,7 @@ class MachineManager(QObject):
position = str(position) position = str(position)
extruder_stack = self._global_container_stack.extruders[position] extruder_stack = self._global_container_stack.extruders[position]
nozzle_name = extruder_stack.variant.getName() 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_node = self._material_manager.getMaterialNode(machine_definition_id, nozzle_name, buildplate_name,
material_diameter, root_material_id) material_diameter, root_material_id)
self.setMaterial(position, material_node) self.setMaterial(position, material_node)
@ -1481,7 +1480,7 @@ class MachineManager(QObject):
# This is not changing the quality for the active machine !!!!!!!! # This is not changing the quality for the active machine !!!!!!!!
global_stack.quality = quality_group.node_for_global.getContainer() global_stack.quality = quality_group.node_for_global.getContainer()
for extruder_nr, extruder_stack in global_stack.extruders.items(): 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: if extruder_nr in quality_group.nodes_for_extruders:
container = quality_group.nodes_for_extruders[extruder_nr].getContainer() container = quality_group.nodes_for_extruders[extruder_nr].getContainer()
quality_container = container if container is not None else quality_container quality_container = container if container is not None else quality_container
@ -1525,7 +1524,7 @@ class MachineManager(QObject):
@pyqtProperty(str, notify = activeQualityGroupChanged) @pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityOrQualityChangesName(self) -> str: def activeQualityOrQualityChangesName(self) -> str:
name = self._empty_quality_container.getName() name = empty_quality_container.getName()
if self._current_quality_changes_group: if self._current_quality_changes_group:
name = self._current_quality_changes_group.name name = self._current_quality_changes_group.name
elif self._current_quality_group: elif self._current_quality_group:

View file

@ -926,7 +926,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
build_plate_id = global_stack.variant.getId() build_plate_id = global_stack.variant.getId()
# get material diameter of this extruder # get material diameter of this extruder
machine_material_diameter = extruder_stack.materialDiameter machine_material_diameter = extruder_stack.getCompatibleMaterialDiameter()
material_node = material_manager.getMaterialNode(global_stack.definition.getId(), material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
extruder_stack.variant.getName(), extruder_stack.variant.getName(),
build_plate_id, build_plate_id,

View file

@ -1,3 +1,16 @@
[3.5.1]
*Bug fixes
- Fixed M104 temperature commands giving inaccurate results.
- Fixed crashes caused by loading files from USB stick on Windows platforms.
- Fixed several issues with configuration files that missed the type in the metadata.
- Fixed issues caused by skin/infill optimization.
- Fixed several issues related to missing definition files for third-party printers.
- Fixed an issue where combing path generation cuts corners.
- Fixed a range of crashes caused by lock files.
- Fixed issues with remembering save directories on MacOS.
- Fixed an issue where CuraEngine uses incorrect material settings.
- Fixed an issue where some support layers don't have support infill.
[3.5.0] [3.5.0]
*Monitor page *Monitor page
The monitor page of Ultimaker Cura has been remodeled for better consistency with the Cura Connect Print jobs interface. This means less switching between interfaces, and more control from within Ultimaker Cura. The monitor page of Ultimaker Cura has been remodeled for better consistency with the Cura Connect Print jobs interface. This means less switching between interfaces, and more control from within Ultimaker Cura.

View file

@ -195,10 +195,6 @@ class FlavorParser:
self._previous_z = z self._previous_z = z
elif self._previous_extrusion_value > e[self._extruder_number]: elif self._previous_extrusion_value > e[self._extruder_number]:
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType]) path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType])
# This case only for initial start, for the first coordinate in GCode
elif e[self._extruder_number] == 0 and self._previous_extrusion_value == 0:
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType])
else: else:
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveCombingType]) path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveCombingType])
return self._position(x, y, z, f, e) return self._position(x, y, z, f, e)
@ -235,6 +231,9 @@ class FlavorParser:
# Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width # Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width
self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e
position.e[self._extruder_number] = params.e position.e[self._extruder_number] = params.e
self._previous_extrusion_value = params.e
else:
self._previous_extrusion_value = 0.0
return self._position( return self._position(
params.x if params.x is not None else position.x, params.x if params.x is not None else position.x,
params.y if params.y is not None else position.y, params.y if params.y is not None else position.y,
@ -243,7 +242,6 @@ class FlavorParser:
position.e) position.e)
def processGCode(self, G: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position: def processGCode(self, G: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position:
self._previous_extrusion_value = 0.0
func = getattr(self, "_gCode%s" % G, None) func = getattr(self, "_gCode%s" % G, None)
line = line.split(";", 1)[0] # Remove comments (if any) line = line.split(";", 1)[0] # Remove comments (if any)
if func is not None: if func is not None:
@ -338,6 +336,7 @@ class FlavorParser:
min_layer_number = 0 min_layer_number = 0
negative_layers = 0 negative_layers = 0
previous_layer = 0 previous_layer = 0
self._previous_extrusion_value = 0.0
for line in stream.split("\n"): for line in stream.split("\n"):
if self._cancelled: if self._cancelled:

View file

@ -1,9 +1,9 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from . import FlavorParser from . import FlavorParser
# This parser is intented for interpret the RepRap Firmware flavor ## This parser is intended to interpret the RepRap Firmware g-code flavor.
class RepRapFlavorParser(FlavorParser.FlavorParser): class RepRapFlavorParser(FlavorParser.FlavorParser):
def __init__(self): def __init__(self):

View file

@ -405,7 +405,15 @@ Cura.MachineAction
{ {
if (settingsTabs.currentIndex > 0) if (settingsTabs.currentIndex > 0)
{ {
manager.updateMaterialForDiameter(settingsTabs.currentIndex - 1); manager.updateMaterialForDiameter(settingsTabs.currentIndex - 1)
}
}
function setValueFunction(value)
{
if (settingsTabs.currentIndex > 0)
{
var extruderIndex = (settingsTabs.currentIndex - 1).toString()
Cura.MachineManager.activeMachine.extruders[extruderIndex].compatibleMaterialDiameter = value
} }
} }
property bool isExtruderSetting: true property bool isExtruderSetting: true
@ -564,6 +572,7 @@ Cura.MachineAction
property bool _forceUpdateOnChange: (typeof(forceUpdateOnChange) === 'undefined') ? false : forceUpdateOnChange property bool _forceUpdateOnChange: (typeof(forceUpdateOnChange) === 'undefined') ? false : forceUpdateOnChange
property string _label: (typeof(label) === 'undefined') ? "" : label property string _label: (typeof(label) === 'undefined') ? "" : label
property string _tooltip: (typeof(tooltip) === 'undefined') ? propertyProvider.properties.description : tooltip property string _tooltip: (typeof(tooltip) === 'undefined') ? propertyProvider.properties.description : tooltip
property var _setValueFunction: (typeof(setValueFunction) === 'undefined') ? undefined : setValueFunction
UM.SettingPropertyProvider UM.SettingPropertyProvider
{ {
@ -616,14 +625,32 @@ Cura.MachineAction
{ {
if (propertyProvider && text != propertyProvider.properties.value) if (propertyProvider && text != propertyProvider.properties.value)
{ {
propertyProvider.setPropertyValue("value", text); // For some properties like the extruder-compatible material diameter, they need to
// trigger many updates, such as the available materials, the current material may
// need to be switched, etc. Although setting the diameter can be done directly via
// the provider, all the updates that need to be triggered then need to depend on
// the metadata update, a signal that can be fired way too often. The update functions
// can have if-checks to filter out the irrelevant updates, but still it incurs unnecessary
// overhead.
// The ExtruderStack class has a dedicated function for this call "setCompatibleMaterialDiameter()",
// and it triggers the diameter update signals only when it is needed. Here it is optionally
// choose to use setCompatibleMaterialDiameter() or other more specific functions that
// are available.
if (_setValueFunction !== undefined)
{
_setValueFunction(text)
}
else
{
propertyProvider.setPropertyValue("value", text)
}
if(_forceUpdateOnChange) if(_forceUpdateOnChange)
{ {
manager.forceUpdate(); manager.forceUpdate()
} }
if(_afterOnEditingFinished) if(_afterOnEditingFinished)
{ {
_afterOnEditingFinished(); _afterOnEditingFinished()
} }
} }
} }

View file

@ -407,13 +407,13 @@ class ChangeAtZ(Script):
if "M106" in line and state < 3: #looking for fan speed if "M106" in line and state < 3: #looking for fan speed
old["fanSpeed"] = self.getValue(line, "S", old["fanSpeed"]) old["fanSpeed"] = self.getValue(line, "S", old["fanSpeed"])
if "M221" in line and state < 3: #looking for flow rate if "M221" in line and state < 3: #looking for flow rate
tmp_extruder = self.getValue(line,"T",None) tmp_extruder = self.getValue(line, "T", None)
if tmp_extruder == None: #check if extruder is specified if tmp_extruder == None: #check if extruder is specified
old["flowrate"] = self.getValue(line, "S", old["flowrate"]) old["flowrate"] = self.getValue(line, "S", old["flowrate"])
elif tmp_extruder == 0: #first extruder elif tmp_extruder == 0: #first extruder
old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"]) old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"])
elif tmp_extruder == 1: #second extruder elif tmp_extruder == 1: #second extruder
old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"]) old["flowrateTwo"] = self.getValue(line, "S", old["flowrateTwo"])
if ("M84" in line or "M25" in line): if ("M84" in line or "M25" in line):
if state>0 and ChangeProp["speed"]: #"finish" commands for UM Original and UM2 if state>0 and ChangeProp["speed"]: #"finish" commands for UM Original and UM2
modified_gcode += "M220 S100 ; speed reset to 100% at the end of print\n" modified_gcode += "M220 S100 ; speed reset to 100% at the end of print\n"

View file

@ -1,5 +1,6 @@
# This PostProcessing Plugin script is released # Copyright (c) 2018 Ultimaker B.V.
# under the terms of the AGPLv3 or higher # The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
from typing import Optional, Tuple from typing import Optional, Tuple
from UM.Logger import Logger from UM.Logger import Logger
@ -58,10 +59,10 @@ class FilamentChange(Script):
color_change = "M600" color_change = "M600"
if initial_retract is not None and initial_retract > 0.: if initial_retract is not None and initial_retract > 0.:
color_change = color_change + (" E-%.2f" % initial_retract) color_change = color_change + (" E%.2f" % initial_retract)
if later_retract is not None and later_retract > 0.: if later_retract is not None and later_retract > 0.:
color_change = color_change + (" L-%.2f" % later_retract) color_change = color_change + (" L%.2f" % later_retract)
color_change = color_change + " ; Generated by FilamentChange plugin" color_change = color_change + " ; Generated by FilamentChange plugin"

View file

@ -1,46 +1,40 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import QtQuick.Controls 2.0 as Controls2
import QtGraphicalEffects 1.0
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Rectangle Rectangle {
{ property var iconSource: null;
property var iconSource: null color: clickArea.containsMouse ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary"); // "Cura Blue"
height: width;
radius: Math.round(0.5 * width);
width: 36 * screenScaleFactor;
width: 36 * screenScaleFactor UM.RecolorImage {
height: width id: icon;
radius: 0.5 * width anchors {
color: clickArea.containsMouse ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary") horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
UM.RecolorImage }
{ color: UM.Theme.getColor("primary_text");
id: icon height: width;
width: parent.width / 2 source: iconSource;
height: width width: Math.round(parent.width / 2);
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("primary_text")
source: iconSource
} }
MouseArea MouseArea {
{ id: clickArea;
id: clickArea anchors.fill: parent;
anchors.fill:parent hoverEnabled: true;
hoverEnabled: true onClicked: {
onClicked: if (OutputDevice.activeCamera !== null) {
{
if (OutputDevice.activeCamera !== null)
{
OutputDevice.setActiveCamera(null) OutputDevice.setActiveCamera(null)
} } else {
else OutputDevice.setActiveCamera(modelData.camera);
{
OutputDevice.setActiveCamera(modelData.camera)
} }
} }
} }

View file

@ -1,803 +1,109 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 2.0 as Controls2
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Component {
Rectangle {
id: base;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width;
anchors.fill: parent;
color: UM.Theme.getColor("sidebar");
visible: OutputDevice != null;
Component UM.I18nCatalog {
{ id: catalog;
Rectangle name: "cura";
{
id: base
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
property var shadowRadius: 5 * screenScaleFactor
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
visible: OutputDevice != null
anchors.fill: parent
color: "white"
UM.I18nCatalog
{
id: catalog
name: "cura"
} }
Label Label {
{ id: printingLabel;
id: printingLabel anchors {
font: UM.Theme.getFont("large") left: parent.left;
anchors leftMargin: 4 * UM.Theme.getSize("default_margin").width;
{ margins: 2 * UM.Theme.getSize("default_margin").width;
margins: 2 * UM.Theme.getSize("default_margin").width right: parent.right;
leftMargin: 4 * UM.Theme.getSize("default_margin").width top: parent.top;
top: parent.top }
left: parent.left color: UM.Theme.getColor("text");
right: parent.right elide: Text.ElideRight;
font: UM.Theme.getFont("large");
text: catalog.i18nc("@label", "Printing");
} }
text: catalog.i18nc("@label", "Printing") Label {
elide: Text.ElideRight id: managePrintersLabel;
anchors {
bottom: printingLabel.bottom;
right: printerScrollView.right;
rightMargin: 4 * UM.Theme.getSize("default_margin").width;
}
color: UM.Theme.getColor("primary"); // "Cura Blue"
font: UM.Theme.getFont("default");
linkColor: UM.Theme.getColor("primary"); // "Cura Blue"
text: catalog.i18nc("@label link to connect manager", "Manage printers");
} }
Label MouseArea {
{ anchors.fill: managePrintersLabel;
id: managePrintersLabel hoverEnabled: true;
anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel();
anchors.right: printerScrollView.right onEntered: managePrintersLabel.font.underline = true;
anchors.bottom: printingLabel.bottom onExited: managePrintersLabel.font.underline = false;
text: catalog.i18nc("@label link to connect manager", "Manage printers")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("primary")
linkColor: UM.Theme.getColor("primary")
} }
MouseArea // Skeleton loading
{ Column {
anchors.fill: managePrintersLabel id: skeletonLoader;
hoverEnabled: true anchors {
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel() left: parent.left;
onEntered: managePrintersLabel.font.underline = true leftMargin: UM.Theme.getSize("wide_margin").width;
onExited: managePrintersLabel.font.underline = false right: parent.right;
rightMargin: UM.Theme.getSize("wide_margin").width;
top: printingLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
} }
spacing: UM.Theme.getSize("default_margin").height - 10;
visible: printerList.count === 0;
ScrollView PrinterCard {
{ printer: null;
id: printerScrollView
anchors
{
top: printingLabel.bottom
left: parent.left
right: parent.right
topMargin: UM.Theme.getSize("default_margin").height
bottom: parent.bottom
bottomMargin: UM.Theme.getSize("default_margin").height
}
style: UM.Theme.styles.scrollview
ListView
{
id: printer_list
property var current_index: -1
anchors
{
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
leftMargin: 2 * UM.Theme.getSize("default_margin").width
rightMargin: 2 * UM.Theme.getSize("default_margin").width
}
spacing: UM.Theme.getSize("default_margin").height -10
model: OutputDevice.printers
delegate: Item
{
width: parent.width
height: base.height + 2 * base.shadowRadius // To ensure that the shadow doesn't get cut off.
Rectangle
{
width: parent.width - 2 * shadowRadius
height: childrenRect.height + UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color:
{
if(modelData.state == "disabled")
{
return UM.Theme.getColor("monitor_background_inactive")
}
else
{
return UM.Theme.getColor("monitor_background_active")
}
}
id: base
property var shadowRadius: 5 * screenScaleFactor
property var collapsed: true
layer.enabled: true
layer.effect: DropShadow
{
radius: 5 * screenScaleFactor
verticalOffset: 2
color: "#3F000000" // 25% shadow
}
Connections
{
target: printer_list
onCurrent_indexChanged: { base.collapsed = printer_list.current_index != model.index }
}
Item
{
id: printerInfo
height: machineIcon.height
anchors
{
top: parent.top
left: parent.left
right: parent.right
margins: UM.Theme.getSize("default_margin").width
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (base.collapsed) {
printer_list.current_index = model.index
}
else
{
printer_list.current_index = -1
} }
PrinterCard {
printer: null;
} }
} }
Item // Actual content
{ ScrollView {
id: machineIcon id: printerScrollView;
// Yeah, this is hardcoded now, but I can't think of a good way to fix this. anchors {
// The UI is going to get another update soon, so it's probably not worth the effort... bottom: parent.bottom;
width: 58 left: parent.left;
height: 58 right: parent.right;
anchors.top: parent.top top: printingLabel.bottom;
anchors.leftMargin: UM.Theme.getSize("default_margin").width topMargin: UM.Theme.getSize("default_margin").height;
anchors.left: parent.left }
style: UM.Theme.styles.scrollview;
UM.RecolorImage ListView {
{ id: printerList;
anchors.centerIn: parent property var currentIndex: -1;
source: anchors {
{ fill: parent;
switch(modelData.type) leftMargin: UM.Theme.getSize("wide_margin").width;
{ rightMargin: UM.Theme.getSize("wide_margin").width;
case "Ultimaker 3": }
return "../svg/UM3-icon.svg" delegate: PrinterCard {
case "Ultimaker 3 Extended": printer: modelData;
return "../svg/UM3x-icon.svg" }
case "Ultimaker S5": model: OutputDevice.printers;
return "../svg/UMs5-icon.svg" spacing: UM.Theme.getSize("default_margin").height - 10;
}
}
width: sourceSize.width
height: sourceSize.height
color:
{
if(modelData.state == "disabled")
{
return UM.Theme.getColor("monitor_text_inactive")
}
if(modelData.activePrintJob != undefined)
{
return UM.Theme.getColor("primary")
}
return UM.Theme.getColor("monitor_text_inactive")
}
}
}
Item
{
height: childrenRect.height
anchors
{
right: collapseIcon.left
rightMargin: UM.Theme.getSize("default_margin").width
left: machineIcon.right
leftMargin: UM.Theme.getSize("default_margin").width
verticalCenter: machineIcon.verticalCenter
}
Label
{
id: machineNameLabel
text: modelData.name
width: parent.width
elide: Text.ElideRight
font: UM.Theme.getFont("default_bold")
}
Label
{
id: activeJobLabel
text:
{
if (modelData.state == "disabled")
{
return catalog.i18nc("@label", "Not available")
} else if (modelData.state == "unreachable")
{
return catalog.i18nc("@label", "Unreachable")
}
if (modelData.activePrintJob != null)
{
return modelData.activePrintJob.name
}
return catalog.i18nc("@label", "Available")
}
anchors.top: machineNameLabel.bottom
width: parent.width
elide: Text.ElideRight
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("monitor_text_inactive")
}
}
UM.RecolorImage
{
id: collapseIcon
width: 15
height: 15
sourceSize.width: width
sourceSize.height: height
source: base.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
color: "black"
}
}
Item
{
id: detailedInfo
property var printJob: modelData.activePrintJob
visible: height == childrenRect.height
anchors.top: printerInfo.bottom
width: parent.width
height: !base.collapsed ? childrenRect.height : 0
opacity: visible ? 1 : 0
Behavior on height { NumberAnimation { duration: 100 } }
Behavior on opacity { NumberAnimation { duration: 100 } }
Rectangle
{
id: topSpacer
color:
{
if(modelData.state == "disabled")
{
return UM.Theme.getColor("monitor_lining_inactive")
}
return UM.Theme.getColor("viewport_background")
}
// UM.Theme.getColor("viewport_background")
height: 1
anchors
{
left: parent.left
right: parent.right
margins: UM.Theme.getSize("default_margin").width
top: parent.top
topMargin: UM.Theme.getSize("default_margin").width
}
}
PrinterFamilyPill
{
id: printerFamilyPill
color:
{
if(modelData.state == "disabled")
{
return "transparent"
}
return UM.Theme.getColor("viewport_background")
}
anchors.top: topSpacer.bottom
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
text: modelData.type
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
padding: 3
}
Row
{
id: extrudersInfo
anchors.top: printerFamilyPill.bottom
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin: 2 * UM.Theme.getSize("default_margin").width
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
PrintCoreConfiguration
{
id: leftExtruderInfo
width: Math.round(parent.width / 2)
printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[0]
}
PrintCoreConfiguration
{
id: rightExtruderInfo
width: Math.round(parent.width / 2)
printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[1]
}
}
Rectangle
{
id: jobSpacer
color: UM.Theme.getColor("viewport_background")
height: 2
anchors
{
left: parent.left
right: parent.right
margins: UM.Theme.getSize("default_margin").width
top: extrudersInfo.bottom
topMargin: 2 * UM.Theme.getSize("default_margin").height
}
}
Item
{
id: jobInfo
property var showJobInfo: modelData.activePrintJob != null && modelData.activePrintJob.state != "queued"
anchors.top: jobSpacer.bottom
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").width
anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
height: showJobInfo ? childrenRect.height + 2 * UM.Theme.getSize("default_margin").height: 0
visible: showJobInfo
Label
{
id: printJobName
text: modelData.activePrintJob != null ? modelData.activePrintJob.name : ""
font: UM.Theme.getFont("default_bold")
anchors.left: parent.left
anchors.right: contextButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
elide: Text.ElideRight
}
Label
{
id: ownerName
anchors.top: printJobName.bottom
text: modelData.activePrintJob != null ? modelData.activePrintJob.owner : ""
font: UM.Theme.getFont("default")
opacity: 0.6
width: parent.width
elide: Text.ElideRight
}
function switchPopupState()
{
popup.visible ? popup.close() : popup.open()
}
Controls2.Button
{
id: contextButton
text: "\u22EE" //Unicode; Three stacked points.
width: 35
height: width
anchors
{
right: parent.right
top: parent.top
}
hoverEnabled: true
background: Rectangle
{
opacity: contextButton.down || contextButton.hovered ? 1 : 0
width: contextButton.width
height: contextButton.height
radius: 0.5 * width
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: contextButton.text
color: UM.Theme.getColor("monitor_text_inactive")
font.pixelSize: 25
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
onClicked: parent.switchPopupState()
}
Controls2.Popup
{
// TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property
id: popup
clip: true
closePolicy: Popup.CloseOnPressOutside
x: (parent.width - width) + 26 * screenScaleFactor
y: contextButton.height - 5 * screenScaleFactor // Because shadow
width: 182 * screenScaleFactor
height: contentItem.height + 2 * padding
visible: false
padding: 5 * screenScaleFactor // Because shadow
transformOrigin: Popup.Top
contentItem: Item
{
width: popup.width
height: childrenRect.height + 36 * screenScaleFactor
anchors.topMargin: 10 * screenScaleFactor
anchors.bottomMargin: 10 * screenScaleFactor
Controls2.Button
{
id: pauseButton
text: modelData.activePrintJob != null && modelData.activePrintJob.state == "paused" ? catalog.i18nc("@label", "Resume") : catalog.i18nc("@label", "Pause")
onClicked:
{
if(modelData.activePrintJob.state == "paused")
{
modelData.activePrintJob.setState("print")
}
else if(modelData.activePrintJob.state == "printing")
{
modelData.activePrintJob.setState("pause")
}
popup.close()
}
width: parent.width
enabled: modelData.activePrintJob != null && ["paused", "printing"].indexOf(modelData.activePrintJob.state) >= 0
visible: enabled
anchors.top: parent.top
anchors.topMargin: 18 * screenScaleFactor
height: visible ? 39 * screenScaleFactor : 0 * screenScaleFactor
hoverEnabled: true
background: Rectangle
{
opacity: pauseButton.down || pauseButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: pauseButton.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
}
Controls2.Button
{
id: abortButton
text: catalog.i18nc("@label", "Abort")
onClicked:
{
abortConfirmationDialog.visible = true;
popup.close();
}
width: parent.width
height: 39 * screenScaleFactor
anchors.top: pauseButton.bottom
hoverEnabled: true
enabled: modelData.activePrintJob != null && ["paused", "printing", "pre_print"].indexOf(modelData.activePrintJob.state) >= 0
background: Rectangle
{
opacity: abortButton.down || abortButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: abortButton.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
}
MessageDialog
{
id: abortConfirmationDialog
title: catalog.i18nc("@window:title", "Abort print")
icon: StandardIcon.Warning
text: catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to abort %1?").arg(modelData.activePrintJob.name)
standardButtons: StandardButton.Yes | StandardButton.No
Component.onCompleted: visible = false
onYes: modelData.activePrintJob.setState("abort")
}
}
background: Item
{
width: popup.width
height: popup.height
DropShadow
{
anchors.fill: pointedRectangle
radius: 5
color: "#3F000000" // 25% shadow
source: pointedRectangle
transparentBorder: true
verticalOffset: 2
}
Item
{
id: pointedRectangle
width: parent.width - 10 * screenScaleFactor // Because of the shadow
height: parent.height - 10 * screenScaleFactor // Because of the shadow
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
id: point
height: 14 * screenScaleFactor
width: 14 * screenScaleFactor
color: UM.Theme.getColor("setting_control")
transform: Rotation { angle: 45}
anchors.right: bloop.right
anchors.rightMargin: 24
y: 1
}
Rectangle
{
id: bloop
color: UM.Theme.getColor("setting_control")
width: parent.width
anchors.top: parent.top
anchors.topMargin: 8 * screenScaleFactor // Because of the shadow + point
anchors.bottom: parent.bottom
anchors.bottomMargin: 8 * screenScaleFactor // Because of the shadow
}
}
}
exit: Transition
{
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
NumberAnimation { property: "visible"; duration: 75; }
}
enter: Transition
{
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
NumberAnimation { property: "visible"; duration: 75; }
}
onClosed: visible = false
onOpened: visible = true
}
Image
{
id: printJobPreview
source: modelData.activePrintJob != null ? modelData.activePrintJob.previewImageUrl : ""
anchors.top: ownerName.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width / 2
height: width
opacity:
{
if(modelData.activePrintJob == null)
{
return 1.0
}
switch(modelData.activePrintJob.state)
{
case "wait_cleanup":
case "wait_user_action":
case "paused":
return 0.5
default:
return 1.0
}
}
}
UM.RecolorImage
{
id: statusImage
anchors.centerIn: printJobPreview
source:
{
if(modelData.activePrintJob == null)
{
return ""
}
switch(modelData.activePrintJob.state)
{
case "paused":
return "../svg/paused-icon.svg"
case "wait_cleanup":
if(modelData.activePrintJob.timeElapsed < modelData.activePrintJob.timeTotal)
{
return "../svg/aborted-icon.svg"
}
return "../svg/approved-icon.svg"
case "wait_user_action":
return "../svg/aborted-icon.svg"
default:
return ""
}
}
visible: source != ""
width: 0.5 * printJobPreview.width
height: 0.5 * printJobPreview.height
sourceSize.width: width
sourceSize.height: height
color: "black"
}
CameraButton
{
id: showCameraButton
iconSource: "../svg/camera-icon.svg"
anchors
{
left: parent.left
bottom: printJobPreview.bottom
}
}
}
}
ProgressBar
{
property var progress:
{
if(modelData.activePrintJob == null)
{
return 0
}
var result = modelData.activePrintJob.timeElapsed / modelData.activePrintJob.timeTotal
if(result > 1.0)
{
result = 1.0
}
return result
}
id: jobProgressBar
width: parent.width
value: progress
anchors.top: detailedInfo.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
visible: modelData.activePrintJob != null && modelData.activePrintJob != undefined
style: ProgressBarStyle
{
property var remainingTime:
{
if(modelData.activePrintJob == null)
{
return 0
}
/* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining
time from ever being less than 0. Negative durations cause strange behavior such
as displaying "-1h -1m". */
var activeJob = modelData.activePrintJob
return Math.max(activeJob.timeTotal - activeJob.timeElapsed, 0);
}
property var progressText:
{
if(modelData.activePrintJob == null)
{
return ""
}
switch(modelData.activePrintJob.state)
{
case "wait_cleanup":
if(modelData.activePrintJob.timeTotal > modelData.activePrintJob.timeElapsed)
{
return catalog.i18nc("@label:status", "Aborted")
}
return catalog.i18nc("@label:status", "Finished")
case "pre_print":
case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing")
case "aborted":
return catalog.i18nc("@label:status", "Aborted")
case "wait_user_action":
return catalog.i18nc("@label:status", "Aborted")
case "pausing":
return catalog.i18nc("@label:status", "Pausing")
case "paused":
return OutputDevice.formatDuration( remainingTime )
case "resuming":
return catalog.i18nc("@label:status", "Resuming")
case "queued":
return catalog.i18nc("@label:status", "Action required")
default:
return OutputDevice.formatDuration( remainingTime )
}
}
background: Rectangle
{
implicitWidth: 100
implicitHeight: visible ? 24 : 0
color: UM.Theme.getColor("viewport_background")
}
progress: Rectangle
{
color:
{
var state = modelData.activePrintJob.state
var inactiveStates = [
"pausing",
"paused",
"resuming",
"wait_cleanup"
]
if(inactiveStates.indexOf(state) > -1 && remainingTime > 0)
{
return UM.Theme.getColor("monitor_text_inactive")
}
else
{
return UM.Theme.getColor("primary")
}
}
id: progressItem
function getTextOffset()
{
if(progressItem.width + progressLabel.width + 16 < control.width)
{
return progressItem.width + UM.Theme.getSize("default_margin").width
}
else
{
return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width
}
}
Label
{
id: progressLabel
anchors.left: parent.left
anchors.leftMargin: getTextOffset()
text: progressText
anchors.verticalCenter: parent.verticalCenter
color: progressItem.width + progressLabel.width < control.width ? "black" : "white"
width: contentWidth
font: UM.Theme.getFont("default")
}
}
}
}
}
}
} }
} }
} }

View file

@ -1,108 +1,132 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Component Component {
{ Rectangle {
Rectangle id: monitorFrame;
{ property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight");
id: monitorFrame property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width;
width: maximumWidth color: UM.Theme.getColor("viewport_background");
height: maximumHeight height: maximumHeight;
color: UM.Theme.getColor("viewport_background") onVisibleChanged: {
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") if (monitorFrame != null && !monitorFrame.visible) {
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme. OutputDevice.setActiveCamera(null);
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme. }
}
width: maximumWidth;
UM.I18nCatalog UM.I18nCatalog {
{ id: catalog;
id: catalog name: "cura";
name: "cura"
} }
Label Label {
{ id: manageQueueLabel;
id: manageQueueLabel anchors {
anchors.rightMargin: 3 * UM.Theme.getSize("default_margin").width bottom: queuedLabel.bottom;
anchors.right: queuedPrintJobs.right right: queuedPrintJobs.right;
anchors.bottom: queuedLabel.bottom rightMargin: 3 * UM.Theme.getSize("default_margin").width;
text: catalog.i18nc("@label link to connect manager", "Manage queue") }
font: UM.Theme.getFont("default") color: UM.Theme.getColor("primary");
color: UM.Theme.getColor("primary") font: UM.Theme.getFont("default");
linkColor: UM.Theme.getColor("primary") linkColor: UM.Theme.getColor("primary");
text: catalog.i18nc("@label link to connect manager", "Manage queue");
} }
MouseArea MouseArea {
{ anchors.fill: manageQueueLabel;
anchors.fill: manageQueueLabel hoverEnabled: true;
hoverEnabled: true onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel();
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel() onEntered: manageQueueLabel.font.underline = true;
onEntered: manageQueueLabel.font.underline = true onExited: manageQueueLabel.font.underline = false;
onExited: manageQueueLabel.font.underline = false
} }
Label Label {
{ id: queuedLabel;
id: queuedLabel anchors {
anchors.left: queuedPrintJobs.left left: queuedPrintJobs.left;
anchors.top: parent.top leftMargin: 3 * UM.Theme.getSize("default_margin").width + 5 * screenScaleFactor;
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height top: parent.top;
anchors.leftMargin: 3 * UM.Theme.getSize("default_margin").width + 5 topMargin: 2 * UM.Theme.getSize("default_margin").height;
text: catalog.i18nc("@label", "Queued") }
font: UM.Theme.getFont("large") color: UM.Theme.getColor("text");
color: UM.Theme.getColor("text") font: UM.Theme.getFont("large");
text: catalog.i18nc("@label", "Queued");
} }
ScrollView Column {
{ id: skeletonLoader;
id: queuedPrintJobs anchors {
bottom: parent.bottom;
anchors bottomMargin: UM.Theme.getSize("default_margin").height;
{ horizontalCenter: parent.horizontalCenter;
top: queuedLabel.bottom top: queuedLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height topMargin: UM.Theme.getSize("default_margin").height;
horizontalCenter: parent.horizontalCenter
bottomMargin: 0
bottom: parent.bottom
} }
style: UM.Theme.styles.scrollview visible: !queuedPrintJobs.visible;
width: Math.min(800 * screenScaleFactor, maximumWidth) width: Math.min(800 * screenScaleFactor, maximumWidth);
ListView
{
anchors.fill: parent
//anchors.margins: UM.Theme.getSize("default_margin").height
spacing: UM.Theme.getSize("default_margin").height - 10 // 2x the shadow radius
model: OutputDevice.queuedPrintJobs PrintJobInfoBlock {
anchors {
delegate: PrintJobInfoBlock left: parent.left;
{ leftMargin: UM.Theme.getSize("default_margin").width;
printJob: modelData right: parent.right;
anchors.left: parent.left rightMargin: UM.Theme.getSize("default_margin").width;
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").height
height: 175 * screenScaleFactor
} }
printJob: null; // Use as skeleton
}
PrintJobInfoBlock {
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
}
printJob: null; // Use as skeleton
} }
} }
PrinterVideoStream ScrollView {
{ id: queuedPrintJobs;
visible: OutputDevice.activeCamera != null anchors {
anchors.fill: parent top: queuedLabel.bottom;
camera: OutputDevice.activeCamera topMargin: UM.Theme.getSize("default_margin").height;
horizontalCenter: parent.horizontalCenter;
bottomMargin: UM.Theme.getSize("default_margin").height;
bottom: parent.bottom;
}
style: UM.Theme.styles.scrollview;
visible: OutputDevice.receivedPrintJobs;
width: Math.min(800 * screenScaleFactor, maximumWidth);
ListView {
id: printJobList;
anchors.fill: parent;
delegate: PrintJobInfoBlock {
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
}
printJob: modelData;
}
model: OutputDevice.queuedPrintJobs;
spacing: UM.Theme.getSize("default_margin").height - 2 * UM.Theme.getSize("monitor_shadow_radius").width;
}
} }
onVisibleChanged: PrinterVideoStream {
{ anchors.fill: parent;
if (monitorFrame != null && !monitorFrame.visible) camera: OutputDevice.activeCamera;
{ visible: OutputDevice.activeCamera != null;
OutputDevice.setActiveCamera(null)
}
} }
} }
} }

View file

@ -1,3 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura

View file

@ -0,0 +1,12 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
Rectangle {
color: UM.Theme.getColor("monitor_lining_light"); // TODO: Maybe theme separately? Maybe not.
height: UM.Theme.getSize("default_lining").height;
width: parent.width;
}

View file

@ -1,53 +1,51 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Component Component {
{ Item {
Item height: maximumHeight;
{ width: maximumWidth;
width: maximumWidth
height: maximumHeight Cura.CameraView {
Image id: cameraImage;
{ anchors {
id: cameraImage horizontalCenter: parent.horizontalCenter;
width: Math.min(sourceSize.width === 0 ? 800 * screenScaleFactor : sourceSize.width, maximumWidth) verticalCenter: parent.verticalCenter;
height: Math.floor((sourceSize.height === 0 ? 600 * screenScaleFactor : sourceSize.height) * width / sourceSize.width) }
anchors.horizontalCenter: parent.horizontalCenter Component.onCompleted: {
anchors.verticalCenter: parent.verticalCenter if (OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null) {
z: 1 OutputDevice.activePrinter.camera.start();
Component.onCompleted:
{
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
{
OutputDevice.activePrinter.camera.start()
} }
} }
onVisibleChanged: height: Math.floor((imageHeight === 0 ? 600 * screenScaleFactor : imageHeight) * width / imageWidth);
{ onVisibleChanged: {
if(visible) if (visible) {
{ if (OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null) {
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null) OutputDevice.activePrinter.camera.start();
{
OutputDevice.activePrinter.camera.start()
} }
} else } else {
{ if (OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null) {
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null) OutputDevice.activePrinter.camera.stop();
{
OutputDevice.activePrinter.camera.stop()
} }
} }
} }
source: width: Math.min(imageWidth === 0 ? 800 * screenScaleFactor : imageWidth, maximumWidth);
z: 1;
Connections
{ {
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null && OutputDevice.activePrinter.camera.latestImage) target: OutputDevice.activePrinter.camera;
onNewImage:
{ {
return OutputDevice.activePrinter.camera.latestImage; if (cameraImage.visible) {
cameraImage.image = OutputDevice.activePrinter.camera.latestImage;
cameraImage.update();
}
} }
return "";
} }
} }
} }

View file

@ -1,93 +1,121 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM import UM 1.2 as UM
Item {
id: extruderInfo;
property var printCoreConfiguration: null;
height: childrenRect.height;
width: Math.round(parent.width / 2);
Item // Extruder circle
{ Item {
id: extruderInfo id: extruderCircle;
property var printCoreConfiguration height: UM.Theme.getSize("monitor_extruder_circle").height;
width: UM.Theme.getSize("monitor_extruder_circle").width;
width: Math.round(parent.width / 2) // Loading skeleton
height: childrenRect.height Rectangle {
anchors.fill: parent;
Item color: UM.Theme.getColor("monitor_skeleton_fill");
{ radius: Math.round(width / 2);
id: extruderCircle visible: !printCoreConfiguration;
width: 30
height: 30
anchors.verticalCenter: printAndMaterialLabel.verticalCenter
opacity:
{
if(printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null)
{
return 0.5
}
return 1
} }
Rectangle // Actual content
{ Rectangle {
anchors.fill: parent anchors.fill: parent;
radius: Math.round(width / 2) border.width: UM.Theme.getSize("monitor_thick_lining").width;
border.width: 2 border.color: UM.Theme.getColor("monitor_lining_heavy");
border.color: "black" color: "transparent";
opacity: {
if (printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null) {
return 0.5;
} }
return 1;
}
radius: Math.round(width / 2);
visible: printCoreConfiguration;
Label Label {
{ anchors.centerIn: parent;
anchors.centerIn: parent color: UM.Theme.getColor("text");
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold");
text: printCoreConfiguration.position + 1 text: printCoreConfiguration ? printCoreConfiguration.position + 1 : 0;
}
} }
} }
Item // Print core and material labels
{ Item {
id: printAndMaterialLabel
anchors
{
right: parent.right
left: extruderCircle.right
margins: UM.Theme.getSize("default_margin").width
}
height: childrenRect.height
Label
{
id: materialLabel id: materialLabel
text: anchors {
{ left: extruderCircle.right;
if(printCoreConfiguration != undefined && printCoreConfiguration.activeMaterial != undefined) leftMargin: UM.Theme.getSize("default_margin").width;
{ right: parent.right;
return printCoreConfiguration.activeMaterial.name top: parent.top;
} }
return "" height: UM.Theme.getSize("monitor_text_line").height;
}
font: UM.Theme.getFont("default") // Loading skeleton
elide: Text.ElideRight Rectangle {
width: parent.width anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
visible: !extruderInfo.printCoreConfiguration;
} }
Label // Actual content
{ Label {
id: printCoreLabel anchors.fill: parent;
text: elide: Text.ElideRight;
{ color: UM.Theme.getColor("text");
if(printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined) font: UM.Theme.getFont("default");
{ text: {
return printCoreConfiguration.hotendID if (printCoreConfiguration && printCoreConfiguration.activeMaterial != undefined) {
return printCoreConfiguration.activeMaterial.name;
} }
return "" return "";
} }
anchors.top: materialLabel.bottom visible: extruderInfo.printCoreConfiguration;
elide: Text.ElideRight }
width: parent.width }
opacity: 0.6
font: UM.Theme.getFont("default") Item {
id: printCoreLabel;
anchors {
left: extruderCircle.right;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
top: materialLabel.bottom;
topMargin: Math.floor(UM.Theme.getSize("default_margin").height/4);
}
height: UM.Theme.getSize("monitor_text_line").height;
// Loading skeleton
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !extruderInfo.printCoreConfiguration;
width: Math.round(parent.width / 3);
}
// Actual content
Label {
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
opacity: 0.6;
text: {
if (printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined) {
return printCoreConfiguration.hotendID;
}
return "";
}
visible: extruderInfo.printCoreConfiguration;
} }
} }
} }

View file

@ -0,0 +1,212 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.1
import QtGraphicalEffects 1.0
import UM 1.3 as UM
Item {
id: root;
property var printJob: null;
property var running: isRunning(printJob);
Button {
id: button;
background: Rectangle {
color: UM.Theme.getColor("viewport_background"); // TODO: Theme!
height: button.height;
opacity: button.down || button.hovered ? 1 : 0;
radius: Math.round(0.5 * width);
width: button.width;
}
contentItem: Label {
color: UM.Theme.getColor("monitor_context_menu_dots");
font.pixelSize: 25 * screenScaleFactor;
horizontalAlignment: Text.AlignHCenter;
text: button.text;
verticalAlignment: Text.AlignVCenter;
}
height: width;
hoverEnabled: true;
onClicked: parent.switchPopupState();
text: "\u22EE"; //Unicode; Three stacked points.
width: 35 * screenScaleFactor; // TODO: Theme!
}
Popup {
id: popup;
background: Item {
anchors.fill: parent;
DropShadow {
anchors.fill: pointedRectangle;
color: UM.Theme.getColor("monitor_shadow");
radius: UM.Theme.getSize("monitor_shadow_radius").width;
source: pointedRectangle;
transparentBorder: true;
verticalOffset: 2 * screenScaleFactor;
}
Item {
id: pointedRectangle;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
height: parent.height - 10 * screenScaleFactor; // Because of the shadow
width: parent.width - 10 * screenScaleFactor; // Because of the shadow
Rectangle {
id: point;
anchors {
right: bloop.right;
rightMargin: 24 * screenScaleFactor;
}
color: UM.Theme.getColor("monitor_context_menu_background");
height: 14 * screenScaleFactor;
transform: Rotation {
angle: 45;
}
width: 14 * screenScaleFactor;
y: 1 * screenScaleFactor;
}
Rectangle {
id: bloop;
anchors {
bottom: parent.bottom;
bottomMargin: 8 * screenScaleFactor; // Because of the shadow
top: parent.top;
topMargin: 8 * screenScaleFactor; // Because of the shadow + point
}
color: UM.Theme.getColor("monitor_context_menu_background");
width: parent.width;
}
}
}
clip: true;
closePolicy: Popup.CloseOnPressOutside;
contentItem: Column {
id: popupOptions;
anchors {
top: parent.top;
topMargin: UM.Theme.getSize("default_margin").height + 10 * screenScaleFactor; // Account for the point of the box
}
height: childrenRect.height + spacing * popupOptions.children.length + UM.Theme.getSize("default_margin").height;
spacing: Math.floor(UM.Theme.getSize("default_margin").height / 2);
width: parent.width;
PrintJobContextMenuItem {
enabled: {
if (printJob && !running) {
if (OutputDevice && OutputDevice.queuedPrintJobs[0]) {
return OutputDevice.queuedPrintJobs[0].key != printJob.key;
}
}
return false;
}
onClicked: {
sendToTopConfirmationDialog.visible = true;
popup.close();
}
text: catalog.i18nc("@label", "Move to top");
}
PrintJobContextMenuItem {
enabled: printJob && !running;
onClicked: {
deleteConfirmationDialog.visible = true;
popup.close();
}
text: catalog.i18nc("@label", "Delete");
}
PrintJobContextMenuItem {
enabled: printJob && running;
onClicked: {
if (printJob.state == "paused") {
printJob.setState("print");
} else if(printJob.state == "printing") {
printJob.setState("pause");
}
popup.close();
}
text: printJob && printJob.state == "paused" ? catalog.i18nc("@label", "Resume") : catalog.i18nc("@label", "Pause");
}
PrintJobContextMenuItem {
enabled: printJob && running;
onClicked: {
abortConfirmationDialog.visible = true;
popup.close();
}
text: catalog.i18nc("@label", "Abort");
}
}
enter: Transition {
NumberAnimation {
duration: 75;
property: "visible";
}
}
exit: Transition {
NumberAnimation {
duration: 75;
property: "visible";
}
}
height: contentItem.height + 2 * padding;
onClosed: visible = false;
onOpened: visible = true;
padding: UM.Theme.getSize("monitor_shadow_radius").width;
transformOrigin: Popup.Top;
visible: false;
width: 182 * screenScaleFactor;
x: (button.width - width) + 26 * screenScaleFactor;
y: button.height + 5 * screenScaleFactor; // Because shadow
}
MessageDialog {
id: sendToTopConfirmationDialog;
Component.onCompleted: visible = false;
icon: StandardIcon.Warning;
onYes: OutputDevice.sendJobToTop(printJob.key);
standardButtons: StandardButton.Yes | StandardButton.No;
text: printJob && printJob.name ? catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to move %1 to the top of the queue?").arg(printJob.name) : "";
title: catalog.i18nc("@window:title", "Move print job to top");
}
MessageDialog {
id: deleteConfirmationDialog;
Component.onCompleted: visible = false;
icon: StandardIcon.Warning;
onYes: OutputDevice.deleteJobFromQueue(printJob.key);
standardButtons: StandardButton.Yes | StandardButton.No;
text: printJob && printJob.name ? catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to delete %1?").arg(printJob.name) : "";
title: catalog.i18nc("@window:title", "Delete print job");
}
MessageDialog {
id: abortConfirmationDialog;
Component.onCompleted: visible = false;
icon: StandardIcon.Warning;
onYes: printJob.setState("abort");
standardButtons: StandardButton.Yes | StandardButton.No;
text: printJob && printJob.name ? catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to abort %1?").arg(printJob.name) : "";
title: catalog.i18nc("@window:title", "Abort print");
}
// Utils
function switchPopupState() {
popup.visible ? popup.close() : popup.open();
}
function isRunning(job) {
if (!job) {
return false;
}
return ["paused", "printing", "pre_print"].indexOf(job.state) !== -1;
}
}

View file

@ -0,0 +1,24 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
Button {
background: Rectangle {
opacity: parent.down || parent.hovered ? 1 : 0;
color: UM.Theme.getColor("monitor_context_menu_highlight");
}
contentItem: Label {
color: UM.Theme.getColor("text");
text: parent.text
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
height: 39 * screenScaleFactor; // TODO: Theme!
hoverEnabled: true;
visible: enabled;
width: parent.width;
}

View file

@ -1,429 +1,476 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import UM 1.3 as UM import UM 1.3 as UM
Item {
id: root;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var shadowOffset: 2 * screenScaleFactor;
property var debug: false;
property var printJob: null;
width: parent.width; // Bubbles downward
height: childrenRect.height + shadowRadius * 2; // Bubbles upward
Item UM.I18nCatalog {
{ id: catalog;
id: base name: "cura";
property var printJob: null
property var shadowRadius: 5 * screenScaleFactor
function getPrettyTime(time)
{
return OutputDevice.formatDuration(time)
} }
width: parent.width // The actual card (white block)
Rectangle {
UM.I18nCatalog // 5px margin, but shifted 2px vertically because of the shadow
{ anchors {
id: catalog bottomMargin: root.shadowRadius + root.shadowOffset;
name: "cura" leftMargin: root.shadowRadius;
rightMargin: root.shadowRadius;
topMargin: root.shadowRadius - root.shadowOffset;
} }
color: UM.Theme.getColor("monitor_card_background");
Rectangle height: childrenRect.height;
{
id: background
anchors
{
top: parent.top
topMargin: 3 * screenScaleFactor
left: parent.left
leftMargin: base.shadowRadius
rightMargin: base.shadowRadius
right: parent.right
bottom: parent.bottom
bottomMargin: base.shadowRadius
}
layer.enabled: true layer.enabled: true
layer.effect: DropShadow layer.effect: DropShadow {
{ radius: root.shadowRadius
radius: base.shadowRadius
verticalOffset: 2 * screenScaleFactor verticalOffset: 2 * screenScaleFactor
color: "#3F000000" // 25% shadow color: "#3F000000" // 25% shadow
} }
width: parent.width - shadowRadius * 2;
Item Column {
{ height: childrenRect.height;
// Content on the left of the infobox width: parent.width;
anchors
{ // Main content
top: parent.top Item {
bottom: parent.bottom id: mainContent;
left: parent.left height: 200 * screenScaleFactor; // TODO: Theme!
right: parent.horizontalCenter width: parent.width;
margins: UM.Theme.getSize("wide_margin").width
rightMargin: UM.Theme.getSize("default_margin").width // Left content
Item {
anchors {
bottom: parent.bottom;
left: parent.left;
margins: UM.Theme.getSize("wide_margin").width;
right: parent.horizontalCenter;
top: parent.top;
} }
Label Item {
{ id: printJobName;
id: printJobName width: parent.width;
text: printJob.name height: UM.Theme.getSize("monitor_text_line").height;
font: UM.Theme.getFont("default_bold")
width: parent.width
elide: Text.ElideRight
}
Label Rectangle {
{ color: UM.Theme.getColor("monitor_skeleton_fill");
id: ownerName height: parent.height;
anchors.top: printJobName.bottom visible: !printJob;
text: printJob.owner width: Math.round(parent.width / 3);
font: UM.Theme.getFont("default")
opacity: 0.6
width: parent.width
elide: Text.ElideRight
} }
Label {
Image anchors.fill: parent;
{ color: UM.Theme.getColor("text");
id: printJobPreview elide: Text.ElideRight;
source: printJob.previewImageUrl font: UM.Theme.getFont("default_bold");
anchors.top: ownerName.bottom text: printJob && printJob.name ? printJob.name : ""; // Supress QML warnings
anchors.horizontalCenter: parent.horizontalCenter visible: printJob;
anchors.bottom: totalTimeLabel.bottom
width: height
opacity: printJob.state == "error" ? 0.5 : 1.0
}
UM.RecolorImage
{
id: statusImage
anchors.centerIn: printJobPreview
source: printJob.state == "error" ? "../svg/aborted-icon.svg" : ""
visible: source != ""
width: 0.5 * printJobPreview.width
height: 0.5 * printJobPreview.height
sourceSize.width: width
sourceSize.height: height
color: "black"
}
Label
{
id: totalTimeLabel
anchors.bottom: parent.bottom
anchors.right: parent.right
font: UM.Theme.getFont("default")
text: printJob != null ? getPrettyTime(printJob.timeTotal) : ""
elide: Text.ElideRight
} }
} }
Item Item {
{ id: printJobOwnerName;
// Content on the right side of the infobox. anchors {
anchors top: printJobName.bottom;
{ topMargin: Math.floor(UM.Theme.getSize("default_margin").height / 2);
top: parent.top
bottom: parent.bottom
left: parent.horizontalCenter
right: parent.right
margins: 2 * UM.Theme.getSize("default_margin").width
leftMargin: UM.Theme.getSize("default_margin").width
rightMargin: UM.Theme.getSize("default_margin").width / 2
} }
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
Label Rectangle {
{ color: UM.Theme.getColor("monitor_skeleton_fill");
id: targetPrinterLabel height: parent.height;
elide: Text.ElideRight visible: !printJob;
font: UM.Theme.getFont("default_bold") width: Math.round(parent.width / 2);
text:
{
if(printJob.assignedPrinter == null)
{
if(printJob.state == "error")
{
return catalog.i18nc("@label", "Waiting for: Unavailable printer")
} }
return catalog.i18nc("@label", "Waiting for: First available") Label {
} anchors.fill: parent;
else color: UM.Theme.getColor("text");
{ elide: Text.ElideRight;
return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name font: UM.Theme.getFont("default");
} text: printJob ? printJob.owner : ""; // Supress QML warnings
visible: printJob;
}
anchors
{
left: parent.left
right: contextButton.left
rightMargin: UM.Theme.getSize("default_margin").width
} }
} }
Item {
id: printJobPreview;
property var useUltibot: false;
anchors {
bottom: parent.bottom;
horizontalCenter: parent.horizontalCenter;
top: printJobOwnerName.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
width: height;
function switchPopupState() // Skeleton
{ Rectangle {
popup.visible ? popup.close() : popup.open() anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
radius: UM.Theme.getSize("default_margin").width;
visible: !printJob;
} }
Button // Actual content
{ Image {
id: contextButton id: previewImage;
text: "\u22EE" //Unicode; Three stacked points. anchors.fill: parent;
width: 35 opacity: printJob && printJob.state == "error" ? 0.5 : 1.0;
height: width source: printJob ? printJob.previewImageUrl : "";
anchors visible: printJob;
{
right: parent.right
top: parent.top
}
hoverEnabled: true
background: Rectangle
{
opacity: contextButton.down || contextButton.hovered ? 1 : 0
width: contextButton.width
height: contextButton.height
radius: 0.5 * width
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: contextButton.text
color: UM.Theme.getColor("monitor_text_inactive")
font.pixelSize: 25
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} }
onClicked: parent.switchPopupState() UM.RecolorImage {
id: ultiBotImage;
anchors.centerIn: printJobPreview;
color: UM.Theme.getColor("monitor_placeholder_image");
height: printJobPreview.height;
source: "../svg/ultibot.svg";
sourceSize {
height: height;
width: width;
}
/* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or
not in order to determine if we show the placeholder (ultibot) image instead. */
visible: printJob && previewImage.status == Image.Error;
width: printJobPreview.width;
} }
Popup UM.RecolorImage {
{ id: statusImage;
// TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property anchors.centerIn: printJobPreview;
id: popup color: UM.Theme.getColor("monitor_image_overlay");
clip: true height: 0.5 * printJobPreview.height;
closePolicy: Popup.CloseOnPressOutside source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : "";
x: (parent.width - width) + 26 * screenScaleFactor sourceSize {
y: contextButton.height - 5 * screenScaleFactor // Because shadow height: height;
width: 182 * screenScaleFactor width: width;
height: contentItem.height + 2 * padding
visible: false
padding: 5 * screenScaleFactor // Because shadow
transformOrigin: Popup.Top
contentItem: Item
{
width: popup.width
height: childrenRect.height + 36 * screenScaleFactor
anchors.topMargin: 10 * screenScaleFactor
anchors.bottomMargin: 10 * screenScaleFactor
Button
{
id: sendToTopButton
text: catalog.i18nc("@label", "Move to top")
onClicked:
{
sendToTopConfirmationDialog.visible = true;
popup.close();
} }
width: parent.width visible: source != "";
enabled: OutputDevice.queuedPrintJobs[0].key != printJob.key width: 0.5 * printJobPreview.width;
visible: enabled
anchors.top: parent.top
anchors.topMargin: 18 * screenScaleFactor
height: visible ? 39 * screenScaleFactor : 0 * screenScaleFactor
hoverEnabled: true
background: Rectangle
{
opacity: sendToTopButton.down || sendToTopButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: sendToTopButton.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
}
MessageDialog
{
id: sendToTopConfirmationDialog
title: catalog.i18nc("@window:title", "Move print job to top")
icon: StandardIcon.Warning
text: catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to move %1 to the top of the queue?").arg(printJob.name)
standardButtons: StandardButton.Yes | StandardButton.No
Component.onCompleted: visible = false
onYes: OutputDevice.sendJobToTop(printJob.key)
}
Button
{
id: deleteButton
text: catalog.i18nc("@label", "Delete")
onClicked:
{
deleteConfirmationDialog.visible = true;
popup.close();
}
width: parent.width
height: 39 * screenScaleFactor
anchors.top: sendToTopButton.bottom
hoverEnabled: true
background: Rectangle
{
opacity: deleteButton.down || deleteButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
contentItem: Label
{
text: deleteButton.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
}
MessageDialog
{
id: deleteConfirmationDialog
title: catalog.i18nc("@window:title", "Delete print job")
icon: StandardIcon.Warning
text: catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to delete %1?").arg(printJob.name)
standardButtons: StandardButton.Yes | StandardButton.No
Component.onCompleted: visible = false
onYes: OutputDevice.deleteJobFromQueue(printJob.key)
}
}
background: Item
{
width: popup.width
height: popup.height
DropShadow
{
anchors.fill: pointedRectangle
radius: 5
color: "#3F000000" // 25% shadow
source: pointedRectangle
transparentBorder: true
verticalOffset: 2
}
Item
{
id: pointedRectangle
width: parent.width - 10 * screenScaleFactor // Because of the shadow
height: parent.height - 10 * screenScaleFactor // Because of the shadow
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
id: point
height: 14 * screenScaleFactor
width: 14 * screenScaleFactor
color: UM.Theme.getColor("setting_control")
transform: Rotation { angle: 45}
anchors.right: bloop.right
anchors.rightMargin: 24
y: 1
}
Rectangle
{
id: bloop
color: UM.Theme.getColor("setting_control")
width: parent.width
anchors.top: parent.top
anchors.topMargin: 8 * screenScaleFactor // Because of the shadow + point
anchors.bottom: parent.bottom
anchors.bottomMargin: 8 * screenScaleFactor // Because of the shadow
} }
} }
} }
exit: Transition // Divider
{ Rectangle {
// This applies a default NumberAnimation to any changes a state change makes to x or y properties anchors {
NumberAnimation { property: "visible"; duration: 75; } horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
} }
enter: Transition color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light");
{ height: parent.height - 2 * UM.Theme.getSize("default_margin").height;
// This applies a default NumberAnimation to any changes a state change makes to x or y properties width: UM.Theme.getSize("default_lining").width;
NumberAnimation { property: "visible"; duration: 75; }
} }
onClosed: visible = false // Right content
onOpened: visible = true Item {
anchors {
bottom: parent.bottom;
left: parent.horizontalCenter;
margins: UM.Theme.getSize("wide_margin").width;
right: parent.right;
top: parent.top;
} }
Row Item {
{ id: targetPrinterLabel;
id: printerFamilyPills height: UM.Theme.getSize("monitor_text_line").height;
spacing: 0.5 * UM.Theme.getSize("default_margin").width width: parent.width;
anchors
{
left: parent.left
right: parent.right
bottom: extrudersInfo.top
bottomMargin: UM.Theme.getSize("default_margin").height
}
height: childrenRect.height
Repeater
{
model: printJob.compatibleMachineFamilies
delegate: PrinterFamilyPill Rectangle {
{ visible: !printJob;
text: modelData color: UM.Theme.getColor("monitor_skeleton_fill");
color: UM.Theme.getColor("viewport_background") anchors.fill: parent;
padding: 3 * screenScaleFactor
}
}
}
// PrintCore && Material config
Row
{
id: extrudersInfo
anchors.bottom: parent.bottom
anchors
{
left: parent.left
right: parent.right
}
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
PrintCoreConfiguration
{
id: leftExtruderInfo
width: Math.round(parent.width / 2) * screenScaleFactor
printCoreConfiguration: printJob.configuration.extruderConfigurations[0]
} }
PrintCoreConfiguration Label {
{ color: UM.Theme.getColor("text");
id: rightExtruderInfo elide: Text.ElideRight;
width: Math.round(parent.width / 2) * screenScaleFactor font: UM.Theme.getFont("default_bold");
printCoreConfiguration: printJob.configuration.extruderConfigurations[1] text: {
if (printJob !== null) {
if (printJob.assignedPrinter == null) {
if (printJob.state == "error") {
return catalog.i18nc("@label", "Waiting for: Unavailable printer");
}
return catalog.i18nc("@label", "Waiting for: First available");
} else {
return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name;
}
}
return "";
}
visible: printJob;
} }
} }
PrinterInfoBlock {
anchors.bottom: parent.bottom;
printer: root.printJon && root.printJob.assignedPrinter;
printJob: root.printJob;
}
} }
Rectangle PrintJobContextMenu {
{ id: contextButton;
color: UM.Theme.getColor("viewport_background") anchors {
width: 2 * screenScaleFactor right: mainContent.right;
anchors.top: parent.top rightMargin: UM.Theme.getSize("default_margin").width * 3 + root.shadowRadius;
anchors.bottom: parent.bottom top: mainContent.top;
anchors.margins: UM.Theme.getSize("default_margin").height topMargin: UM.Theme.getSize("default_margin").height;
anchors.horizontalCenter: parent.horizontalCenter
} }
printJob: root.printJob;
visible: root.printJob;
}
}
Item {
id: configChangesBox;
height: childrenRect.height;
visible: printJob && printJob.configurationChanges.length !== 0;
width: parent.width;
// Config change toggle
Rectangle {
id: configChangeToggle;
color: {
if (configChangeToggleArea.containsMouse) {
return UM.Theme.getColor("viewport_background"); // TODO: Theme!
} else {
return "transparent";
}
}
width: parent.width;
height: UM.Theme.getSize("default_margin").height * 4; // TODO: Theme!
anchors {
left: parent.left;
right: parent.right;
top: parent.top;
}
Rectangle {
color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light");
height: UM.Theme.getSize("default_lining").height;
width: parent.width;
}
UM.RecolorImage {
anchors {
right: configChangeToggleLabel.left;
rightMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 23 * screenScaleFactor; // TODO: Theme!
source: "../svg/warning-icon.svg";
sourceSize {
height: height;
width: width;
}
width: 23 * screenScaleFactor; // TODO: Theme!
}
Label {
id: configChangeToggleLabel;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
text: catalog.i18nc("@label", "Configuration change");
}
UM.RecolorImage {
anchors {
left: configChangeToggleLabel.right;
leftMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 15 * screenScaleFactor; // TODO: Theme!
source: {
if (configChangeDetails.visible) {
return UM.Theme.getIcon("arrow_top");
} else {
return UM.Theme.getIcon("arrow_bottom");
}
}
sourceSize {
width: width;
height: height;
}
width: 15 * screenScaleFactor; // TODO: Theme!
}
MouseArea {
id: configChangeToggleArea;
anchors.fill: parent;
hoverEnabled: true;
onClicked: {
configChangeDetails.visible = !configChangeDetails.visible;
}
}
}
// Config change details
Item {
id: configChangeDetails;
anchors.top: configChangeToggle.bottom;
Behavior on height { NumberAnimation { duration: 100 } }
// In case of really massive multi-line configuration changes
height: visible ? Math.max(UM.Theme.getSize("monitor_config_override_box").height, childrenRect.height) : 0;
visible: false;
width: parent.width;
Item {
anchors {
bottomMargin: UM.Theme.getSize("wide_margin").height;
fill: parent;
leftMargin: UM.Theme.getSize("wide_margin").height * 4;
rightMargin: UM.Theme.getSize("wide_margin").height * 4;
topMargin: UM.Theme.getSize("wide_margin").height;
}
clip: true;
Label {
anchors.fill: parent;
elide: Text.ElideRight;
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("large_nonbold");
text: {
if (!printJob || printJob.configurationChanges.length === 0) {
return "";
}
var topLine;
if (materialsAreKnown(printJob)) {
topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printJob.assignedPrinter.name);
} else {
topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printJob.assignedPrinter.name);
}
var result = "<p>" + topLine +"</p>";
for (var i = 0; i < printJob.configurationChanges.length; i++) {
var change = printJob.configurationChanges[i];
var text;
switch (change.typeOfChange) {
case "material_change":
text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
break;
case "material_insert":
text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName);
break;
case "print_core_change":
text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
break;
case "buildplate_change":
text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name));
break;
default:
text = "";
}
result += "<p><b>" + text + "</b></p>";
}
return result;
}
wrapMode: Text.WordWrap;
}
Button {
anchors {
bottom: parent.bottom;
left: parent.left;
}
onClicked: {
overrideConfirmationDialog.visible = true;
}
text: catalog.i18nc("@label", "Override");
visible: {
if (printJob && printJob.configurationChanges) {
var length = printJob.configurationChanges.length;
for (var i = 0; i < length; i++) {
var typeOfChange = printJob.configurationChanges[i].typeOfChange;
if (typeOfChange === "material_insert" || typeOfChange === "buildplate_change") {
return false;
}
}
}
return true;
}
}
}
}
MessageDialog {
id: overrideConfirmationDialog;
Component.onCompleted: visible = false;
icon: StandardIcon.Warning;
onYes: OutputDevice.forceSendJob(printJob.key);
standardButtons: StandardButton.Yes | StandardButton.No;
text: {
if (!printJob) {
return "";
}
var printJobName = formatPrintJobName(printJob.name);
var confirmText = catalog.i18nc("@label", "Starting a print job with an incompatible configuration could damage your 3D printer. Are you sure you want to override the configuration and print %1?").arg(printJobName);
return confirmText;
}
title: catalog.i18nc("@window:title", "Override configuration configuration and start print");
}
}
}
}
// Utils
function formatPrintJobName(name) {
var extensions = [ ".gz", ".gcode", ".ufp" ];
for (var i = 0; i < extensions.length; i++) {
var extension = extensions[i];
if (name.slice(-extension.length) === extension) {
name = name.substring(0, name.length - extension.length);
}
}
return name;
}
function materialsAreKnown(job) {
var conf0 = job.configuration[0];
if (conf0 && !conf0.material.material) {
return false;
}
var conf1 = job.configuration[1];
if (conf1 && !conf1.material.material) {
return false;
}
return true;
}
function formatBuildPlateType(buildPlateType) {
var translationText = "";
switch (buildPlateType) {
case "glass":
translationText = catalog.i18nc("@label", "Glass");
break;
case "aluminum":
translationText = catalog.i18nc("@label", "Aluminum");
break;
default:
translationText = null;
}
return translationText;
} }
} }

View file

@ -0,0 +1,75 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
// Includes print job name, owner, and preview
Item {
property var job: null;
property var useUltibot: false;
height: 100 * screenScaleFactor;
width: height;
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
radius: UM.Theme.getSize("default_margin").width;
visible: !job;
}
// Actual content
Image {
id: previewImage;
visible: job;
source: job ? job.previewImageUrl : "";
opacity: {
if (job == null) {
return 1.0;
}
var states = ["wait_cleanup", "wait_user_action", "error", "paused"];
if (states.indexOf(job.state) !== -1) {
return 0.5;
}
return 1.0;
}
anchors.fill: parent;
}
UM.RecolorImage {
id: ultibotImage;
anchors.centerIn: parent;
color: UM.Theme.getColor("monitor_placeholder_image"); // TODO: Theme!
height: parent.height;
source: "../svg/ultibot.svg";
sourceSize {
height: height;
width: width;
}
/* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or
not in order to determine if we show the placeholder (ultibot) image instead. */
visible: job && previewImage.status == Image.Error;
width: parent.width;
}
UM.RecolorImage {
id: statusImage;
anchors.centerIn: parent;
color: "black"; // TODO: Theme!
height: Math.round(0.5 * parent.height);
source: job && job.state == "error" ? "../svg/aborted-icon.svg" : "";
sourceSize {
height: height;
width: width;
}
visible: source != "";
width: Math.round(0.5 * parent.width);
}
}

View file

@ -0,0 +1,59 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
Column {
property var job: null;
height: childrenRect.height;
spacing: Math.floor( UM.Theme.getSize("default_margin").height / 2); // TODO: Use explicit theme size
width: parent.width;
Item {
id: jobName;
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
// Skeleton loading
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !job;
width: Math.round(parent.width / 3);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: job && job.name ? job.name : "";
visible: job;
}
}
Item {
id: ownerName;
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
// Skeleton loading
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !job;
width: Math.round(parent.width / 2);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text")
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: job ? job.owner : "";
visible: job;
}
}
}

View file

@ -4,112 +4,100 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import QtQuick.Controls 1.2 import QtQuick.Controls 1.2
import UM 1.1 as UM import UM 1.1 as UM
UM.Dialog UM.Dialog {
{
id: base; id: base;
height: minimumHeight;
minimumWidth: 500 * screenScaleFactor
minimumHeight: 140 * screenScaleFactor
maximumWidth: minimumWidth
maximumHeight: minimumHeight
width: minimumWidth
height: minimumHeight
visible: true
modality: Qt.ApplicationModal
onVisibleChanged:
{
if(visible)
{
resetPrintersModel()
}
else
{
OutputDevice.cancelPrintSelection()
}
}
title: catalog.i18nc("@title:window", "Print over network")
property var printersModel: ListModel{}
function resetPrintersModel() {
printersModel.clear()
printersModel.append({ name: "Automatic", key: ""})
for (var index in OutputDevice.printers)
{
printersModel.append({name: OutputDevice.printers[index].name, key: OutputDevice.printers[index].key})
}
}
Column
{
id: printerSelection
anchors.fill: parent
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.rightMargin: UM.Theme.getSize("default_margin").width
height: 50 * screenScaleFactor
Label
{
id: manualPrinterSelectionLabel
anchors
{
left: parent.left
topMargin: UM.Theme.getSize("default_margin").height
right: parent.right
}
text: catalog.i18nc("@label", "Printer selection")
wrapMode: Text.Wrap
height: 20 * screenScaleFactor
}
ComboBox
{
id: printerSelectionCombobox
model: base.printersModel
textRole: "name"
width: parent.width
height: 40 * screenScaleFactor
Behavior on height { NumberAnimation { duration: 100 } }
}
SystemPalette
{
id: palette
}
UM.I18nCatalog { id: catalog; name: "cura"; }
}
leftButtons: [ leftButtons: [
Button Button {
{ enabled: true;
text: catalog.i18nc("@action:button","Cancel")
enabled: true
onClicked: { onClicked: {
base.visible = false; base.visible = false;
printerSelectionCombobox.currentIndex = 0 printerSelectionCombobox.currentIndex = 0;
OutputDevice.cancelPrintSelection() OutputDevice.cancelPrintSelection();
} }
text: catalog.i18nc("@action:button","Cancel");
} }
] ]
maximumHeight: minimumHeight;
maximumWidth: minimumWidth;
minimumHeight: 140 * screenScaleFactor;
minimumWidth: 500 * screenScaleFactor;
modality: Qt.ApplicationModal;
onVisibleChanged: {
if (visible) {
resetPrintersModel();
} else {
OutputDevice.cancelPrintSelection();
}
}
rightButtons: [ rightButtons: [
Button Button {
{ enabled: true;
text: catalog.i18nc("@action:button","Print")
enabled: true
onClicked: { onClicked: {
base.visible = false; base.visible = false;
OutputDevice.selectPrinter(printerSelectionCombobox.model.get(printerSelectionCombobox.currentIndex).key) OutputDevice.selectPrinter(printerSelectionCombobox.model.get(printerSelectionCombobox.currentIndex).key);
// reset to defaults // reset to defaults
printerSelectionCombobox.currentIndex = 0 printerSelectionCombobox.currentIndex = 0;
} }
text: catalog.i18nc("@action:button","Print");
} }
] ]
title: catalog.i18nc("@title:window", "Print over network");
visible: true;
width: minimumWidth;
Column {
id: printerSelection;
anchors {
fill: parent;
leftMargin: UM.Theme.getSize("default_margin").width;
rightMargin: UM.Theme.getSize("default_margin").width;
top: parent.top;
topMargin: UM.Theme.getSize("default_margin").height;
}
height: 50 * screenScaleFactor;
SystemPalette {
id: palette;
}
UM.I18nCatalog {
id: catalog;
name: "cura";
}
Label {
id: manualPrinterSelectionLabel;
anchors {
left: parent.left;
right: parent.right;
topMargin: UM.Theme.getSize("default_margin").height;
}
height: 20 * screenScaleFactor;
text: catalog.i18nc("@label", "Printer selection");
wrapMode: Text.Wrap;
}
ComboBox {
id: printerSelectionCombobox;
Behavior on height { NumberAnimation { duration: 100 } }
height: 40 * screenScaleFactor;
model: ListModel {
id: printersModel;
}
textRole: "name";
width: parent.width;
}
}
// Utils
function resetPrintersModel() {
printersModel.clear();
printersModel.append({ name: "Automatic", key: ""});
for (var index in OutputDevice.printers) {
printersModel.append({name: OutputDevice.printers[index].name, key: OutputDevice.printers[index].key});
}
}
} }

View file

@ -0,0 +1,239 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import UM 1.3 as UM
Item {
id: root;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var shadowOffset: UM.Theme.getSize("monitor_shadow_offset").width;
property var printer: null;
property var collapsed: true;
height: childrenRect.height + shadowRadius * 2; // Bubbles upward
width: parent.width; // Bubbles downward
// The actual card (white block)
Rectangle {
// 5px margin, but shifted 2px vertically because of the shadow
anchors {
bottomMargin: root.shadowRadius + root.shadowOffset;
leftMargin: root.shadowRadius;
rightMargin: root.shadowRadius;
topMargin: root.shadowRadius - root.shadowOffset;
}
color: {
if (!printer) {
return UM.Theme.getColor("monitor_card_background_inactive");
}
if (printer.state == "disabled") {
return UM.Theme.getColor("monitor_card_background_inactive");
} else {
return UM.Theme.getColor("monitor_card_background");
}
}
height: childrenRect.height;
layer.effect: DropShadow {
radius: root.shadowRadius;
verticalOffset: root.shadowOffset;
color: "#3F000000"; // 25% shadow
}
layer.enabled: true
width: parent.width - 2 * shadowRadius;
Column {
height: childrenRect.height;
width: parent.width;
// Main card
Item {
id: mainCard;
height: 60 * screenScaleFactor + 2 * UM.Theme.getSize("default_margin").width;
width: parent.width;
// Machine icon
Item {
id: machineIcon;
anchors {
leftMargin: UM.Theme.getSize("wide_margin").width;
top: parent.top;
left: parent.left;
margins: UM.Theme.getSize("default_margin").width;
}
height: parent.height - 2 * UM.Theme.getSize("default_margin").width;
width: height;
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
radius: UM.Theme.getSize("default_margin").width;
visible: !printer;
}
// Content
UM.RecolorImage {
anchors.centerIn: parent;
color: {
if (printer && printer.activePrintJob != undefined) {
return UM.Theme.getColor("monitor_printer_icon");
}
return UM.Theme.getColor("monitor_printer_icon_inactive");
}
height: sourceSize.height;
source: {
if (!printer) {
return "";
}
switch(printer.type) {
case "Ultimaker 3":
return "../svg/UM3-icon.svg";
case "Ultimaker 3 Extended":
return "../svg/UM3x-icon.svg";
case "Ultimaker S5":
return "../svg/UMs5-icon.svg";
}
}
visible: printer;
width: sourceSize.width;
}
}
// Printer info
Item {
id: printerInfo;
anchors {
left: machineIcon.right;
leftMargin: UM.Theme.getSize("default_margin").width;
right: collapseIcon.left;
verticalCenter: machineIcon.verticalCenter;
}
height: childrenRect.height;
// Machine name
Item {
id: machineNameLabel;
height: UM.Theme.getSize("monitor_text_line").height;
width: {
var percent = printer ? 0.75 : 0.3;
return Math.round(parent.width * percent);
}
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
visible: !printer;
}
// Actual content
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: printer ? printer.name : "";
visible: printer;
width: parent.width;
}
}
// Job name
Item {
id: activeJobLabel;
anchors {
top: machineNameLabel.bottom;
topMargin: Math.round(UM.Theme.getSize("default_margin").height / 2);
}
height: UM.Theme.getSize("monitor_text_line").height;
width: Math.round(parent.width * 0.75);
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
visible: !printer;
}
// Actual content
Label {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_text_inactive");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: {
if (!printer) {
return "";
}
if (printer.state == "disabled") {
return catalog.i18nc("@label", "Not available");
} else if (printer.state == "unreachable") {
return catalog.i18nc("@label", "Unreachable");
}
if (printer.activePrintJob != null && printer.activePrintJob.name) {
return printer.activePrintJob.name;
}
return catalog.i18nc("@label", "Available");
}
visible: printer;
}
}
}
// Collapse icon
UM.RecolorImage {
id: collapseIcon;
anchors {
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 15 * screenScaleFactor; // TODO: Theme!
source: root.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom");
sourceSize {
height: height;
width: width;
}
visible: printer;
width: 15 * screenScaleFactor; // TODO: Theme!
}
MouseArea {
anchors.fill: parent;
enabled: printer;
onClicked: {
if (model && root.collapsed) {
printerList.currentIndex = model.index;
} else {
printerList.currentIndex = -1;
}
}
}
Connections {
target: printerList;
onCurrentIndexChanged: {
root.collapsed = printerList.currentIndex != model.index;
}
}
}
// Detailed card
PrinterCardDetails {
collapsed: root.collapsed;
printer: root.printer;
visible: root.printer;
}
// Progress bar
PrinterCardProgressBar {
visible: printer && printer.activePrintJob != null;
}
}
}
}

View file

@ -0,0 +1,84 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
Item {
id: root;
property var printer: null;
property var printJob: printer ? printer.activePrintJob : null;
property var collapsed: true;
Behavior on height { NumberAnimation { duration: 100 } }
Behavior on opacity { NumberAnimation { duration: 100 } }
height: collapsed ? 0 : childrenRect.height;
opacity: collapsed ? 0 : 1;
width: parent.width;
Column {
id: contentColumn;
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
}
height: childrenRect.height + UM.Theme.getSize("wide_margin").height;
spacing: UM.Theme.getSize("default_margin").height;
width: parent.width;
HorizontalLine {}
PrinterInfoBlock {
printer: root.printer;
printJob: root.printer ? root.printer.activePrintJob : null;
}
HorizontalLine {
visible: root.printJob;
}
Row {
height: childrenRect.height;
visible: root.printJob;
width: parent.width;
PrintJobTitle {
job: root.printer ? root.printer.activePrintJob : null;
}
PrintJobContextMenu {
id: contextButton;
anchors {
right: parent.right;
rightMargin: UM.Theme.getSize("wide_margin").width;
}
printJob: root.printer ? root.printer.activePrintJob : null;
visible: printJob;
}
}
PrintJobPreview {
anchors.horizontalCenter: parent.horizontalCenter;
job: root.printer && root.printer.activePrintJob ? root.printer.activePrintJob : null;
visible: root.printJob;
}
}
CameraButton {
id: showCameraButton;
anchors {
bottom: contentColumn.bottom;
bottomMargin: Math.round(1.5 * UM.Theme.getSize("default_margin").height);
left: contentColumn.left;
leftMargin: Math.round(0.5 * UM.Theme.getSize("default_margin").width);
}
iconSource: "../svg/camera-icon.svg";
visible: root.printJob;
}
}

View file

@ -0,0 +1,108 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Controls 1.4
import UM 1.3 as UM
ProgressBar {
property var progress: {
if (!printer || printer.activePrintJob == null) {
return 0;
}
var result = printer.activePrintJob.timeElapsed / printer.activePrintJob.timeTotal;
if (result > 1.0) {
result = 1.0;
}
return result;
}
style: ProgressBarStyle {
property var remainingTime: {
if (!printer || printer.activePrintJob == null) {
return 0;
}
/* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining
time from ever being less than 0. Negative durations cause strange behavior such
as displaying "-1h -1m". */
return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0);
}
property var progressText: {
if (printer === null ) {
return "";
}
switch (printer.activePrintJob.state) {
case "wait_cleanup":
if (printer.activePrintJob.timeTotal > printer.activePrintJob.timeElapsed) {
return catalog.i18nc("@label:status", "Aborted");
}
return catalog.i18nc("@label:status", "Finished");
case "pre_print":
case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing");
case "aborted":
return catalog.i18nc("@label:status", "Aborted");
case "wait_user_action":
return catalog.i18nc("@label:status", "Aborted");
case "pausing":
return catalog.i18nc("@label:status", "Pausing");
case "paused":
return OutputDevice.formatDuration( remainingTime );
case "resuming":
return catalog.i18nc("@label:status", "Resuming");
case "queued":
return catalog.i18nc("@label:status", "Action required");
default:
return OutputDevice.formatDuration( remainingTime );
}
}
background: Rectangle {
color: UM.Theme.getColor("monitor_progress_background");
implicitHeight: visible ? 24 : 0;
implicitWidth: 100;
}
progress: Rectangle {
id: progressItem;
color: {
if (! printer || !printer.activePrintJob) {
return "black";
}
var state = printer.activePrintJob.state
var inactiveStates = [
"pausing",
"paused",
"resuming",
"wait_cleanup"
];
if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) {
return UM.Theme.getColor("monitor_progress_fill_inactive");
} else {
return UM.Theme.getColor("monitor_progress_fill");
}
}
Label {
id: progressLabel;
anchors {
left: parent.left;
leftMargin: getTextOffset();
}
text: progressText;
anchors.verticalCenter: parent.verticalCenter;
color: progressItem.width + progressLabel.width < control.width ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_progress_fill_text");
width: contentWidth;
font: UM.Theme.getFont("default");
}
function getTextOffset() {
if (progressItem.width + progressLabel.width + 16 < control.width) {
return progressItem.width + UM.Theme.getSize("default_margin").width;
} else {
return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width;
}
}
}
}
value: progress;
width: parent.width;
}

View file

@ -1,28 +1,32 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import UM 1.2 as UM import UM 1.2 as UM
Item Item {
{ property alias text: familyNameLabel.text;
property alias color: background.color property var padding: 3 * screenScaleFactor; // TODO: Theme!
property alias text: familyNameLabel.text implicitHeight: familyNameLabel.contentHeight + 2 * padding; // Apply the padding to top and bottom.
property var padding: 0 implicitWidth: Math.max(48 * screenScaleFactor, familyNameLabel.contentWidth + implicitHeight); // The extra height is added to ensure the radius doesn't cut something off.
implicitHeight: familyNameLabel.contentHeight + 2 * padding // Apply the padding to top and bottom.
implicitWidth: familyNameLabel.contentWidth + implicitHeight // The extra height is added to ensure the radius doesn't cut something off. Rectangle {
Rectangle id: background;
{ anchors {
id: background horizontalCenter: parent.horizontalCenter;
height: parent.height right: parent.right;
width: parent.width
color: parent.color
anchors.right: parent.right
anchors.horizontalCenter: parent.horizontalCenter
radius: 0.5 * height
} }
Label color: familyNameLabel.text.length < 1 ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_pill_background");
{ height: parent.height;
id: familyNameLabel radius: 0.5 * height;
anchors.centerIn: parent width: parent.width;
text: "" }
Label {
id: familyNameLabel;
anchors.centerIn: parent;
color: UM.Theme.getColor("text");
text: "";
} }
} }

View file

@ -0,0 +1,83 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
// Includes printer type pill and extuder configurations
Item {
id: root;
property var printer: null;
property var printJob: null;
width: parent.width;
height: childrenRect.height;
// Printer family pills
Row {
id: printerFamilyPills;
anchors {
left: parent.left;
right: parent.right;
}
height: childrenRect.height;
spacing: Math.round(0.5 * UM.Theme.getSize("default_margin").width);
width: parent.width;
Repeater {
id: compatiblePills;
delegate: PrinterFamilyPill {
text: modelData;
}
model: printJob ? printJob.compatibleMachineFamilies : [];
visible: printJob;
}
PrinterFamilyPill {
text: printer ? printer.type : "";
visible: !compatiblePills.visible && printer;
}
}
// Extruder info
Row {
id: extrudersInfo;
anchors {
left: parent.left;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
top: printerFamilyPills.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
height: childrenRect.height;
spacing: UM.Theme.getSize("default_margin").width;
width: parent.width;
PrintCoreConfiguration {
width: Math.round(parent.width / 2) * screenScaleFactor;
printCoreConfiguration: getExtruderConfig(0);
}
PrintCoreConfiguration {
width: Math.round(parent.width / 2) * screenScaleFactor;
printCoreConfiguration: getExtruderConfig(1);
}
}
function getExtruderConfig( i ) {
if (root.printJob) {
// Use more-specific print job if possible
return root.printJob.configuration.extruderConfigurations[i];
}
if (root.printer) {
return root.printer.printerConfiguration.extruderConfigurations[i];
}
return null;
}
}

View file

@ -1,84 +1,75 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura
Item {
property var camera: null;
Item Rectangle {
{ anchors.fill:parent;
property var camera: null color: UM.Theme.getColor("viewport_overlay");
opacity: 0.5;
Rectangle
{
anchors.fill:parent
color: UM.Theme.getColor("viewport_overlay")
opacity: 0.5
} }
MouseArea MouseArea {
{ anchors.fill: parent;
anchors.fill: parent onClicked: OutputDevice.setActiveCamera(null);
onClicked: OutputDevice.setActiveCamera(null) z: 0;
z: 0
} }
CameraButton CameraButton {
{ id: closeCameraButton;
id: closeCameraButton anchors {
iconSource: UM.Theme.getIcon("cross1")
anchors
{
top: cameraImage.top
topMargin: UM.Theme.getSize("default_margin").height
right: cameraImage.right right: cameraImage.right
rightMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width
top: cameraImage.top
topMargin: UM.Theme.getSize("default_margin").height
} }
z: 999 iconSource: UM.Theme.getIcon("cross1");
z: 999;
} }
Image Cura.CameraView {
{
id: cameraImage id: cameraImage
width: Math.min(sourceSize.width === 0 ? 800 * screenScaleFactor : sourceSize.width, maximumWidth) anchors.horizontalCenter: parent.horizontalCenter;
height: Math.round((sourceSize.height === 0 ? 600 * screenScaleFactor : sourceSize.height) * width / sourceSize.width) anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter height: Math.round((imageHeight === 0 ? 600 * screenScaleFactor : imageHeight) * width / imageWidth);
anchors.verticalCenter: parent.verticalCenter onVisibleChanged: {
z: 1 if (visible) {
onVisibleChanged: if (camera != null) {
{ camera.start();
if(visible)
{
if(camera != null)
{
camera.start()
} }
} else } else {
{ if (camera != null) {
if(camera != null) camera.stop();
{
camera.stop()
} }
} }
} }
source: Connections
{ {
if(camera != null && camera.latestImage != null) target: camera
{ onNewImage: {
return camera.latestImage; if (cameraImage.visible) {
cameraImage.image = camera.latestImage;
cameraImage.update();
} }
return "";
} }
} }
width: Math.min(imageWidth === 0 ? 800 * screenScaleFactor : imageWidth, maximumWidth);
z: 1
}
MouseArea MouseArea {
{ anchors.fill: cameraImage;
anchors.fill: cameraImage onClicked: {
onClicked: OutputDevice.setActiveCamera(null);
{
OutputDevice.setActiveCamera(null)
} }
z: 1 z: 1;
} }
} }

View file

@ -1,125 +1,126 @@
import UM 1.2 as UM // Copyright (c) 2018 Ultimaker B.V.
import Cura 1.0 as Cura // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.1 import QtQuick.Window 2.1
import UM 1.2 as UM
import Cura 1.0 as Cura
Item Item {
{ id: base;
id: base property string activeQualityDefinitionId: Cura.MachineManager.activeQualityDefinitionId;
property bool isUM3: activeQualityDefinitionId == "ultimaker3" || activeQualityDefinitionId.match("ultimaker_") != null;
property bool printerConnected: Cura.MachineManager.printerConnected;
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands;
property bool authenticationRequested: printerConnected && (Cura.MachineManager.printerOutputDevices[0].authenticationState == 2 || Cura.MachineManager.printerOutputDevices[0].authenticationState == 5); // AuthState.AuthenticationRequested or AuthenticationReceived.
property string activeQualityDefinitionId: Cura.MachineManager.activeQualityDefinitionId UM.I18nCatalog {
property bool isUM3: activeQualityDefinitionId == "ultimaker3" || activeQualityDefinitionId.match("ultimaker_") != null id: catalog;
property bool printerConnected: Cura.MachineManager.printerConnected name: "cura";
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property bool authenticationRequested: printerConnected && (Cura.MachineManager.printerOutputDevices[0].authenticationState == 2 || Cura.MachineManager.printerOutputDevices[0].authenticationState == 5) // AuthState.AuthenticationRequested or AuthenticationReceived.
Row
{
objectName: "networkPrinterConnectButton"
visible: isUM3
spacing: UM.Theme.getSize("default_margin").width
Button
{
height: UM.Theme.getSize("save_button_save_to_button").height
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer")
text: catalog.i18nc("@action:button", "Request Access")
style: UM.Theme.styles.sidebar_action_button
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication()
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested
} }
Button Row {
{ objectName: "networkPrinterConnectButton";
height: UM.Theme.getSize("save_button_save_to_button").height spacing: UM.Theme.getSize("default_margin").width;
tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer") visible: isUM3;
text: catalog.i18nc("@action:button", "Connect")
style: UM.Theme.styles.sidebar_action_button Button {
onClicked: connectActionDialog.show() height: UM.Theme.getSize("save_button_save_to_button").height;
visible: !printerConnected onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication();
style: UM.Theme.styles.sidebar_action_button;
text: catalog.i18nc("@action:button", "Request Access");
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer");
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested;
}
Button {
height: UM.Theme.getSize("save_button_save_to_button").height;
onClicked: connectActionDialog.show();
style: UM.Theme.styles.sidebar_action_button;
text: catalog.i18nc("@action:button", "Connect");
tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer");
visible: !printerConnected;
} }
} }
UM.Dialog UM.Dialog {
{ id: connectActionDialog;
id: connectActionDialog rightButtons: Button {
Loader iconName: "dialog-close";
{ onClicked: connectActionDialog.reject();
anchors.fill: parent text: catalog.i18nc("@action:button", "Close");
source: "DiscoverUM3Action.qml"
} }
rightButtons: Button
{ Loader {
text: catalog.i18nc("@action:button", "Close") anchors.fill: parent;
iconName: "dialog-close" source: "DiscoverUM3Action.qml";
onClicked: connectActionDialog.reject()
} }
} }
Column {
anchors.fill: parent;
objectName: "networkPrinterConnectionInfo";
spacing: UM.Theme.getSize("default_margin").width;
visible: isUM3;
Column Button {
{ onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication();
objectName: "networkPrinterConnectionInfo" text: catalog.i18nc("@action:button", "Request Access");
visible: isUM3 tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer");
spacing: UM.Theme.getSize("default_margin").width visible: printerConnected && !printerAcceptsCommands && !authenticationRequested;
anchors.fill: parent
Button
{
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer")
text: catalog.i18nc("@action:button", "Request Access")
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication()
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested
} }
Row Row {
{ anchors {
visible: printerConnected left: parent.left;
spacing: UM.Theme.getSize("default_margin").width right: parent.right;
}
height: childrenRect.height;
spacing: UM.Theme.getSize("default_margin").width;
visible: printerConnected;
anchors.left: parent.left Column {
anchors.right: parent.right Repeater {
height: childrenRect.height model: Cura.ExtrudersModel {
simpleNames: true;
}
Column Label {
{ text: model.name;
Repeater
{
model: Cura.ExtrudersModel { simpleNames: true }
Label { text: model.name }
}
}
Column
{
Repeater
{
id: nozzleColumn
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].hotendIds : null
Label { text: nozzleColumn.model[index] }
}
}
Column
{
Repeater
{
id: materialColumn
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].materialNames : null
Label { text: materialColumn.model[index] }
} }
} }
} }
Button Column {
{ Repeater {
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura") id: nozzleColumn;
text: catalog.i18nc("@action:button", "Activate Configuration") model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].hotendIds : null;
visible: false // printerConnected && !isClusterPrinter()
onClicked: manager.loadConfigurationFromPrinter() Label {
text: nozzleColumn.model[index];
}
} }
} }
UM.I18nCatalog{id: catalog; name: "cura"} Column {
Repeater {
id: materialColumn;
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].materialNames : null;
Label {
text: materialColumn.model[index];
}
}
}
}
Button {
onClicked: manager.loadConfigurationFromPrinter();
text: catalog.i18nc("@action:button", "Activate Configuration");
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura");
visible: false; // printerConnected && !isClusterPrinter()
}
}
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1 +1,4 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>warning-icon</title><path d="M18.09,1.31A2.35,2.35,0,0,0,16,0a2.31,2.31,0,0,0-2.09,1.31L.27,28.44A2.49,2.49,0,0,0,.11,30.3a2.38,2.38,0,0,0,1.16,1.42A2.33,2.33,0,0,0,2.36,32H29.64A2.4,2.4,0,0,0,32,29.57a2.55,2.55,0,0,0-.27-1.14ZM3.34,29,16,3.83,28.66,29Z"/><polygon points="13.94 25.19 13.94 25.19 13.94 25.19 13.94 25.19"/><polygon points="14.39 21.68 17.61 21.68 18.11 11.85 13.89 11.85 14.39 21.68"/><path d="M16.06,23.08a2.19,2.19,0,0,0-1.56,3.66,2.14,2.14,0,0,0,1.56.55,2.06,2.06,0,0,0,1.54-.55,2.1,2.1,0,0,0,.55-1.55,2.17,2.17,0,0,0-.53-1.55A2.05,2.05,0,0,0,16.06,23.08Z"/></svg> <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<title>warning-icon</title>
<path d="M18.09,1.31A2.35,2.35,0,0,0,16,0a2.31,2.31,0,0,0-2.09,1.31L.27,28.44A2.49,2.49,0,0,0,.11,30.3a2.38,2.38,0,0,0,1.16,1.42A2.33,2.33,0,0,0,2.36,32H29.64A2.4,2.4,0,0,0,32,29.57a2.55,2.55,0,0,0-.27-1.14ZM3.34,29,16,3.83,28.66,29Z"/><polygon points="13.94 25.19 13.94 25.19 13.94 25.19 13.94 25.19"/><polygon points="14.39 21.68 17.61 21.68 18.11 11.85 13.89 11.85 14.39 21.68"/><path d="M16.06,23.08a2.19,2.19,0,0,0-1.56,3.66,2.14,2.14,0,0,0,1.56.55,2.06,2.06,0,0,0,1.54-.55,2.1,2.1,0,0,0,.55-1.55,2.17,2.17,0,0,0-.53-1.55A2.05,2.05,0,0,0,16.06,23.08Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 695 B

Before After
Before After

View file

@ -21,12 +21,13 @@ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.NetworkCamera import NetworkCamera from cura.PrinterOutput.NetworkCamera import NetworkCamera
from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
from .SendMaterialJob import SendMaterialJob from .SendMaterialJob import SendMaterialJob
from .ConfigurationChangeModel import ConfigurationChangeModel
from .UM3PrintJobOutputModel import UM3PrintJobOutputModel
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QDesktopServices, QImage from PyQt5.QtGui import QDesktopServices, QImage
@ -47,6 +48,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
printJobsChanged = pyqtSignal() printJobsChanged = pyqtSignal()
activePrinterChanged = pyqtSignal() activePrinterChanged = pyqtSignal()
activeCameraChanged = pyqtSignal() activeCameraChanged = pyqtSignal()
receivedPrintJobsChanged = pyqtSignal()
# This is a bit of a hack, as the notify can only use signals that are defined by the class that they are in. # This is a bit of a hack, as the notify can only use signals that are defined by the class that they are in.
# Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions. # Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions.
@ -60,7 +62,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._dummy_lambdas = ("", {}, io.BytesIO()) #type: Tuple[str, Dict, Union[io.StringIO, io.BytesIO]] self._dummy_lambdas = ("", {}, io.BytesIO()) #type: Tuple[str, Dict, Union[io.StringIO, io.BytesIO]]
self._print_jobs = [] # type: List[PrintJobOutputModel] self._print_jobs = [] # type: List[UM3PrintJobOutputModel]
self._received_print_jobs = False # type: bool
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml")
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterControlItem.qml") self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterControlItem.qml")
@ -90,7 +93,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str] self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str]
self._finished_jobs = [] # type: List[PrintJobOutputModel] self._finished_jobs = [] # type: List[UM3PrintJobOutputModel]
self._cluster_size = int(properties.get(b"cluster_size", 0)) # type: int self._cluster_size = int(properties.get(b"cluster_size", 0)) # type: int
@ -349,15 +352,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers")) QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers"))
@pyqtProperty("QVariantList", notify = printJobsChanged) @pyqtProperty("QVariantList", notify = printJobsChanged)
def printJobs(self)-> List[PrintJobOutputModel]: def printJobs(self)-> List[UM3PrintJobOutputModel]:
return self._print_jobs return self._print_jobs
@pyqtProperty(bool, notify = receivedPrintJobsChanged)
def receivedPrintJobs(self) -> bool:
return self._received_print_jobs
@pyqtProperty("QVariantList", notify = printJobsChanged) @pyqtProperty("QVariantList", notify = printJobsChanged)
def queuedPrintJobs(self) -> List[PrintJobOutputModel]: def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"] return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"]
@pyqtProperty("QVariantList", notify = printJobsChanged) @pyqtProperty("QVariantList", notify = printJobsChanged)
def activePrintJobs(self) -> List[PrintJobOutputModel]: def activePrintJobs(self) -> List[UM3PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"] return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"]
@pyqtProperty("QVariantList", notify = clusterPrintersChanged) @pyqtProperty("QVariantList", notify = clusterPrintersChanged)
@ -406,6 +413,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# is a modification of the cluster queue and not of the actual job. # is a modification of the cluster queue and not of the actual job.
self.delete("print_jobs/{uuid}".format(uuid = print_job_uuid), on_finished=None) self.delete("print_jobs/{uuid}".format(uuid = print_job_uuid), on_finished=None)
@pyqtSlot(str)
def forceSendJob(self, print_job_uuid: str) -> None:
data = "{\"force\": true}"
self.put("print_jobs/{uuid}".format(uuid=print_job_uuid), data, on_finished=None)
def _printJobStateChanged(self) -> None: def _printJobStateChanged(self) -> None:
username = self._getUserName() username = self._getUserName()
@ -455,6 +467,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self.get("print_jobs/{uuid}/preview_image".format(uuid=print_job.key), on_finished=self._onGetPreviewImageFinished) self.get("print_jobs/{uuid}/preview_image".format(uuid=print_job.key), on_finished=self._onGetPreviewImageFinished)
def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None: def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
self._received_print_jobs = True
self.receivedPrintJobsChanged.emit()
if not checkValidGetReply(reply): if not checkValidGetReply(reply):
return return
@ -537,8 +552,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._printers.append(printer) self._printers.append(printer)
return printer return printer
def _createPrintJobModel(self, data: Dict[str, Any]) -> PrintJobOutputModel: def _createPrintJobModel(self, data: Dict[str, Any]) -> UM3PrintJobOutputModel:
print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self), print_job = UM3PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
key=data["uuid"], name= data["name"]) key=data["uuid"], name= data["name"])
configuration = ConfigurationModel() configuration = ConfigurationModel()
@ -558,7 +573,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
print_job.stateChanged.connect(self._printJobStateChanged) print_job.stateChanged.connect(self._printJobStateChanged)
return print_job return print_job
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict[str, Any]) -> None: def _updatePrintJob(self, print_job: UM3PrintJobOutputModel, data: Dict[str, Any]) -> None:
print_job.updateTimeTotal(data["time_total"]) print_job.updateTimeTotal(data["time_total"])
print_job.updateTimeElapsed(data["time_elapsed"]) print_job.updateTimeElapsed(data["time_elapsed"])
impediments_to_printing = data.get("impediments_to_printing", []) impediments_to_printing = data.get("impediments_to_printing", [])
@ -574,6 +589,16 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if not status_set_by_impediment: if not status_set_by_impediment:
print_job.updateState(data["status"]) print_job.updateState(data["status"])
print_job.updateConfigurationChanges(self._createConfigurationChanges(data["configuration_changes_required"]))
def _createConfigurationChanges(self, data: List[Dict[str, Any]]) -> List[ConfigurationChangeModel]:
result = []
for change in data:
result.append(ConfigurationChangeModel(type_of_change=change["type_of_change"],
index=change["index"],
target_name=change["target_name"],
origin_name=change["origin_name"]))
return result
def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel: def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel:
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"]) containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"])
@ -631,7 +656,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
material = self._createMaterialOutputModel(material_data) material = self._createMaterialOutputModel(material_data)
extruder.updateActiveMaterial(material) extruder.updateActiveMaterial(material)
def _removeJob(self, job: PrintJobOutputModel) -> bool: def _removeJob(self, job: UM3PrintJobOutputModel) -> bool:
if job not in self._print_jobs: if job not in self._print_jobs:
return False return False
@ -675,7 +700,7 @@ def checkValidGetReply(reply: QNetworkReply) -> bool:
return True return True
def findByKey(lst: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]: def findByKey(lst: List[Union[UM3PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[UM3PrintJobOutputModel]:
for item in lst: for item in lst:
if item.key == key: if item.key == key:
return item return item

View file

@ -0,0 +1,29 @@
# 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
class ConfigurationChangeModel(QObject):
def __init__(self, type_of_change: str, index: int, target_name: str, origin_name: str) -> None:
super().__init__()
self._type_of_change = type_of_change
# enum = ["material", "print_core_change"]
self._index = index
self._target_name = target_name
self._origin_name = origin_name
@pyqtProperty(int, constant = True)
def index(self) -> int:
return self._index
@pyqtProperty(str, constant = True)
def typeOfChange(self) -> str:
return self._type_of_change
@pyqtProperty(str, constant = True)
def targetName(self) -> str:
return self._target_name
@pyqtProperty(str, constant = True)
def originName(self) -> str:
return self._origin_name

View file

@ -0,0 +1,29 @@
# 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
from typing import Optional, TYPE_CHECKING, List
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QImage
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from .ConfigurationChangeModel import ConfigurationChangeModel
class UM3PrintJobOutputModel(PrintJobOutputModel):
configurationChangesChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None) -> None:
super().__init__(output_controller, key, name, parent)
self._configuration_changes = [] # type: List[ConfigurationChangeModel]
@pyqtProperty("QVariantList", notify=configurationChangesChanged)
def configurationChanges(self) -> List[ConfigurationChangeModel]:
return self._configuration_changes
def updateConfigurationChanges(self, changes: List[ConfigurationChangeModel]) -> None:
if len(self._configuration_changes) == 0 and len(changes) == 0:
return
self._configuration_changes = changes
self.configurationChangesChanged.emit()

View file

@ -64,6 +64,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._accepts_commands = True self._accepts_commands = True
self._paused = False self._paused = False
self._printer_busy = False # when printer is preheating and waiting (M190/M109), or when waiting for action on the printer
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB")) self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
@ -73,6 +74,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._command_received = Event() self._command_received = Event()
self._command_received.set() self._command_received.set()
self._firmware_name_requested = False
self._firmware_updater = AvrFirmwareUpdater(self) self._firmware_updater = AvrFirmwareUpdater(self)
CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit) CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
@ -223,16 +225,19 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
except: except:
continue continue
if not self._firmware_name_requested:
self._firmware_name_requested = True
self.sendCommand("M115")
if b"FIRMWARE_NAME:" in line:
self._setFirmwareName(line)
if self._last_temperature_request is None or time() > self._last_temperature_request + self._timeout: if self._last_temperature_request is None or time() > self._last_temperature_request + self._timeout:
# Timeout, or no request has been sent at all. # Timeout, or no request has been sent at all.
self._command_received.set() # We haven't really received the ok, but we need to send a new command if not self._printer_busy: # Don't flood the printer with temperature requests while it is busy
self.sendCommand("M105") self.sendCommand("M105")
self._last_temperature_request = time() self._last_temperature_request = time()
if self._firmware_name is None:
self.sendCommand("M115")
if re.search(b"[B|T\d*]: ?\d+\.?\d*", line): # Temperature message. 'T:' for extruder and 'B:' for bed if re.search(b"[B|T\d*]: ?\d+\.?\d*", line): # Temperature message. 'T:' for extruder and 'B:' for bed
extruder_temperature_matches = re.findall(b"T(\d*): ?(\d+\.?\d*) ?\/?(\d+\.?\d*)?", line) extruder_temperature_matches = re.findall(b"T(\d*): ?(\d+\.?\d*) ?\/?(\d+\.?\d*)?", line)
# Update all temperature values # Update all temperature values
@ -264,29 +269,39 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if match[1]: if match[1]:
self._printers[0].updateTargetBedTemperature(float(match[1])) self._printers[0].updateTargetBedTemperature(float(match[1]))
if b"FIRMWARE_NAME:" in line: if line == b"":
self._setFirmwareName(line) # An empty line means that the firmware is idle
# Multiple empty lines probably means that the firmware and Cura are waiting
# for eachother due to a missed "ok", so we keep track of empty lines
self._firmware_idle_count += 1
else:
self._firmware_idle_count = 0
if line.startswith(b"ok") or self._firmware_idle_count > 1:
self._printer_busy = False
if b"ok" in line:
self._command_received.set() self._command_received.set()
if not self._command_queue.empty(): if not self._command_queue.empty():
self._sendCommand(self._command_queue.get()) self._sendCommand(self._command_queue.get())
if self._is_printing: elif self._is_printing:
if self._paused: if self._paused:
pass # Nothing to do! pass # Nothing to do!
else: else:
self._sendNextGcodeLine() self._sendNextGcodeLine()
if line.startswith(b"echo:busy:"):
self._printer_busy = True
if self._is_printing: if self._is_printing:
if line.startswith(b'!!'): if line.startswith(b'!!'):
Logger.log('e', "Printer signals fatal error. Cancelling print. {}".format(line)) Logger.log('e', "Printer signals fatal error. Cancelling print. {}".format(line))
self.cancelPrint() self.cancelPrint()
elif b"resend" in line.lower() or b"rs" in line: elif line.lower().startswith(b"resend") or line.startswith(b"rs"):
# A resend can be requested either by Resend, resend or rs. # A resend can be requested either by Resend, resend or rs.
try: try:
self._gcode_position = int(line.replace(b"N:", b" ").replace(b"N", b" ").replace(b":", b" ").split()[-1]) self._gcode_position = int(line.replace(b"N:", b" ").replace(b"N", b" ").replace(b":", b" ").split()[-1])
except: except:
if b"rs" in line: if line.startswith(b"rs"):
# In some cases of the RS command it needs to be handled differently. # In some cases of the RS command it needs to be handled differently.
self._gcode_position = int(line.split()[1]) self._gcode_position = int(line.split()[1])

View file

@ -0,0 +1,88 @@
{
"version": 2,
"name": "Anycubic 4Max",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Jason Scurtu",
"manufacturer": "Anycubic",
"category": "Other",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"platform": "anycubic_4max_platform.stl",
"has_materials": true,
"quality_definition": "anycubic_4max",
"has_machine_quality": true,
"preferred_quality_type": "normal",
"machine_extruder_trains":
{
"0": "anycubic_4max_extruder_0"
}
},
"overrides":
{
"machine_name": { "default_value": "Anycubic 4Max" },
"machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 220 },
"machine_height": {"default_value": 300 },
"machine_depth": { "default_value": 220 },
"machine_center_is_zero": { "default_value": false },
"machine_max_feedrate_x": { "default_value": 300 },
"machine_max_feedrate_y": { "default_value": 300 },
"machine_max_feedrate_z": { "default_value": 10 },
"machine_acceleration": { "default_value": 1500 },
"machine_max_acceleration_x": { "default_value": 1500 },
"machine_max_acceleration_y": { "default_value": 1500 },
"machine_max_acceleration_z": { "default_value": 100 },
"machine_max_jerk_xy": { "default_value": 11.0 },
"machine_max_jerk_z": { "default_value": 0.4 },
"machine_max_jerk_e": { "default_value": 11.0 },
"jerk_enabled": { "value": "True" },
"jerk_layer_0": { "value": "jerk_topbottom" },
"jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_print": { "value": "11" },
"jerk_support": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_support_interface": { "value": "jerk_topbottom" },
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
"gantry_height": { "default_value": 25.0 },
"skin_overlap": { "value": "10" },
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_print": { "value": "900" },
"acceleration_support": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_support_interface": { "value": "acceleration_topbottom" },
"acceleration_topbottom": { "value": "math.ceil(acceleration_print * 1000 / 3000)" },
"acceleration_travel": { "value": "acceleration_print" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 3000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 1000 / 1000)" },
"speed_layer_0": { "value": "20" },
"speed_print": { "value": "40" },
"speed_support": { "value": "speed_wall_0" },
"speed_support_interface": { "value": "speed_topbottom" },
"speed_topbottom": { "value": "math.ceil(speed_print * 20 / 35)" },
"speed_travel": { "value": "60" },
"speed_wall": { "value": "math.ceil(speed_print * 30 / 35)" },
"speed_wall_0": { "value": "math.ceil(speed_wall * 20 / 30)" },
"speed_wall_x": { "value": "speed_wall" },
"infill_pattern": {"value": "'zigzag'" },
"infill_before_walls": {"value": false },
"adhesion_type": { "default_value": "skirt" },
"material_bed_temperature": { "maximum_value": "150" },
"material_bed_temperature_layer_0": { "maximum_value": "150" },
"machine_gcode_flavor":{"default_value": "RepRap (Marlin/Sprinter)"},
"machine_start_gcode":{"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F{speed_travel} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{speed_travel}\nM117 Printing...\nG5"},
"machine_end_gcode":{"default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle\nto release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops\nso the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000"}
}
}

View file

@ -19,13 +19,13 @@
"default_value": "Creality Ender-3" "default_value": "Creality Ender-3"
}, },
"machine_width": { "machine_width": {
"default_value": 220 "default_value": 235
}, },
"machine_height": { "machine_height": {
"default_value": 250 "default_value": 250
}, },
"machine_depth": { "machine_depth": {
"default_value": 220 "default_value": 235
}, },
"machine_heated_bed": { "machine_heated_bed": {
"default_value": true "default_value": true
@ -87,10 +87,10 @@
"default_value": 5 "default_value": 5
}, },
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "; Ender 3 Custom Start G-code\nM104 S{material_print_temperature_layer_0} ; Set Extruder temperature\nM140 S{material_bed_temperature_layer_0} ; Set Heat Bed temperature\nM190 S{material_bed_temperature_layer_0} ; Wait for Heat Bed temperature\nM109 S{material_print_temperature_layer_0} ; Wait for Extruder temperature\nG28 ; Home all axes\nG92 E0 ; Reset Extruder\nG1 Z5.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed\nG1 X0.1 Y20 Z0.3 F5000.0 ; Move to start position\nG1 X0.1 Y200.0 Z0.3 F1500.0 E15 ; Draw the first line\nG1 X0.4 Y200.0 Z0.3 F5000.0 ; Move to side a little\nG1 X0.4 Y20 Z0.3 F1500.0 E30 ; Draw the second line\nG92 E0 ; Reset Extruder\nG1 Z5.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed" "default_value": "; Ender 3 Custom Start G-code\nG28 ; Home all axes\nG92 E0 ; Reset Extruder\nG1 Z2.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed\nG1 X0.1 Y20 Z0.3 F5000.0 ; Move to start position\nG1 X0.1 Y200.0 Z0.3 F1500.0 E15 ; Draw the first line\nG1 X0.4 Y200.0 Z0.3 F5000.0 ; Move to side a little\nG1 X0.4 Y20 Z0.3 F1500.0 E30 ; Draw the second line\nG92 E0 ; Reset Extruder\nG1 Z2.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed\n; End of custom start GCode"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "; Ender 3 Custom End G-code\nG4 ; Wait\nM220 S100 ; Reset Speed factor override percentage to default (100%)\nM221 S100 ; Reset Extrude factor override percentage to default (100%)\nG91 ; Set coordinates to relative\nG1 F1800 E-3 ; Retract filament 3 mm to prevent oozing\nG1 F3000 Z10 ; Move Z Axis up 10 mm to allow filament ooze freely\nG90 ; Set coordinates to absolute\nG1 X0 Y{machine_depth} F1000 ; Move Heat Bed to the front for easy print removal\nM104 S0 ; Turn off Extruder temperature\nM140 S0 ; Turn off Heat Bed\nM106 S0 ; Turn off Cooling Fan\nM107 ; Turn off Fan\nM84 ; Disable stepper motors" "default_value": "; Ender 3 Custom End G-code\nG4 ; Wait\nM220 S100 ; Reset Speed factor override percentage to default (100%)\nM221 S100 ; Reset Extrude factor override percentage to default (100%)\nG91 ; Set coordinates to relative\nG1 F1800 E-3 ; Retract filament 3 mm to prevent oozing\nG1 F3000 Z20 ; Move Z Axis up 20 mm to allow filament ooze freely\nG90 ; Set coordinates to absolute\nG1 X0 Y{machine_depth} F1000 ; Move Heat Bed to the front for easy print removal\nM84 ; Disable stepper motors\n; End of custom end GCode"
} }
} }
} }

4
resources/definitions/deltacomb.def.json Normal file → Executable file
View file

@ -33,6 +33,7 @@
"material_final_print_temperature": { "value": "material_print_temperature - 5" }, "material_final_print_temperature": { "value": "material_print_temperature - 5" },
"material_initial_print_temperature": { "value": "material_print_temperature" }, "material_initial_print_temperature": { "value": "material_print_temperature" },
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" }, "material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
"material_diameter": { "default_value": 1.75 },
"travel_avoid_distance": { "default_value": 1, "value": "1" }, "travel_avoid_distance": { "default_value": 1, "value": "1" },
"speed_print" : { "default_value": 70 }, "speed_print" : { "default_value": 70 },
"speed_travel": { "value": "150.0" }, "speed_travel": { "value": "150.0" },
@ -55,6 +56,7 @@
"support_use_towers" : { "default_value": false }, "support_use_towers" : { "default_value": false },
"jerk_wall_0" : { "value": "30" }, "jerk_wall_0" : { "value": "30" },
"jerk_travel" : { "default_value": 20 }, "jerk_travel" : { "default_value": 20 },
"acceleration_travel" : { "value": 10000 } "acceleration_travel" : { "value": 10000 },
"machine_max_feedrate_z" : { "default_value": 150 }
} }
} }

View file

@ -4619,7 +4619,7 @@
"enabled": "resolveOrValue('adhesion_type') == 'brim' and support_enable", "enabled": "resolveOrValue('adhesion_type') == 'brim' and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
"limit_to_extruder": "adhesion_extruder_nr" "limit_to_extruder": "support_infill_extruder_nr"
}, },
"brim_outside_only": "brim_outside_only":
{ {
@ -6598,9 +6598,8 @@
"unit": "%", "unit": "%",
"type": "float", "type": "float",
"default_value": 100, "default_value": 100,
"minimum_value": "10", "minimum_value": "0.001",
"minimum_value_warning": "25", "minimum_value_warning": "25",
"maximum_value": "100",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"bridge_settings_enabled": "bridge_settings_enabled":

View file

@ -14,6 +14,8 @@
"preferred_material": "tizyx_pla", "preferred_material": "tizyx_pla",
"has_machine_quality": true, "has_machine_quality": true,
"has_materials": true, "has_materials": true,
"has_variants": true,
"preferred_variant_name": "0.4 mm",
"machine_extruder_trains": "machine_extruder_trains":
{ {
"0": "tizyx_k25_extruder_0" "0": "tizyx_k25_extruder_0"

View file

@ -0,0 +1,16 @@
{
"id": "anycubic_4max_extruder_0",
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "anycubic_4max",
"position": "0"
},
"overrides": {
"extruder_nr": { "default_value": 0 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

0
resources/extruders/deltacomb_extruder_0.def.json Normal file → Executable file
View file

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Copyright (c) 2016 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
@ -9,9 +9,8 @@ import Cura 1.0 as Cura
Menu Menu
{ {
title: catalog.i18nc("@title:menu menubar:toplevel", "&View"); title: catalog.i18nc("@title:menu menubar:toplevel", "&View")
id: base id: base
enabled: !PrintInformation.preSliced
property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel()
@ -26,11 +25,15 @@ Menu
checked: model.active checked: model.active
exclusiveGroup: group exclusiveGroup: group
onTriggered: UM.Controller.setActiveView(model.id) onTriggered: UM.Controller.setActiveView(model.id)
enabled: !PrintInformation.preSliced
} }
onObjectAdded: base.insertItem(index, object) onObjectAdded: base.insertItem(index, object)
onObjectRemoved: base.removeItem(object) onObjectRemoved: base.removeItem(object)
} }
ExclusiveGroup { id: group } ExclusiveGroup
{
id: group
}
MenuSeparator {} MenuSeparator {}
@ -52,26 +55,33 @@ Menu
Menu Menu
{ {
id: buildPlateMenu; id: buildPlateMenu;
title: catalog.i18nc("@action:inmenu menubar:view","&Build plate"); title: catalog.i18nc("@action:inmenu menubar:view","&Build plate")
visible: UM.Preferences.getValue("cura/use_multi_build_plate") visible: UM.Preferences.getValue("cura/use_multi_build_plate")
Instantiator Instantiator
{ {
model: base.multiBuildPlateModel model: base.multiBuildPlateModel
MenuItem { MenuItem
{
text: base.multiBuildPlateModel.getItem(index).name; text: base.multiBuildPlateModel.getItem(index).name;
onTriggered: Cura.SceneController.setActiveBuildPlate(base.multiBuildPlateModel.getItem(index).buildPlateNumber); onTriggered: Cura.SceneController.setActiveBuildPlate(base.multiBuildPlateModel.getItem(index).buildPlateNumber)
checkable: true; checkable: true
checked: base.multiBuildPlateModel.getItem(index).buildPlateNumber == base.multiBuildPlateModel.activeBuildPlate; checked: base.multiBuildPlateModel.getItem(index).buildPlateNumber == base.multiBuildPlateModel.activeBuildPlate
exclusiveGroup: buildPlateGroup; exclusiveGroup: buildPlateGroup
visible: UM.Preferences.getValue("cura/use_multi_build_plate") visible: UM.Preferences.getValue("cura/use_multi_build_plate")
} }
onObjectAdded: buildPlateMenu.insertItem(index, object); onObjectAdded: buildPlateMenu.insertItem(index, object)
onObjectRemoved: buildPlateMenu.removeItem(object) onObjectRemoved: buildPlateMenu.removeItem(object)
} }
ExclusiveGroup { id: buildPlateGroup; } ExclusiveGroup
{
id: buildPlateGroup
}
} }
MenuSeparator {} MenuSeparator {}
MenuItem { action: Cura.Actions.toggleFullScreen; } MenuItem
{
action: Cura.Actions.toggleFullScreen
}
} }

View file

@ -0,0 +1,15 @@
[general]
version = 4
name = Draft
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = draft
weight = 0
material = generic_abs
[values]
cool_fan_enabled = False
adhesion_type = brim

View file

@ -0,0 +1,15 @@
[general]
version = 4
name = High
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = high
weight = 2
material = generic_abs
[values]
cool_fan_enabled = False
adhesion_type = brim

View file

@ -0,0 +1,16 @@
[general]
version = 4
name = Normal
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = normal
weight = 1
material = generic_abs
[values]
cool_fan_enabled = False
adhesion_type = brim

View file

@ -0,0 +1,15 @@
[general]
version = 4
name = Draft
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = draft
weight = 0
global_quality = True
[values]
layer_height = 0.3

View file

@ -0,0 +1,15 @@
[general]
version = 4
name = High
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = high
weight = 2
global_quality = True
[values]
layer_height = 0.15

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = Normal
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = normal
weight = 1
global_quality = True
[values]
layer_height = 0.2

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = Draft
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = draft
weight = 0
material = generic_hips
[values]

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = High
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = high
weight = 2
material = generic_hips
[values]

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = Normal
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = normal
weight = 1
material = generic_hips
[values]

View file

@ -0,0 +1,18 @@
[general]
version = 4
name = Draft
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = draft
weight = 0
material = generic_petg
[values]
material_print_temperature = =default_material_print_temperature + 35
material_bed_temperature = 70
cool_fan_enabled = False
speed_print = 30

View file

@ -0,0 +1,18 @@
[general]
version = 4
name = High
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = high
weight = 2
material = generic_petg
[values]
material_print_temperature = =default_material_print_temperature + 35
material_bed_temperature = 70
cool_fan_enabled = False
speed_print = 30

View file

@ -0,0 +1,18 @@
[general]
version = 4
name = Normal
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = normal
weight = 1
material = generic_petg
[values]
material_print_temperature = =default_material_print_temperature + 35
material_bed_temperature = 70
cool_fan_enabled = False
speed_print = 30

View file

@ -0,0 +1,15 @@
[general]
version = 4
name = Draft
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = draft
weight = 0
material = generic_pla
[values]

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = High
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = high
weight = 2
material = generic_pla
[values]

View file

@ -0,0 +1,14 @@
[general]
version = 4
name = Normal
definition = anycubic_4max
[metadata]
setting_version = 5
type = quality
quality_type = normal
weight = 1
material = generic_pla
[values]

View file

@ -17,8 +17,6 @@ cool_fan_speed_0 = 100
fill_outline_gaps = True fill_outline_gaps = True
infill_angles = [0,90 ] infill_angles = [0,90 ]
infill_sparse_density = 15 infill_sparse_density = 15
layer_height = 0.2
layer_height_0 = 0.25
material_diameter = 1.75 material_diameter = 1.75
retraction_amount = 2.5 retraction_amount = 2.5
retraction_min_travel = 2 retraction_min_travel = 2

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
@ -26,6 +27,7 @@ speed_wall = =math.ceil(speed_print * 50 / 70)
speed_wall_0 = =math.ceil(speed_wall * 35 / 50) speed_wall_0 = =math.ceil(speed_wall * 35 / 50)
top_bottom_thickness = 1 top_bottom_thickness = 1
wall_thickness = 1 wall_thickness = 1
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
@ -25,6 +26,7 @@ speed_wall = =math.ceil(speed_print * 40 / 80)
speed_wall_0 = =math.ceil(speed_wall * 30 / 40) speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
top_bottom_thickness = 1 top_bottom_thickness = 1
wall_thickness = 1 wall_thickness = 1
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
cool_min_speed = 7 cool_min_speed = 7
@ -21,6 +22,7 @@ material_print_temperature = =default_material_print_temperature - 10
prime_tower_enable = =min(extruderValues('material_surface_energy')) < 100 prime_tower_enable = =min(extruderValues('material_surface_energy')) < 100
skin_overlap = 10 skin_overlap = 10
speed_layer_0 = =math.ceil(speed_print * 20 / 70) speed_layer_0 = =math.ceil(speed_print * 20 / 70)
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,7 +12,9 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 10 material_print_temperature = =default_material_print_temperature + 10
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
skin_overlap = 20 skin_overlap = 20
support_brim_enable = True

View file

@ -12,8 +12,10 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 5 material_print_temperature = =default_material_print_temperature + 5
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
skin_overlap = 15 skin_overlap = 15
support_brim_enable = True
support_infill_sparse_thickness = 0.3 support_infill_sparse_thickness = 0.3

View file

@ -12,6 +12,8 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
support_brim_enable = True
support_infill_sparse_thickness = 0.18 support_infill_sparse_thickness = 0.18

View file

@ -12,5 +12,7 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
support_brim_enable = True

View file

@ -12,5 +12,7 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 5 material_print_temperature = =default_material_print_temperature + 5
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True

View file

@ -12,6 +12,8 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
layer_height = 0.4 layer_height = 0.4
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True
support_interface_height = 0.9 support_interface_height = 0.9

View file

@ -12,7 +12,9 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
layer_height = 0.3 layer_height = 0.3
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True
support_infill_sparse_thickness = 0.3 support_infill_sparse_thickness = 0.3
support_interface_height = 1.2 support_interface_height = 1.2

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
@ -26,6 +27,7 @@ speed_wall = =math.ceil(speed_print * 50 / 70)
speed_wall_0 = =math.ceil(speed_wall * 35 / 50) speed_wall_0 = =math.ceil(speed_wall * 35 / 50)
top_bottom_thickness = 1 top_bottom_thickness = 1
wall_thickness = 1 wall_thickness = 1
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
@ -25,6 +26,7 @@ speed_wall = =math.ceil(speed_print * 40 / 80)
speed_wall_0 = =math.ceil(speed_wall * 30 / 40) speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
top_bottom_thickness = 1 top_bottom_thickness = 1
wall_thickness = 1 wall_thickness = 1
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,6 +12,7 @@ material = generic_bam
variant = AA 0.4 variant = AA 0.4
[values] [values]
brim_replaces_support = False
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed cool_fan_speed_max = =cool_fan_speed
cool_min_speed = 7 cool_min_speed = 7
@ -22,6 +23,7 @@ material_print_temperature = =default_material_print_temperature - 10
prime_tower_enable = =min(extruderValues('material_surface_energy')) < 100 prime_tower_enable = =min(extruderValues('material_surface_energy')) < 100
skin_overlap = 10 skin_overlap = 10
speed_layer_0 = =math.ceil(speed_print * 20 / 70) speed_layer_0 = =math.ceil(speed_print * 20 / 70)
support_brim_enable = True
support_interface_enable = True support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy')) support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric' support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'

View file

@ -12,7 +12,9 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 10 material_print_temperature = =default_material_print_temperature + 10
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
skin_overlap = 20 skin_overlap = 20
support_brim_enable = True

View file

@ -12,8 +12,10 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 5 material_print_temperature = =default_material_print_temperature + 5
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
skin_overlap = 15 skin_overlap = 15
support_brim_enable = True
support_infill_sparse_thickness = 0.3 support_infill_sparse_thickness = 0.3

View file

@ -12,6 +12,8 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
support_brim_enable = True
support_infill_sparse_thickness = 0.18 support_infill_sparse_thickness = 0.18

View file

@ -12,5 +12,7 @@ material = generic_pva
variant = BB 0.4 variant = BB 0.4
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
prime_tower_enable = False prime_tower_enable = False
support_brim_enable = True

View file

@ -12,5 +12,7 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
material_print_temperature = =default_material_print_temperature + 5 material_print_temperature = =default_material_print_temperature + 5
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True

View file

@ -12,5 +12,7 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True
support_interface_height = 0.9 support_interface_height = 0.9

View file

@ -12,6 +12,8 @@ material = generic_pva
variant = BB 0.8 variant = BB 0.8
[values] [values]
brim_replaces_support = False
material_standby_temperature = 100 material_standby_temperature = 100
support_brim_enable = True
support_infill_sparse_thickness = 0.3 support_infill_sparse_thickness = 0.3
support_interface_height = 1.2 support_interface_height = 1.2

View file

@ -216,6 +216,26 @@
"toolbox_header_button_text_active": [255, 255, 255, 255], "toolbox_header_button_text_active": [255, 255, 255, 255],
"toolbox_header_button_text_inactive": [128, 128, 128, 255], "toolbox_header_button_text_inactive": [128, 128, 128, 255],
"toolbox_header_button_text_hovered": [255, 255, 255, 255] "toolbox_header_button_text_hovered": [255, 255, 255, 255],
"monitor_card_background_inactive": [43, 48, 52, 255],
"monitor_card_background": [43, 48, 52, 255],
"monitor_context_menu_background": [80, 84, 87, 255],
"monitor_context_menu_dots": [0, 167, 233, 255],
"monitor_context_menu_highlight": [0, 167, 233, 255],
"monitor_image_overlay": [255, 255, 255, 255],
"monitor_lining_heavy": [255, 255, 255, 255],
"monitor_lining_light": [102, 102, 102, 255],
"monitor_pill_background": [102, 102, 102, 255],
"monitor_placeholder_image": [102, 102, 102, 255],
"monitor_printer_icon": [255, 255, 255, 255],
"monitor_progress_background_text": [102, 102, 102, 255],
"monitor_progress_background": [80, 84, 87, 255],
"monitor_progress_fill_inactive": [216, 216, 216, 255],
"monitor_progress_fill_text": [0, 0, 0, 255],
"monitor_progress_fill": [216, 216, 216, 255],
"monotir_printer_icon_inactive": [154, 154, 154, 255],
"monitor_skeleton_fill": [31, 36, 39, 255],
"monitor_skeleton_fill_dark": [31, 36, 39, 255]
} }
} }

View file

@ -335,10 +335,27 @@
"favorites_header_text_hover": [31, 36, 39, 255], "favorites_header_text_hover": [31, 36, 39, 255],
"favorites_row_selected": [196, 239, 255, 255], "favorites_row_selected": [196, 239, 255, 255],
"monitor_text_inactive": [154, 154, 154, 255], "monitor_card_background_inactive": [240, 240, 240, 255],
"monitor_background_inactive": [240, 240, 240, 255], "monitor_card_background": [255, 255, 255, 255],
"monitor_background_active": [255, 255, 255, 255], "monitor_context_menu_background": [255, 255, 255, 255],
"monitor_lining_inactive": [230, 230, 230, 255] "monitor_context_menu_dots": [154, 154, 154, 255],
"monitor_context_menu_highlight": [245, 245, 245, 255],
"monitor_image_overlay": [0, 0, 0, 255],
"monitor_lining_heavy": [0, 0, 0, 255],
"monitor_lining_light": [230, 230, 230, 255],
"monitor_pill_background": [245, 245, 245, 255],
"monitor_placeholder_image": [230, 230, 230, 255],
"monitor_printer_icon_inactive": [154, 154, 154, 255],
"monitor_printer_icon": [12, 169, 227, 255],
"monitor_progress_background_text": [0,0,0,255],
"monitor_progress_background": [245, 245, 245, 255],
"monitor_progress_fill_inactive": [154, 154, 154, 255],
"monitor_progress_fill_text": [255,255,255,255],
"monitor_progress_fill": [12, 169, 227, 255],
"monitor_shadow": [0, 0, 0, 63],
"monitor_skeleton_fill": [245, 245, 245, 255],
"monitor_skeleton_fill_dark": [216, 216, 216, 255],
"monitor_text_inactive": [154, 154, 154, 255]
}, },
"sizes": { "sizes": {
@ -501,6 +518,12 @@
"action_button_radius": [0.15, 0.15], "action_button_radius": [0.15, 0.15],
"drop_shadow_radius": [1.0, 1.0] "monitor_config_override_box": [1.0, 14.0],
"monitor_extruder_circle": [2.75, 2.75],
"monitor_text_line": [1.16, 1.16],
"monitor_thick_lining": [0.16, 0.16],
"monitor_corner_radius": [0.3, 0.3],
"monitor_shadow_radius": [0.4, 0.4],
"monitor_shadow_offset": [0.15, 0.15]
} }
} }

View file

@ -0,0 +1,12 @@
[general]
name = 0.2 mm
version = 4
definition = tizyx_k25
[metadata]
setting_version = 5
type = variant
hardware_type = nozzle
[values]
machine_nozzle_size = 0.2

View file

@ -0,0 +1,12 @@
[general]
name = 0.3 mm
version = 4
definition = tizyx_k25
[metadata]
setting_version = 5
type = variant
hardware_type = nozzle
[values]
machine_nozzle_size = 0.3

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