mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 06:57:28 -06:00
Merge branch 'master' into master
This commit is contained in:
commit
3a84a5868f
220 changed files with 30385 additions and 25983 deletions
|
@ -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)
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
from PyQt5.QtGui import QImage
|
|
||||||
from PyQt5.QtQuick import QQuickImageProvider
|
|
||||||
from PyQt5.QtCore import QSize
|
|
||||||
|
|
||||||
from UM.Application import Application
|
|
||||||
|
|
||||||
## Creates screenshots of the current scene.
|
|
||||||
class CameraImageProvider(QQuickImageProvider):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(QQuickImageProvider.Image)
|
|
||||||
|
|
||||||
## Request a new image.
|
|
||||||
#
|
|
||||||
# The image will be taken using the current camera position.
|
|
||||||
# Only the actual objects in the scene will get rendered. Not the build
|
|
||||||
# plate and such!
|
|
||||||
# \param id The ID for the image to create. This is the requested image
|
|
||||||
# source, with the "image:" scheme and provider identifier removed. It's
|
|
||||||
# a Qt thing, they'll provide this parameter.
|
|
||||||
# \param size The dimensions of the image to scale to.
|
|
||||||
def requestImage(self, id, size):
|
|
||||||
for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
|
|
||||||
try:
|
|
||||||
image = output_device.activePrinter.camera.getImage()
|
|
||||||
if image.isNull():
|
|
||||||
image = QImage()
|
|
||||||
|
|
||||||
return image, QSize(15, 15)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
image = output_device.activeCamera.getImage()
|
|
||||||
|
|
||||||
return image, QSize(15, 15)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return QImage(), QSize(15, 15)
|
|
|
@ -96,7 +96,6 @@ from . import PrintInformation
|
||||||
from . import CuraActions
|
from . import CuraActions
|
||||||
from cura.Scene import ZOffsetDecorator
|
from cura.Scene import ZOffsetDecorator
|
||||||
from . import CuraSplashScreen
|
from . import CuraSplashScreen
|
||||||
from . import CameraImageProvider
|
|
||||||
from . import PrintJobPreviewImageProvider
|
from . import PrintJobPreviewImageProvider
|
||||||
from . import MachineActionManager
|
from . import MachineActionManager
|
||||||
|
|
||||||
|
@ -114,6 +113,8 @@ from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
|
||||||
|
|
||||||
from cura.ObjectsModel import ObjectsModel
|
from cura.ObjectsModel import ObjectsModel
|
||||||
|
|
||||||
|
from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage
|
||||||
|
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
from UM.Decorators import override
|
from UM.Decorators import override
|
||||||
|
|
||||||
|
@ -168,6 +169,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)
|
||||||
|
@ -304,8 +307,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)
|
||||||
|
|
||||||
|
@ -428,34 +429,30 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self.setRequiredPlugins([
|
self.setRequiredPlugins([
|
||||||
# Misc.:
|
# Misc.:
|
||||||
"ConsoleLogger",
|
"ConsoleLogger", #You want to be able to read the log if something goes wrong.
|
||||||
"CuraEngineBackend",
|
"CuraEngineBackend", #Cura is useless without this one since you can't slice.
|
||||||
"UserAgreement",
|
"UserAgreement", #Our lawyers want every user to see this at least once.
|
||||||
"FileLogger",
|
"FileLogger", #You want to be able to read the log if something goes wrong.
|
||||||
"XmlMaterialProfile",
|
"XmlMaterialProfile", #Cura crashes without this one.
|
||||||
"Toolbox",
|
"Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back.
|
||||||
"PrepareStage",
|
"PrepareStage", #Cura is useless without this one since you can't load models.
|
||||||
"MonitorStage",
|
"MonitorStage", #Major part of Cura's functionality.
|
||||||
"LocalFileOutputDevice",
|
"LocalFileOutputDevice", #Major part of Cura's functionality.
|
||||||
"LocalContainerProvider",
|
"LocalContainerProvider", #Cura is useless without any profiles or setting definitions.
|
||||||
|
|
||||||
# Views:
|
# Views:
|
||||||
"SimpleView",
|
"SimpleView", #Dependency of SolidView.
|
||||||
"SimulationView",
|
"SolidView", #Displays models. Cura is useless without it.
|
||||||
"SolidView",
|
|
||||||
|
|
||||||
# Readers & Writers:
|
# Readers & Writers:
|
||||||
"GCodeWriter",
|
"GCodeWriter", #Cura is useless if it can't write its output.
|
||||||
"STLReader",
|
"STLReader", #Most common model format, so disabling this makes Cura 90% useless.
|
||||||
"3MFWriter",
|
"3MFWriter", #Required for writing project files.
|
||||||
|
|
||||||
# Tools:
|
# Tools:
|
||||||
"CameraTool",
|
"CameraTool", #Needed to see the scene. Cura is useless without it.
|
||||||
"MirrorTool",
|
"SelectionTool", #Dependency of the rest of the tools.
|
||||||
"RotateTool",
|
"TranslateTool", #You'll need this for almost every print.
|
||||||
"ScaleTool",
|
|
||||||
"SelectionTool",
|
|
||||||
"TranslateTool",
|
|
||||||
])
|
])
|
||||||
self._i18n_catalog = i18nCatalog("cura")
|
self._i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
@ -523,7 +520,6 @@ class CuraApplication(QtApplication):
|
||||||
CuraApplication.Created = True
|
CuraApplication.Created = True
|
||||||
|
|
||||||
def _onEngineCreated(self):
|
def _onEngineCreated(self):
|
||||||
self._qml_engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
|
||||||
self._qml_engine.addImageProvider("print_job_preview", PrintJobPreviewImageProvider.PrintJobPreviewImageProvider())
|
self._qml_engine.addImageProvider("print_job_preview", PrintJobPreviewImageProvider.PrintJobPreviewImageProvider())
|
||||||
|
|
||||||
@pyqtProperty(bool)
|
@pyqtProperty(bool)
|
||||||
|
@ -701,7 +697,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 +943,8 @@ class CuraApplication(QtApplication):
|
||||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
||||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||||
|
|
||||||
|
qmlRegisterType(NetworkMJPGImage, "Cura", 1, 0, "NetworkMJPGImage")
|
||||||
|
|
||||||
qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 0, "ObjectsModel", self.getObjectsModel)
|
qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 0, "ObjectsModel", self.getObjectsModel)
|
||||||
qmlRegisterType(BuildPlateModel, "Cura", 1, 0, "BuildPlateModel")
|
qmlRegisterType(BuildPlateModel, "Cura", 1, 0, "BuildPlateModel")
|
||||||
qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel")
|
qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,13 +95,10 @@ class MachineActionManager(QObject):
|
||||||
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
|
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].append(self._machine_actions[action_key])
|
||||||
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])
|
|
||||||
else:
|
else:
|
||||||
self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
|
self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
|
||||||
else:
|
else:
|
||||||
|
@ -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:
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
from UM.Logger import Logger
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, pyqtProperty, pyqtSignal, QObject, pyqtSlot
|
|
||||||
from PyQt5.QtGui import QImage
|
|
||||||
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkCamera(QObject):
|
|
||||||
newImage = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, target = None, parent = None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self._stream_buffer = b""
|
|
||||||
self._stream_buffer_start_index = -1
|
|
||||||
self._manager = None
|
|
||||||
self._image_request = None
|
|
||||||
self._image_reply = None
|
|
||||||
self._image = QImage()
|
|
||||||
self._image_id = 0
|
|
||||||
|
|
||||||
self._target = target
|
|
||||||
self._started = False
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def setTarget(self, target):
|
|
||||||
restart_required = False
|
|
||||||
if self._started:
|
|
||||||
self.stop()
|
|
||||||
restart_required = True
|
|
||||||
|
|
||||||
self._target = target
|
|
||||||
|
|
||||||
if restart_required:
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
@pyqtProperty(QUrl, notify=newImage)
|
|
||||||
def latestImage(self):
|
|
||||||
self._image_id += 1
|
|
||||||
# There is an image provider that is called "camera". In order to ensure that the image qml object, that
|
|
||||||
# requires a QUrl to function, updates correctly we add an increasing number. This causes to see the QUrl
|
|
||||||
# as new (instead of relying on cached version and thus forces an update.
|
|
||||||
temp = "image://camera/" + str(self._image_id)
|
|
||||||
|
|
||||||
return QUrl(temp, QUrl.TolerantMode)
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def start(self):
|
|
||||||
# Ensure that previous requests (if any) are stopped.
|
|
||||||
self.stop()
|
|
||||||
if self._target is None:
|
|
||||||
Logger.log("w", "Unable to start camera stream without target!")
|
|
||||||
return
|
|
||||||
self._started = True
|
|
||||||
url = QUrl(self._target)
|
|
||||||
self._image_request = QNetworkRequest(url)
|
|
||||||
if self._manager is None:
|
|
||||||
self._manager = QNetworkAccessManager()
|
|
||||||
|
|
||||||
self._image_reply = self._manager.get(self._image_request)
|
|
||||||
self._image_reply.downloadProgress.connect(self._onStreamDownloadProgress)
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def stop(self):
|
|
||||||
self._stream_buffer = b""
|
|
||||||
self._stream_buffer_start_index = -1
|
|
||||||
|
|
||||||
if self._image_reply:
|
|
||||||
try:
|
|
||||||
# disconnect the signal
|
|
||||||
try:
|
|
||||||
self._image_reply.downloadProgress.disconnect(self._onStreamDownloadProgress)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# abort the request if it's not finished
|
|
||||||
if not self._image_reply.isFinished():
|
|
||||||
self._image_reply.close()
|
|
||||||
except Exception as e: # RuntimeError
|
|
||||||
pass # It can happen that the wrapped c++ object is already deleted.
|
|
||||||
|
|
||||||
self._image_reply = None
|
|
||||||
self._image_request = None
|
|
||||||
|
|
||||||
self._manager = None
|
|
||||||
|
|
||||||
self._started = False
|
|
||||||
|
|
||||||
def getImage(self):
|
|
||||||
return self._image
|
|
||||||
|
|
||||||
## Ensure that close gets called when object is destroyed
|
|
||||||
def __del__(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def _onStreamDownloadProgress(self, bytes_received, bytes_total):
|
|
||||||
# An MJPG stream is (for our purpose) a stream of concatenated JPG images.
|
|
||||||
# JPG images start with the marker 0xFFD8, and end with 0xFFD9
|
|
||||||
if self._image_reply is None:
|
|
||||||
return
|
|
||||||
self._stream_buffer += self._image_reply.readAll()
|
|
||||||
|
|
||||||
if len(self._stream_buffer) > 2000000: # No single camera frame should be 2 Mb or larger
|
|
||||||
Logger.log("w", "MJPEG buffer exceeds reasonable size. Restarting stream...")
|
|
||||||
self.stop() # resets stream buffer and start index
|
|
||||||
self.start()
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._stream_buffer_start_index == -1:
|
|
||||||
self._stream_buffer_start_index = self._stream_buffer.indexOf(b'\xff\xd8')
|
|
||||||
stream_buffer_end_index = self._stream_buffer.lastIndexOf(b'\xff\xd9')
|
|
||||||
# If this happens to be more than a single frame, then so be it; the JPG decoder will
|
|
||||||
# ignore the extra data. We do it like this in order not to get a buildup of frames
|
|
||||||
|
|
||||||
if self._stream_buffer_start_index != -1 and stream_buffer_end_index != -1:
|
|
||||||
jpg_data = self._stream_buffer[self._stream_buffer_start_index:stream_buffer_end_index + 2]
|
|
||||||
self._stream_buffer = self._stream_buffer[stream_buffer_end_index + 2:]
|
|
||||||
self._stream_buffer_start_index = -1
|
|
||||||
self._image.loadFromData(jpg_data)
|
|
||||||
|
|
||||||
self.newImage.emit()
|
|
153
cura/PrinterOutput/NetworkMJPGImage.py
Normal file
153
cura/PrinterOutput/NetworkMJPGImage.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
# Copyright (c) 2018 Aldo Hoeben / fieldOfView
|
||||||
|
# NetworkMJPGImage is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QUrl, pyqtProperty, pyqtSignal, pyqtSlot, QRect, QByteArray
|
||||||
|
from PyQt5.QtGui import QImage, QPainter
|
||||||
|
from PyQt5.QtQuick import QQuickPaintedItem
|
||||||
|
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
#
|
||||||
|
# A QQuickPaintedItem that progressively downloads a network mjpeg stream,
|
||||||
|
# picks it apart in individual jpeg frames, and paints it.
|
||||||
|
#
|
||||||
|
class NetworkMJPGImage(QQuickPaintedItem):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._stream_buffer = QByteArray()
|
||||||
|
self._stream_buffer_start_index = -1
|
||||||
|
self._network_manager = None # type: QNetworkAccessManager
|
||||||
|
self._image_request = None # type: QNetworkRequest
|
||||||
|
self._image_reply = None # type: QNetworkReply
|
||||||
|
self._image = QImage()
|
||||||
|
self._image_rect = QRect()
|
||||||
|
|
||||||
|
self._source_url = QUrl()
|
||||||
|
self._started = False
|
||||||
|
|
||||||
|
self._mirror = False
|
||||||
|
|
||||||
|
self.setAntialiasing(True)
|
||||||
|
|
||||||
|
## Ensure that close gets called when object is destroyed
|
||||||
|
def __del__(self) -> None:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def paint(self, painter: "QPainter") -> None:
|
||||||
|
if self._mirror:
|
||||||
|
painter.drawImage(self.contentsBoundingRect(), self._image.mirrored())
|
||||||
|
return
|
||||||
|
|
||||||
|
painter.drawImage(self.contentsBoundingRect(), self._image)
|
||||||
|
|
||||||
|
|
||||||
|
def setSourceURL(self, source_url: "QUrl") -> None:
|
||||||
|
self._source_url = source_url
|
||||||
|
self.sourceURLChanged.emit()
|
||||||
|
if self._started:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def getSourceURL(self) -> "QUrl":
|
||||||
|
return self._source_url
|
||||||
|
|
||||||
|
sourceURLChanged = pyqtSignal()
|
||||||
|
source = pyqtProperty(QUrl, fget = getSourceURL, fset = setSourceURL, notify = sourceURLChanged)
|
||||||
|
|
||||||
|
def setMirror(self, mirror: bool) -> None:
|
||||||
|
if mirror == self._mirror:
|
||||||
|
return
|
||||||
|
self._mirror = mirror
|
||||||
|
self.mirrorChanged.emit()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def getMirror(self) -> bool:
|
||||||
|
return self._mirror
|
||||||
|
|
||||||
|
mirrorChanged = pyqtSignal()
|
||||||
|
mirror = pyqtProperty(bool, fget = getMirror, fset = setMirror, notify = mirrorChanged)
|
||||||
|
|
||||||
|
imageSizeChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify = imageSizeChanged)
|
||||||
|
def imageWidth(self) -> int:
|
||||||
|
return self._image.width()
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify = imageSizeChanged)
|
||||||
|
def imageHeight(self) -> int:
|
||||||
|
return self._image.height()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def start(self) -> None:
|
||||||
|
self.stop() # Ensure that previous requests (if any) are stopped.
|
||||||
|
|
||||||
|
if not self._source_url:
|
||||||
|
Logger.log("w", "Unable to start camera stream without target!")
|
||||||
|
return
|
||||||
|
self._started = True
|
||||||
|
|
||||||
|
self._image_request = QNetworkRequest(self._source_url)
|
||||||
|
if self._network_manager is None:
|
||||||
|
self._network_manager = QNetworkAccessManager()
|
||||||
|
|
||||||
|
self._image_reply = self._network_manager.get(self._image_request)
|
||||||
|
self._image_reply.downloadProgress.connect(self._onStreamDownloadProgress)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def stop(self) -> None:
|
||||||
|
self._stream_buffer = QByteArray()
|
||||||
|
self._stream_buffer_start_index = -1
|
||||||
|
|
||||||
|
if self._image_reply:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
self._image_reply.downloadProgress.disconnect(self._onStreamDownloadProgress)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not self._image_reply.isFinished():
|
||||||
|
self._image_reply.close()
|
||||||
|
except Exception as e: # RuntimeError
|
||||||
|
pass # It can happen that the wrapped c++ object is already deleted.
|
||||||
|
|
||||||
|
self._image_reply = None
|
||||||
|
self._image_request = None
|
||||||
|
|
||||||
|
self._network_manager = None
|
||||||
|
|
||||||
|
self._started = False
|
||||||
|
|
||||||
|
|
||||||
|
def _onStreamDownloadProgress(self, bytes_received: int, bytes_total: int) -> None:
|
||||||
|
# An MJPG stream is (for our purpose) a stream of concatenated JPG images.
|
||||||
|
# JPG images start with the marker 0xFFD8, and end with 0xFFD9
|
||||||
|
if self._image_reply is None:
|
||||||
|
return
|
||||||
|
self._stream_buffer += self._image_reply.readAll()
|
||||||
|
|
||||||
|
if len(self._stream_buffer) > 2000000: # No single camera frame should be 2 Mb or larger
|
||||||
|
Logger.log("w", "MJPEG buffer exceeds reasonable size. Restarting stream...")
|
||||||
|
self.stop() # resets stream buffer and start index
|
||||||
|
self.start()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._stream_buffer_start_index == -1:
|
||||||
|
self._stream_buffer_start_index = self._stream_buffer.indexOf(b'\xff\xd8')
|
||||||
|
stream_buffer_end_index = self._stream_buffer.lastIndexOf(b'\xff\xd9')
|
||||||
|
# If this happens to be more than a single frame, then so be it; the JPG decoder will
|
||||||
|
# ignore the extra data. We do it like this in order not to get a buildup of frames
|
||||||
|
|
||||||
|
if self._stream_buffer_start_index != -1 and stream_buffer_end_index != -1:
|
||||||
|
jpg_data = self._stream_buffer[self._stream_buffer_start_index:stream_buffer_end_index + 2]
|
||||||
|
self._stream_buffer = self._stream_buffer[stream_buffer_end_index + 2:]
|
||||||
|
self._stream_buffer_start_index = -1
|
||||||
|
self._image.loadFromData(jpg_data)
|
||||||
|
|
||||||
|
if self._image.rect() != self._image_rect:
|
||||||
|
self.imageSizeChanged.emit()
|
||||||
|
|
||||||
|
self.update()
|
|
@ -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()
|
||||||
|
@ -55,7 +54,7 @@ class PrintJobOutputModel(QObject):
|
||||||
@pyqtProperty(QUrl, notify=previewImageChanged)
|
@pyqtProperty(QUrl, notify=previewImageChanged)
|
||||||
def previewImageUrl(self):
|
def previewImageUrl(self):
|
||||||
self._preview_image_id += 1
|
self._preview_image_id += 1
|
||||||
# There is an image provider that is called "camera". In order to ensure that the image qml object, that
|
# There is an image provider that is called "print_job_preview". 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
|
# 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.
|
# as new (instead of relying on cached version and thus forces an update.
|
||||||
temp = "image://print_job_preview/" + str(self._preview_image_id) + "/" + self._key
|
temp = "image://print_job_preview/" + str(self._preview_image_id) + "/" + self._key
|
||||||
|
@ -147,4 +146,4 @@ class PrintJobOutputModel(QObject):
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def setState(self, state):
|
def setState(self, state):
|
||||||
self._output_controller.setJobState(self, state)
|
self._output_controller.setJobState(self, state)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# 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 PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot
|
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot, QUrl
|
||||||
from typing import List, Dict, Optional
|
from typing import List, Dict, Optional
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||||
|
@ -11,7 +11,6 @@ MYPY = False
|
||||||
if MYPY:
|
if MYPY:
|
||||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
||||||
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
||||||
from cura.PrinterOutput.NetworkCamera import NetworkCamera
|
|
||||||
|
|
||||||
|
|
||||||
class PrinterOutputModel(QObject):
|
class PrinterOutputModel(QObject):
|
||||||
|
@ -25,7 +24,7 @@ class PrinterOutputModel(QObject):
|
||||||
keyChanged = pyqtSignal()
|
keyChanged = pyqtSignal()
|
||||||
printerTypeChanged = pyqtSignal()
|
printerTypeChanged = pyqtSignal()
|
||||||
buildplateChanged = pyqtSignal()
|
buildplateChanged = pyqtSignal()
|
||||||
cameraChanged = pyqtSignal()
|
cameraUrlChanged = pyqtSignal()
|
||||||
configurationChanged = pyqtSignal()
|
configurationChanged = pyqtSignal()
|
||||||
canUpdateFirmwareChanged = pyqtSignal()
|
canUpdateFirmwareChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@ -50,16 +49,20 @@ 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_url = QUrl() # type: QUrl
|
||||||
|
|
||||||
@pyqtProperty(str, constant = True)
|
@pyqtProperty(str, constant = True)
|
||||||
def firmwareVersion(self) -> str:
|
def firmwareVersion(self) -> str:
|
||||||
return self._firmware_version
|
return self._firmware_version
|
||||||
|
|
||||||
def setCamera(self, camera: Optional["NetworkCamera"]) -> None:
|
def setCameraUrl(self, camera_url: "QUrl") -> None:
|
||||||
if self._camera is not camera:
|
if self._camera_url != camera_url:
|
||||||
self._camera = camera
|
self._camera_url = camera_url
|
||||||
self.cameraChanged.emit()
|
self.cameraUrlChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(QUrl, fset = setCameraUrl, notify = cameraUrlChanged)
|
||||||
|
def cameraUrl(self) -> "QUrl":
|
||||||
|
return self._camera_url
|
||||||
|
|
||||||
def updateIsPreheating(self, pre_heating: bool) -> None:
|
def updateIsPreheating(self, pre_heating: bool) -> None:
|
||||||
if self._is_preheating != pre_heating:
|
if self._is_preheating != pre_heating:
|
||||||
|
@ -70,10 +73,6 @@ class PrinterOutputModel(QObject):
|
||||||
def isPreheating(self) -> bool:
|
def isPreheating(self) -> bool:
|
||||||
return self._is_preheating
|
return self._is_preheating
|
||||||
|
|
||||||
@pyqtProperty(QObject, notify=cameraChanged)
|
|
||||||
def camera(self) -> Optional["NetworkCamera"]:
|
|
||||||
return self._camera
|
|
||||||
|
|
||||||
@pyqtProperty(str, notify = printerTypeChanged)
|
@pyqtProperty(str, notify = printerTypeChanged)
|
||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
return self._printer_type
|
return self._printer_type
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -1,3 +1,65 @@
|
||||||
|
[3.6.0]
|
||||||
|
*Gyroid infill
|
||||||
|
New infill pattern with enhanced strength properties. Gyroid infill is one of the strongest infill types for a given weight, has isotropic properties, and prints relatively fast with reduced material use and a fully connected part interior. Note: Slicing time can increase up to 40 seconds or more, depending on the model. Contributed by smartavionics.
|
||||||
|
|
||||||
|
*Support brim
|
||||||
|
New setting that integrates the first layer of support material with the brim’s geometry. This significantly improves adhesion when printing with support material. Contributed by BagelOrb.
|
||||||
|
|
||||||
|
*Cooling fan number
|
||||||
|
It is now possible to specify the cooling fan to use if your printer has multiple fans. This is implemented under Machine settings in the Extruder tab. Contributed by smartavionics.
|
||||||
|
|
||||||
|
*Settings refactor
|
||||||
|
The CuraEngine has been refactored to create a more testable, future-proof way of storing and representing settings. This makes slicing faster, and future development easier.
|
||||||
|
|
||||||
|
*Print core CC Red 0.6
|
||||||
|
The new print core CC Red 0.6 is selectable when the Ultimaker S5 profile is active. This print core is optimized for use with abrasive materials and composites.
|
||||||
|
|
||||||
|
*File name and layer display
|
||||||
|
Added M117 commands to GCODE to give real-time information about the print job file name and layer number shown on the printer’s display when printing via USB. Contributed by adecastilho.
|
||||||
|
|
||||||
|
*Firmware checker/Ultimaker S5
|
||||||
|
The update checker code has been improved and tested for more reliable firmware update notifications in Ultimaker Cura. The Ultimaker S5 is now included.
|
||||||
|
|
||||||
|
*Fullscreen mode shortcuts
|
||||||
|
Fullscreen mode can be toggled using the View menu or with the keyboard shortcuts: Command + Control + F (macOS), or F11 (Windows and Linux). Contributed by KangDroid.
|
||||||
|
|
||||||
|
*Configuration error message
|
||||||
|
In previous versions, Ultimaker Cura would display an error dialog explaining when something happened to user configuration files, including the option to reset to factory defaults. This would not warn about losing the current printer and print profile settings, so this information has been added.
|
||||||
|
|
||||||
|
*Rename Toolbox to Marketplace
|
||||||
|
The entry points to the Toolbox are now renamed to Marketplace.
|
||||||
|
|
||||||
|
*Materials in the Marketplace
|
||||||
|
A new tab has been added to the Marketplace that includes downloadable material profiles, to quickly and easily prepare models for a range of third-party materials.
|
||||||
|
|
||||||
|
*New third-party definitions
|
||||||
|
New profiles added for Anycube 4MAx and Tizyx K25. Contributed by jscurtu and ValentinPitre respectively.
|
||||||
|
|
||||||
|
*Improved definitions for Ender-3
|
||||||
|
The Ender-3 build plate size has been adjusted to the correct size of 235 x 235 mm, corrected the start-up sequence, and the printhead position has been adjusted when prints are purged or completed. Contributed by stelgenhof.
|
||||||
|
|
||||||
|
*Add mesh names to slicing message
|
||||||
|
Added comment generation to indicate which mesh the GCODE after this comment is constructing. Contributed by paukstelis.
|
||||||
|
|
||||||
|
*Bug fixes
|
||||||
|
- The active material is highlighted in Ultimaker Cura’s material manager list. This behavior is now consistent with the profile and machine manager.
|
||||||
|
- The option to use 1.75 mm diameter filament with third-party 3D printers is now fixed and does not revert back to 2.85 mm. This fix also applies the appropriate a Z-axis speed change for 1.75 mm filament printers. Contributed by kaleidoscopeit.
|
||||||
|
- A fix was created to handle OSX version 10.10, but due to the QT upgrade, users with older versions won’t be able to run Ultimaker Cura on their system without a system update. This applies to OSX version 10.09 and 10.08.
|
||||||
|
- Fixed a memory leak when leaving the “Monitor” page open.
|
||||||
|
- Added performance improvements to the PolygonConnector to efficiently connect polygons that are close to each other. This also reduces the chances of the print head collide with previously printed things. Contributed by BagelOrb.
|
||||||
|
- Fixed a bug where the GCODE reader didn’t show retractions.
|
||||||
|
- Changes the USBPrinting update thread to prevent flooding the printer with M105 temperature update requests. Contributed by fieldOfView.
|
||||||
|
- Fix the behavior of the "manage visible settings" button, when pressing the "cog" icon of a particular category. Contributed by fieldOfView.
|
||||||
|
- Add a new post processing script that pauses the print at a certain height that works with RepRap printers. Contributed by Kriechi.
|
||||||
|
- Fix updates to the print monitor temperatures while preheating. Contributed by fieldOfView.
|
||||||
|
- Fixed a bug where material cost is not shown unless weight is changed.
|
||||||
|
- Fixed bugs crashing the CuraEngine when TreeSupport is enabled.
|
||||||
|
- Fixed a bug where Ultimaker Cura would upload the wrong firmware after switching printers in the UI.
|
||||||
|
- Fixed a bug where the layer view was missing if the first layer was empty.
|
||||||
|
- Fixed a bug where erroneous combing movements were taking place.
|
||||||
|
- Fixed a bug where the initial layer temperature is set correctly for the first object but then never again.
|
||||||
|
- Fixed a bug where clicking the fx icon didn’t respond.
|
||||||
|
|
||||||
[3.5.1]
|
[3.5.1]
|
||||||
*Bug fixes
|
*Bug fixes
|
||||||
- Fixed M104 temperature commands giving inaccurate results.
|
- Fixed M104 temperature commands giving inaccurate results.
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -295,7 +293,7 @@ class FlavorParser:
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
# We obtain the filament diameter from the selected extruder to calculate line widths
|
# We obtain the filament diameter from the selected extruder to calculate line widths
|
||||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
|
|
||||||
if not global_stack:
|
if not global_stack:
|
||||||
return None
|
return 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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
const extruderIndex = index.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,14 +407,9 @@ Item {
|
||||||
function updateFilter()
|
function updateFilter()
|
||||||
{
|
{
|
||||||
var new_filter = {};
|
var new_filter = {};
|
||||||
if (printSequencePropertyProvider.properties.value == "one_at_a_time")
|
new_filter["settable_per_mesh"] = true;
|
||||||
{
|
// Don't filter on "settable_per_meshgroup" any more when `printSequencePropertyProvider.properties.value`
|
||||||
new_filter["settable_per_meshgroup"] = true;
|
// is set to "one_at_a_time", because the current backend architecture isn't ready for that.
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_filter["settable_per_mesh"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(filterInput.text != "")
|
if(filterInput.text != "")
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
@ -72,12 +73,12 @@ class FilamentChange(Script):
|
||||||
later_retract = self.getSettingValueByKey("later_retract")
|
later_retract = self.getSettingValueByKey("later_retract")
|
||||||
x_pos = self.getSettingValueByKey("x_position")
|
x_pos = self.getSettingValueByKey("x_position")
|
||||||
y_pos = self.getSettingValueByKey("y_position")
|
y_pos = self.getSettingValueByKey("y_position")
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ class FilamentChange(Script):
|
||||||
color_change = color_change + (" Y%.2f" % y_pos)
|
color_change = color_change + (" Y%.2f" % y_pos)
|
||||||
|
|
||||||
color_change = color_change + " ; Generated by FilamentChange plugin"
|
color_change = color_change + " ; Generated by FilamentChange plugin"
|
||||||
|
|
||||||
layer_targets = layer_nums.split(",")
|
layer_targets = layer_nums.split(",")
|
||||||
if len(layer_targets) > 0:
|
if len(layer_targets) > 0:
|
||||||
for layer_num in layer_targets:
|
for layer_num in layer_targets:
|
||||||
|
|
|
@ -10,7 +10,7 @@ Window
|
||||||
{
|
{
|
||||||
id: base
|
id: base
|
||||||
property var selection: null
|
property var selection: null
|
||||||
title: catalog.i18nc("@title", "Toolbox")
|
title: catalog.i18nc("@title", "Marketplace")
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
|
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,11 @@ Item
|
||||||
rightMargin: UM.Theme.getSize("wide_margin").width
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
}
|
}
|
||||||
height: UM.Theme.getSize("toolbox_detail_header").height
|
height: UM.Theme.getSize("toolbox_detail_header").height
|
||||||
Image
|
Rectangle
|
||||||
{
|
{
|
||||||
id: thumbnail
|
id: thumbnail
|
||||||
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
||||||
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: details === null ? "" : (details.icon_url || "../images/logobot.svg")
|
|
||||||
mipmap: true
|
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
top: parent.top
|
top: parent.top
|
||||||
|
@ -40,6 +37,14 @@ Item
|
||||||
leftMargin: UM.Theme.getSize("wide_margin").width
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
topMargin: UM.Theme.getSize("wide_margin").height
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
}
|
}
|
||||||
|
color: white //Always a white background for image (regardless of theme).
|
||||||
|
Image
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: details === null ? "" : (details.icon_url || "../images/logobot.svg")
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
// Toolbox is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
|
|
@ -52,7 +52,7 @@ class PackagesModel(ListModel):
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
if self._metadata is None:
|
if self._metadata is None:
|
||||||
Logger.logException("w", "Failed to load packages for Toolbox")
|
Logger.logException("w", "Failed to load packages for Marketplace")
|
||||||
self.setItems(items)
|
self.setItems(items)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ class Toolbox(QObject, Extension):
|
||||||
self._dialog = self._createDialog("Toolbox.qml")
|
self._dialog = self._createDialog("Toolbox.qml")
|
||||||
|
|
||||||
if not self._dialog:
|
if not self._dialog:
|
||||||
Logger.log("e", "Unexpected error trying to create the 'Toolbox' dialog.")
|
Logger.log("e", "Unexpected error trying to create the 'Marketplace' dialog.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self._dialog.show()
|
self._dialog.show()
|
||||||
|
@ -254,7 +254,7 @@ class Toolbox(QObject, Extension):
|
||||||
self.enabledChanged.emit()
|
self.enabledChanged.emit()
|
||||||
|
|
||||||
def _createDialog(self, qml_name: str) -> Optional[QObject]:
|
def _createDialog(self, qml_name: str) -> Optional[QObject]:
|
||||||
Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
|
Logger.log("d", "Marketplace: Creating dialog [%s].", qml_name)
|
||||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||||
if not plugin_path:
|
if not plugin_path:
|
||||||
return None
|
return None
|
||||||
|
@ -262,7 +262,7 @@ class Toolbox(QObject, Extension):
|
||||||
|
|
||||||
dialog = self._application.createQmlComponent(path, {"toolbox": self})
|
dialog = self._application.createQmlComponent(path, {"toolbox": self})
|
||||||
if not dialog:
|
if not dialog:
|
||||||
raise Exception("Failed to create toolbox dialog")
|
raise Exception("Failed to create Marketplace dialog")
|
||||||
return dialog
|
return dialog
|
||||||
|
|
||||||
def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]:
|
def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
@ -578,7 +578,7 @@ class Toolbox(QObject, Extension):
|
||||||
# Make API Calls
|
# Make API Calls
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
def _makeRequestByType(self, type: str) -> None:
|
def _makeRequestByType(self, type: str) -> None:
|
||||||
Logger.log("i", "Toolbox: Requesting %s metadata from server.", type)
|
Logger.log("i", "Marketplace: Requesting %s metadata from server.", type)
|
||||||
request = QNetworkRequest(self._request_urls[type])
|
request = QNetworkRequest(self._request_urls[type])
|
||||||
request.setRawHeader(*self._request_header)
|
request.setRawHeader(*self._request_header)
|
||||||
if self._network_manager:
|
if self._network_manager:
|
||||||
|
@ -586,7 +586,7 @@ class Toolbox(QObject, Extension):
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def startDownload(self, url: str) -> None:
|
def startDownload(self, url: str) -> None:
|
||||||
Logger.log("i", "Toolbox: Attempting to download & install package from %s.", url)
|
Logger.log("i", "Marketplace: Attempting to download & install package from %s.", url)
|
||||||
url = QUrl(url)
|
url = QUrl(url)
|
||||||
self._download_request = QNetworkRequest(url)
|
self._download_request = QNetworkRequest(url)
|
||||||
if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
|
if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
|
||||||
|
@ -603,7 +603,7 @@ class Toolbox(QObject, Extension):
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def cancelDownload(self) -> None:
|
def cancelDownload(self) -> None:
|
||||||
Logger.log("i", "Toolbox: User cancelled the download of a package.")
|
Logger.log("i", "Marketplace: User cancelled the download of a package.")
|
||||||
self.resetDownload()
|
self.resetDownload()
|
||||||
|
|
||||||
def resetDownload(self) -> None:
|
def resetDownload(self) -> None:
|
||||||
|
@ -690,7 +690,7 @@ class Toolbox(QObject, Extension):
|
||||||
|
|
||||||
return
|
return
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
|
Logger.log("w", "Marketplace: Received invalid JSON for %s.", type)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.setViewPage("errored")
|
self.setViewPage("errored")
|
||||||
|
@ -717,10 +717,10 @@ class Toolbox(QObject, Extension):
|
||||||
self._onDownloadComplete(file_path)
|
self._onDownloadComplete(file_path)
|
||||||
|
|
||||||
def _onDownloadComplete(self, file_path: str) -> None:
|
def _onDownloadComplete(self, file_path: str) -> None:
|
||||||
Logger.log("i", "Toolbox: Download complete.")
|
Logger.log("i", "Marketplace: Download complete.")
|
||||||
package_info = self._package_manager.getPackageInfo(file_path)
|
package_info = self._package_manager.getPackageInfo(file_path)
|
||||||
if not package_info:
|
if not package_info:
|
||||||
Logger.log("w", "Toolbox: Package file [%s] was not a valid CuraPackage.", file_path)
|
Logger.log("w", "Marketplace: Package file [%s] was not a valid CuraPackage.", file_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
license_content = self._package_manager.getPackageLicense(file_path)
|
license_content = self._package_manager.getPackageLicense(file_path)
|
||||||
|
@ -819,7 +819,7 @@ class Toolbox(QObject, Extension):
|
||||||
@pyqtSlot(str, str, str)
|
@pyqtSlot(str, str, str)
|
||||||
def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
|
def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
|
||||||
if not self._models[model_type]:
|
if not self._models[model_type]:
|
||||||
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
|
Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type)
|
||||||
return
|
return
|
||||||
self._models[model_type].setFilter({filter_type: parameter})
|
self._models[model_type].setFilter({filter_type: parameter})
|
||||||
self.filterChanged.emit()
|
self.filterChanged.emit()
|
||||||
|
@ -827,7 +827,7 @@ class Toolbox(QObject, Extension):
|
||||||
@pyqtSlot(str, "QVariantMap")
|
@pyqtSlot(str, "QVariantMap")
|
||||||
def setFilters(self, model_type: str, filter_dict: dict) -> None:
|
def setFilters(self, model_type: str, filter_dict: dict) -> None:
|
||||||
if not self._models[model_type]:
|
if not self._models[model_type]:
|
||||||
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
|
Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type)
|
||||||
return
|
return
|
||||||
self._models[model_type].setFilter(filter_dict)
|
self._models[model_type].setFilter(filter_dict)
|
||||||
self.filterChanged.emit()
|
self.filterChanged.emit()
|
||||||
|
@ -835,7 +835,7 @@ class Toolbox(QObject, Extension):
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def removeFilters(self, model_type: str) -> None:
|
def removeFilters(self, model_type: str) -> None:
|
||||||
if not self._models[model_type]:
|
if not self._models[model_type]:
|
||||||
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", model_type)
|
Logger.log("w", "Marketplace: Couldn't remove filters on %s model because it doesn't exist.", model_type)
|
||||||
return
|
return
|
||||||
self._models[model_type].setFilter({})
|
self._models[model_type].setFilter({})
|
||||||
self.filterChanged.emit()
|
self.filterChanged.emit()
|
||||||
|
@ -845,6 +845,7 @@ class Toolbox(QObject, Extension):
|
||||||
def buildMaterialsModels(self) -> None:
|
def buildMaterialsModels(self) -> None:
|
||||||
self._metadata["materials_showcase"] = []
|
self._metadata["materials_showcase"] = []
|
||||||
self._metadata["materials_available"] = []
|
self._metadata["materials_available"] = []
|
||||||
|
self._metadata["materials_generic"] = []
|
||||||
|
|
||||||
processed_authors = [] # type: List[str]
|
processed_authors = [] # type: List[str]
|
||||||
|
|
||||||
|
|
|
@ -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.activeCameraUrl != "") {
|
||||||
{
|
OutputDevice.setActiveCameraUrl("");
|
||||||
if (OutputDevice.activeCamera !== null)
|
} else {
|
||||||
{
|
OutputDevice.setActiveCameraUrl(modelData.cameraUrl);
|
||||||
OutputDevice.setActiveCamera(null)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OutputDevice.setActiveCamera(modelData.camera)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
right: parent.right
|
|
||||||
}
|
}
|
||||||
|
color: UM.Theme.getColor("text");
|
||||||
text: catalog.i18nc("@label", "Printing")
|
elide: Text.ElideRight;
|
||||||
elide: Text.ElideRight
|
font: UM.Theme.getFont("large");
|
||||||
|
text: catalog.i18nc("@label", "Printing");
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label {
|
||||||
{
|
id: managePrintersLabel;
|
||||||
id: managePrintersLabel
|
anchors {
|
||||||
anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width
|
bottom: printingLabel.bottom;
|
||||||
anchors.right: printerScrollView.right
|
right: printerScrollView.right;
|
||||||
anchors.bottom: printingLabel.bottom
|
rightMargin: 4 * UM.Theme.getSize("default_margin").width;
|
||||||
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
|
|
||||||
{
|
|
||||||
anchors.fill: managePrintersLabel
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
|
|
||||||
onEntered: managePrintersLabel.font.underline = true
|
|
||||||
onExited: managePrintersLabel.font.underline = false
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
style: UM.Theme.styles.scrollview
|
MouseArea {
|
||||||
|
anchors.fill: managePrintersLabel;
|
||||||
|
hoverEnabled: true;
|
||||||
|
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel();
|
||||||
|
onEntered: managePrintersLabel.font.underline = true;
|
||||||
|
onExited: managePrintersLabel.font.underline = false;
|
||||||
|
}
|
||||||
|
|
||||||
ListView
|
// Skeleton loading
|
||||||
{
|
Column {
|
||||||
id: printer_list
|
id: skeletonLoader;
|
||||||
property var current_index: -1
|
anchors {
|
||||||
anchors
|
left: parent.left;
|
||||||
{
|
leftMargin: UM.Theme.getSize("wide_margin").width;
|
||||||
top: parent.top
|
right: parent.right;
|
||||||
bottom: parent.bottom
|
rightMargin: UM.Theme.getSize("wide_margin").width;
|
||||||
left: parent.left
|
top: printingLabel.bottom;
|
||||||
right: parent.right
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
leftMargin: 2 * UM.Theme.getSize("default_margin").width
|
}
|
||||||
rightMargin: 2 * UM.Theme.getSize("default_margin").width
|
spacing: UM.Theme.getSize("default_margin").height - 10;
|
||||||
|
visible: printerList.count === 0;
|
||||||
|
|
||||||
|
PrinterCard {
|
||||||
|
printer: null;
|
||||||
|
}
|
||||||
|
PrinterCard {
|
||||||
|
printer: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual content
|
||||||
|
ScrollView {
|
||||||
|
id: printerScrollView;
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom;
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
top: printingLabel.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
style: UM.Theme.styles.scrollview;
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: printerList;
|
||||||
|
property var currentIndex: -1;
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width;
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width;
|
||||||
}
|
}
|
||||||
spacing: UM.Theme.getSize("default_margin").height -10
|
delegate: PrinterCard {
|
||||||
model: OutputDevice.printers
|
printer: modelData;
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item
|
|
||||||
{
|
|
||||||
id: machineIcon
|
|
||||||
// Yeah, this is hardcoded now, but I can't think of a good way to fix this.
|
|
||||||
// The UI is going to get another update soon, so it's probably not worth the effort...
|
|
||||||
width: 58
|
|
||||||
height: 58
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
anchors.left: parent.left
|
|
||||||
|
|
||||||
UM.RecolorImage
|
|
||||||
{
|
|
||||||
anchors.centerIn: parent
|
|
||||||
source:
|
|
||||||
{
|
|
||||||
switch(modelData.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
model: OutputDevice.printers;
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height - 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.setActiveCameraUrl("");
|
||||||
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
|
|
||||||
|
|
||||||
UM.I18nCatalog
|
|
||||||
{
|
|
||||||
id: catalog
|
|
||||||
name: "cura"
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: manageQueueLabel
|
|
||||||
anchors.rightMargin: 3 * UM.Theme.getSize("default_margin").width
|
|
||||||
anchors.right: queuedPrintJobs.right
|
|
||||||
anchors.bottom: queuedLabel.bottom
|
|
||||||
text: catalog.i18nc("@label link to connect manager", "Manage queue")
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
linkColor: UM.Theme.getColor("primary")
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
anchors.fill: manageQueueLabel
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
|
|
||||||
onEntered: manageQueueLabel.font.underline = true
|
|
||||||
onExited: manageQueueLabel.font.underline = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: queuedLabel
|
|
||||||
anchors.left: queuedPrintJobs.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
|
|
||||||
anchors.leftMargin: 3 * UM.Theme.getSize("default_margin").width + 5
|
|
||||||
text: catalog.i18nc("@label", "Queued")
|
|
||||||
font: UM.Theme.getFont("large")
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView
|
|
||||||
{
|
|
||||||
id: queuedPrintJobs
|
|
||||||
|
|
||||||
anchors
|
|
||||||
{
|
|
||||||
top: queuedLabel.bottom
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottomMargin: 0
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
}
|
||||||
style: UM.Theme.styles.scrollview
|
}
|
||||||
width: Math.min(800 * screenScaleFactor, maximumWidth)
|
width: 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
|
UM.I18nCatalog {
|
||||||
|
id: catalog;
|
||||||
|
name: "cura";
|
||||||
|
}
|
||||||
|
|
||||||
delegate: PrintJobInfoBlock
|
Label {
|
||||||
{
|
id: manageQueueLabel;
|
||||||
printJob: modelData
|
anchors {
|
||||||
anchors.left: parent.left
|
bottom: queuedLabel.bottom;
|
||||||
anchors.right: parent.right
|
right: queuedPrintJobs.right;
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").height
|
rightMargin: 3 * UM.Theme.getSize("default_margin").width;
|
||||||
anchors.leftMargin: UM.Theme.getSize("default_margin").height
|
}
|
||||||
height: 175 * screenScaleFactor
|
color: UM.Theme.getColor("primary");
|
||||||
|
font: UM.Theme.getFont("default");
|
||||||
|
linkColor: UM.Theme.getColor("primary");
|
||||||
|
text: catalog.i18nc("@label link to connect manager", "Manage queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: manageQueueLabel;
|
||||||
|
hoverEnabled: true;
|
||||||
|
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel();
|
||||||
|
onEntered: manageQueueLabel.font.underline = true;
|
||||||
|
onExited: manageQueueLabel.font.underline = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: queuedLabel;
|
||||||
|
anchors {
|
||||||
|
left: queuedPrintJobs.left;
|
||||||
|
leftMargin: 3 * UM.Theme.getSize("default_margin").width + 5 * screenScaleFactor;
|
||||||
|
top: parent.top;
|
||||||
|
topMargin: 2 * UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
color: UM.Theme.getColor("text");
|
||||||
|
font: UM.Theme.getFont("large");
|
||||||
|
text: catalog.i18nc("@label", "Queued");
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: skeletonLoader;
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom;
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
top: queuedLabel.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
visible: !queuedPrintJobs.visible;
|
||||||
|
width: Math.min(800 * screenScaleFactor, maximumWidth);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
cameraUrl: OutputDevice.activeCameraUrl;
|
||||||
{
|
visible: OutputDevice.activeCameraUrl != "";
|
||||||
OutputDevice.setActiveCamera(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
12
plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml
Normal file
12
plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml
Normal 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;
|
||||||
|
}
|
|
@ -1,54 +1,45 @@
|
||||||
|
// 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.NetworkMJPGImage {
|
||||||
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.cameraUrl != null) {
|
||||||
z: 1
|
cameraImage.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.cameraUrl != null) {
|
||||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
|
cameraImage.start();
|
||||||
{
|
|
||||||
OutputDevice.activePrinter.camera.start()
|
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
{
|
if (OutputDevice.activePrinter != null && OutputDevice.activePrinter.cameraUrl != null) {
|
||||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
|
cameraImage.stop();
|
||||||
{
|
|
||||||
OutputDevice.activePrinter.camera.stop()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source:
|
source: {
|
||||||
{
|
if (OutputDevice.activePrinter != null && OutputDevice.activePrinter.cameraUrl != null) {
|
||||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null && OutputDevice.activePrinter.camera.latestImage)
|
return OutputDevice.activePrinter.cameraUrl;
|
||||||
{
|
|
||||||
return OutputDevice.activePrinter.camera.latestImage;
|
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
width: Math.min(imageWidth === 0 ? 800 * screenScaleFactor : imageWidth, maximumWidth);
|
||||||
|
z: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
color: UM.Theme.getColor("monitor_skeleton_fill");
|
||||||
|
radius: Math.round(width / 2);
|
||||||
|
visible: !printCoreConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
Item
|
// Actual content
|
||||||
{
|
Rectangle {
|
||||||
id: extruderCircle
|
anchors.fill: parent;
|
||||||
width: 30
|
border.width: UM.Theme.getSize("monitor_thick_lining").width;
|
||||||
height: 30
|
border.color: UM.Theme.getColor("monitor_lining_heavy");
|
||||||
|
color: "transparent";
|
||||||
anchors.verticalCenter: printAndMaterialLabel.verticalCenter
|
opacity: {
|
||||||
opacity:
|
if (printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null) {
|
||||||
{
|
return 0.5;
|
||||||
if(printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null)
|
}
|
||||||
{
|
return 1;
|
||||||
return 0.5
|
|
||||||
}
|
}
|
||||||
return 1
|
radius: Math.round(width / 2);
|
||||||
}
|
visible: printCoreConfiguration;
|
||||||
|
|
||||||
Rectangle
|
Label {
|
||||||
{
|
anchors.centerIn: parent;
|
||||||
anchors.fill: parent
|
color: UM.Theme.getColor("text");
|
||||||
radius: Math.round(width / 2)
|
font: UM.Theme.getFont("default_bold");
|
||||||
border.width: 2
|
text: printCoreConfiguration ? printCoreConfiguration.position + 1 : 0;
|
||||||
border.color: "black"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
anchors.centerIn: parent
|
|
||||||
font: UM.Theme.getFont("default_bold")
|
|
||||||
text: printCoreConfiguration.position + 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item
|
// Print core and material labels
|
||||||
{
|
Item {
|
||||||
id: printAndMaterialLabel
|
id: materialLabel
|
||||||
anchors
|
anchors {
|
||||||
{
|
left: extruderCircle.right;
|
||||||
right: parent.right
|
leftMargin: UM.Theme.getSize("default_margin").width;
|
||||||
left: extruderCircle.right
|
right: parent.right;
|
||||||
margins: UM.Theme.getSize("default_margin").width
|
top: parent.top;
|
||||||
}
|
}
|
||||||
height: childrenRect.height
|
height: UM.Theme.getSize("monitor_text_line").height;
|
||||||
|
|
||||||
Label
|
// Loading skeleton
|
||||||
{
|
Rectangle {
|
||||||
id: materialLabel
|
anchors.fill: parent;
|
||||||
text:
|
color: UM.Theme.getColor("monitor_skeleton_fill");
|
||||||
{
|
visible: !extruderInfo.printCoreConfiguration;
|
||||||
if(printCoreConfiguration != undefined && printCoreConfiguration.activeMaterial != undefined)
|
|
||||||
{
|
|
||||||
return printCoreConfiguration.activeMaterial.name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
elide: Text.ElideRight
|
|
||||||
width: parent.width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
220
plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml
Normal file
220
plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
// 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);
|
||||||
|
property var assigned: isAssigned(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.
|
||||||
|
visible: printJob.state == "queued" || running ? true : false;
|
||||||
|
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 && printJob.state == "queued" && !assigned) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
function isAssigned(job) {
|
||||||
|
if (!job) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return job.assignedPrinter ? true : false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
Rectangle
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
color: UM.Theme.getColor("monitor_card_background");
|
||||||
|
height: childrenRect.height;
|
||||||
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
|
|
||||||
{
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
left: parent.left
|
|
||||||
right: parent.horizontalCenter
|
|
||||||
margins: UM.Theme.getSize("wide_margin").width
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
// Main content
|
||||||
{
|
Item {
|
||||||
id: printJobName
|
id: mainContent;
|
||||||
text: printJob.name
|
height: 200 * screenScaleFactor; // TODO: Theme!
|
||||||
font: UM.Theme.getFont("default_bold")
|
width: parent.width;
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
// Left content
|
||||||
{
|
Item {
|
||||||
id: ownerName
|
anchors {
|
||||||
anchors.top: printJobName.bottom
|
bottom: parent.bottom;
|
||||||
text: printJob.owner
|
left: parent.left;
|
||||||
font: UM.Theme.getFont("default")
|
margins: UM.Theme.getSize("wide_margin").width;
|
||||||
opacity: 0.6
|
right: parent.horizontalCenter;
|
||||||
width: parent.width
|
top: parent.top;
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Image
|
|
||||||
{
|
|
||||||
id: printJobPreview
|
|
||||||
source: printJob.previewImageUrl
|
|
||||||
anchors.top: ownerName.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
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
|
|
||||||
{
|
|
||||||
// Content on the right side of the infobox.
|
|
||||||
anchors
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: targetPrinterLabel
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font: UM.Theme.getFont("default_bold")
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Item {
|
||||||
|
id: printJobName;
|
||||||
|
width: parent.width;
|
||||||
|
height: UM.Theme.getSize("monitor_text_line").height;
|
||||||
|
|
||||||
anchors
|
Rectangle {
|
||||||
{
|
color: UM.Theme.getColor("monitor_skeleton_fill");
|
||||||
left: parent.left
|
height: parent.height;
|
||||||
right: contextButton.left
|
visible: !printJob;
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
width: Math.round(parent.width / 3);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function switchPopupState()
|
|
||||||
{
|
|
||||||
popup.visible ? popup.close() : popup.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
Button
|
|
||||||
{
|
|
||||||
id: sendToTopButton
|
|
||||||
text: catalog.i18nc("@label", "Move to top")
|
|
||||||
onClicked:
|
|
||||||
{
|
|
||||||
sendToTopConfirmationDialog.visible = true;
|
|
||||||
popup.close();
|
|
||||||
}
|
}
|
||||||
width: parent.width
|
Label {
|
||||||
enabled: OutputDevice.queuedPrintJobs[0].key != printJob.key
|
anchors.fill: parent;
|
||||||
visible: enabled
|
color: UM.Theme.getColor("text");
|
||||||
anchors.top: parent.top
|
elide: Text.ElideRight;
|
||||||
anchors.topMargin: 18 * screenScaleFactor
|
font: UM.Theme.getFont("default_bold");
|
||||||
height: visible ? 39 * screenScaleFactor : 0 * screenScaleFactor
|
text: printJob && printJob.name ? printJob.name : ""; // Supress QML warnings
|
||||||
hoverEnabled: true
|
visible: printJob;
|
||||||
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
|
Item {
|
||||||
{
|
id: printJobOwnerName;
|
||||||
id: sendToTopConfirmationDialog
|
anchors {
|
||||||
title: catalog.i18nc("@window:title", "Move print job to top")
|
top: printJobName.bottom;
|
||||||
icon: StandardIcon.Warning
|
topMargin: Math.floor(UM.Theme.getSize("default_margin").height / 2);
|
||||||
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
|
height: UM.Theme.getSize("monitor_text_line").height;
|
||||||
Component.onCompleted: visible = false
|
width: parent.width;
|
||||||
onYes: OutputDevice.sendJobToTop(printJob.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button
|
Rectangle {
|
||||||
{
|
color: UM.Theme.getColor("monitor_skeleton_fill");
|
||||||
id: deleteButton
|
height: parent.height;
|
||||||
text: catalog.i18nc("@label", "Delete")
|
visible: !printJob;
|
||||||
onClicked:
|
width: Math.round(parent.width / 2);
|
||||||
{
|
|
||||||
deleteConfirmationDialog.visible = true;
|
|
||||||
popup.close();
|
|
||||||
}
|
}
|
||||||
width: parent.width
|
Label {
|
||||||
height: 39 * screenScaleFactor
|
anchors.fill: parent;
|
||||||
anchors.top: sendToTopButton.bottom
|
color: UM.Theme.getColor("text");
|
||||||
hoverEnabled: true
|
elide: Text.ElideRight;
|
||||||
background: Rectangle
|
font: UM.Theme.getFont("default");
|
||||||
{
|
text: printJob ? printJob.owner : ""; // Supress QML warnings
|
||||||
opacity: deleteButton.down || deleteButton.hovered ? 1 : 0
|
visible: printJob;
|
||||||
color: UM.Theme.getColor("viewport_background")
|
|
||||||
}
|
|
||||||
contentItem: Label
|
|
||||||
{
|
|
||||||
text: deleteButton.text
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog
|
Item {
|
||||||
{
|
id: printJobPreview;
|
||||||
id: deleteConfirmationDialog
|
property var useUltibot: false;
|
||||||
title: catalog.i18nc("@window:title", "Delete print job")
|
anchors {
|
||||||
icon: StandardIcon.Warning
|
bottom: parent.bottom;
|
||||||
text: catalog.i18nc("@label %1 is the name of a print job.", "Are you sure you want to delete %1?").arg(printJob.name)
|
horizontalCenter: parent.horizontalCenter;
|
||||||
standardButtons: StandardButton.Yes | StandardButton.No
|
top: printJobOwnerName.bottom;
|
||||||
Component.onCompleted: visible = false
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
onYes: OutputDevice.deleteJobFromQueue(printJob.key)
|
}
|
||||||
}
|
width: height;
|
||||||
}
|
|
||||||
|
|
||||||
background: Item
|
// Skeleton
|
||||||
{
|
Rectangle {
|
||||||
width: popup.width
|
anchors.fill: parent;
|
||||||
height: popup.height
|
color: UM.Theme.getColor("monitor_skeleton_fill");
|
||||||
|
radius: UM.Theme.getSize("default_margin").width;
|
||||||
DropShadow
|
visible: !printJob;
|
||||||
{
|
|
||||||
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
|
// Actual content
|
||||||
{
|
Image {
|
||||||
id: bloop
|
id: previewImage;
|
||||||
color: UM.Theme.getColor("setting_control")
|
anchors.fill: parent;
|
||||||
width: parent.width
|
opacity: printJob && printJob.state == "error" ? 0.5 : 1.0;
|
||||||
anchors.top: parent.top
|
source: printJob ? printJob.previewImageUrl : "";
|
||||||
anchors.topMargin: 8 * screenScaleFactor // Because of the shadow + point
|
visible: printJob;
|
||||||
anchors.bottom: parent.bottom
|
}
|
||||||
anchors.bottomMargin: 8 * screenScaleFactor // Because of the shadow
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
UM.RecolorImage {
|
||||||
|
id: statusImage;
|
||||||
|
anchors.centerIn: printJobPreview;
|
||||||
|
color: UM.Theme.getColor("monitor_image_overlay");
|
||||||
|
height: 0.5 * printJobPreview.height;
|
||||||
|
source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : "";
|
||||||
|
sourceSize {
|
||||||
|
height: height;
|
||||||
|
width: width;
|
||||||
|
}
|
||||||
|
visible: source != "";
|
||||||
|
width: 0.5 * printJobPreview.width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
height: parent.height - 2 * UM.Theme.getSize("default_margin").height;
|
||||||
NumberAnimation { property: "visible"; duration: 75; }
|
width: UM.Theme.getSize("default_lining").width;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
color: UM.Theme.getColor("text");
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
font: UM.Theme.getFont("default_bold");
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// PrintCore && Material config
|
|
||||||
Row
|
|
||||||
{
|
|
||||||
id: extrudersInfo
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
anchors
|
PrintJobContextMenu {
|
||||||
{
|
id: contextButton;
|
||||||
left: parent.left
|
anchors {
|
||||||
right: parent.right
|
right: mainContent.right;
|
||||||
}
|
rightMargin: UM.Theme.getSize("default_margin").width * 3 + root.shadowRadius;
|
||||||
height: childrenRect.height
|
top: mainContent.top;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
spacing: UM.Theme.getSize("default_margin").width
|
}
|
||||||
|
printJob: root.printJob;
|
||||||
PrintCoreConfiguration
|
visible: root.printJob;
|
||||||
{
|
|
||||||
id: leftExtruderInfo
|
|
||||||
width: Math.round(parent.width / 2) * screenScaleFactor
|
|
||||||
printCoreConfiguration: printJob.configuration.extruderConfigurations[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintCoreConfiguration
|
|
||||||
{
|
|
||||||
id: rightExtruderInfo
|
|
||||||
width: Math.round(parent.width / 2) * screenScaleFactor
|
|
||||||
printCoreConfiguration: printJob.configuration.extruderConfigurations[1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Item {
|
||||||
|
id: configChangesBox;
|
||||||
|
height: childrenRect.height;
|
||||||
|
visible: printJob && printJob.configurationChanges.length !== 0;
|
||||||
|
width: parent.width;
|
||||||
|
|
||||||
Rectangle
|
// Config change toggle
|
||||||
{
|
Rectangle {
|
||||||
color: UM.Theme.getColor("viewport_background")
|
id: configChangeToggle;
|
||||||
width: 2 * screenScaleFactor
|
color: {
|
||||||
anchors.top: parent.top
|
if (configChangeToggleArea.containsMouse) {
|
||||||
anchors.bottom: parent.bottom
|
return UM.Theme.getColor("viewport_background"); // TODO: Theme!
|
||||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
} else {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
75
plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml
Normal file
75
plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
59
plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml
Normal file
59
plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
270
plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml
Normal file
270
plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
// 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 + UM.Theme.getSize("default_margin").height;
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
radius: root.shadowRadius;
|
||||||
|
verticalOffset: root.shadowOffset;
|
||||||
|
color: "#3F000000"; // 25% shadow
|
||||||
|
}
|
||||||
|
layer.enabled: true
|
||||||
|
width: parent.width - 2 * shadowRadius;
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: cardContents;
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
height: childrenRect.height;
|
||||||
|
width: parent.width;
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height;
|
||||||
|
|
||||||
|
// Main card
|
||||||
|
Item {
|
||||||
|
id: mainCard;
|
||||||
|
height: 60 * screenScaleFactor;
|
||||||
|
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("wide_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalLine {
|
||||||
|
anchors {
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width;
|
||||||
|
}
|
||||||
|
visible: root.printer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed card
|
||||||
|
PrinterCardDetails {
|
||||||
|
collapsed: root.collapsed;
|
||||||
|
printer: root.printer;
|
||||||
|
visible: root.printer;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraButton {
|
||||||
|
id: showCameraButton;
|
||||||
|
anchors {
|
||||||
|
left: parent.left;
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width;
|
||||||
|
}
|
||||||
|
iconSource: "../svg/camera-icon.svg";
|
||||||
|
visible: root.printer && root.printJob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress bar
|
||||||
|
PrinterCardProgressBar {
|
||||||
|
anchors {
|
||||||
|
top: cardContents.bottom;
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
|
}
|
||||||
|
visible: printer && printer.activePrintJob != null;
|
||||||
|
width: parent.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
color: familyNameLabel.text.length < 1 ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_pill_background");
|
||||||
anchors.right: parent.right
|
height: parent.height;
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
radius: 0.5 * height;
|
||||||
radius: 0.5 * height
|
width: parent.width;
|
||||||
}
|
}
|
||||||
Label
|
|
||||||
{
|
Label {
|
||||||
id: familyNameLabel
|
id: familyNameLabel;
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent;
|
||||||
text: ""
|
color: UM.Theme.getColor("text");
|
||||||
|
text: "";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,84 +1,65 @@
|
||||||
|
// 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 cameraUrl: "";
|
||||||
|
|
||||||
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.setActiveCameraUrl("");
|
||||||
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.NetworkMJPGImage {
|
||||||
{
|
|
||||||
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: {
|
||||||
|
if (visible) {
|
||||||
|
if (cameraUrl != "") {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cameraUrl != "") {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source: cameraUrl
|
||||||
|
width: Math.min(imageWidth === 0 ? 800 * screenScaleFactor : imageWidth, maximumWidth);
|
||||||
z: 1
|
z: 1
|
||||||
onVisibleChanged:
|
|
||||||
{
|
|
||||||
if(visible)
|
|
||||||
{
|
|
||||||
if(camera != null)
|
|
||||||
{
|
|
||||||
camera.start()
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if(camera != null)
|
|
||||||
{
|
|
||||||
camera.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
source:
|
|
||||||
{
|
|
||||||
if(camera != null && camera.latestImage != null)
|
|
||||||
{
|
|
||||||
return camera.latestImage;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea
|
MouseArea {
|
||||||
{
|
anchors.fill: cameraImage;
|
||||||
anchors.fill: cameraImage
|
onClicked: {
|
||||||
onClicked:
|
OutputDevice.setActiveCameraUrl("");
|
||||||
{
|
|
||||||
OutputDevice.setActiveCamera(null)
|
|
||||||
}
|
}
|
||||||
z: 1
|
z: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
Row {
|
||||||
{
|
objectName: "networkPrinterConnectButton";
|
||||||
objectName: "networkPrinterConnectButton"
|
spacing: UM.Theme.getSize("default_margin").width;
|
||||||
visible: isUM3
|
visible: isUM3;
|
||||||
spacing: UM.Theme.getSize("default_margin").width
|
|
||||||
|
|
||||||
Button
|
Button {
|
||||||
{
|
height: UM.Theme.getSize("save_button_save_to_button").height;
|
||||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication();
|
||||||
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer")
|
style: UM.Theme.styles.sidebar_action_button;
|
||||||
text: catalog.i18nc("@action:button", "Request Access")
|
text: catalog.i18nc("@action:button", "Request Access");
|
||||||
style: UM.Theme.styles.sidebar_action_button
|
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer");
|
||||||
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication()
|
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested;
|
||||||
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button
|
Button {
|
||||||
{
|
height: UM.Theme.getSize("save_button_save_to_button").height;
|
||||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
onClicked: connectActionDialog.show();
|
||||||
tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer")
|
style: UM.Theme.styles.sidebar_action_button;
|
||||||
text: catalog.i18nc("@action:button", "Connect")
|
text: catalog.i18nc("@action:button", "Connect");
|
||||||
style: UM.Theme.styles.sidebar_action_button
|
tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer");
|
||||||
onClicked: connectActionDialog.show()
|
visible: !printerConnected;
|
||||||
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
|
|
||||||
{
|
Column {
|
||||||
Repeater
|
Repeater {
|
||||||
{
|
id: nozzleColumn;
|
||||||
id: nozzleColumn
|
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].hotendIds : null;
|
||||||
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].hotendIds : null
|
|
||||||
Label { text: nozzleColumn.model[index] }
|
Label {
|
||||||
|
text: nozzleColumn.model[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column
|
|
||||||
{
|
Column {
|
||||||
Repeater
|
Repeater {
|
||||||
{
|
id: materialColumn;
|
||||||
id: materialColumn
|
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].materialNames : null;
|
||||||
model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].materialNames : null
|
|
||||||
Label { text: materialColumn.model[index] }
|
Label {
|
||||||
|
text: materialColumn.model[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button
|
Button {
|
||||||
{
|
onClicked: manager.loadConfigurationFromPrinter();
|
||||||
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura")
|
text: catalog.i18nc("@action:button", "Activate Configuration");
|
||||||
text: catalog.i18nc("@action:button", "Activate Configuration")
|
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura");
|
||||||
visible: false // printerConnected && !isClusterPrinter()
|
visible: false; // printerConnected && !isClusterPrinter()
|
||||||
onClicked: manager.loadConfigurationFromPrinter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UM.I18nCatalog{id: catalog; name:"cura"}
|
|
||||||
}
|
}
|
||||||
|
|
1
plugins/UM3NetworkPrinting/resources/svg/ultibot.svg
Normal file
1
plugins/UM3NetworkPrinting/resources/svg/ultibot.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
|
@ -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 |
|
@ -21,12 +21,12 @@ 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 .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
|
||||||
|
@ -46,7 +46,8 @@ i18n_catalog = i18nCatalog("cura")
|
||||||
class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||||
printJobsChanged = pyqtSignal()
|
printJobsChanged = pyqtSignal()
|
||||||
activePrinterChanged = pyqtSignal()
|
activePrinterChanged = pyqtSignal()
|
||||||
activeCameraChanged = pyqtSignal()
|
activeCameraUrlChanged = 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 +61,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,14 +92,14 @@ 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
|
||||||
|
|
||||||
self._latest_reply_handler = None # type: Optional[QNetworkReply]
|
self._latest_reply_handler = None # type: Optional[QNetworkReply]
|
||||||
self._sending_job = None
|
self._sending_job = None
|
||||||
|
|
||||||
self._active_camera = None # type: Optional[NetworkCamera]
|
self._active_camera_url = QUrl() # type: QUrl
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||||
self.writeStarted.emit(self)
|
self.writeStarted.emit(self)
|
||||||
|
@ -261,30 +263,21 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||||
def activePrinter(self) -> Optional[PrinterOutputModel]:
|
def activePrinter(self) -> Optional[PrinterOutputModel]:
|
||||||
return self._active_printer
|
return self._active_printer
|
||||||
|
|
||||||
@pyqtProperty(QObject, notify=activeCameraChanged)
|
|
||||||
def activeCamera(self) -> Optional[NetworkCamera]:
|
|
||||||
return self._active_camera
|
|
||||||
|
|
||||||
@pyqtSlot(QObject)
|
@pyqtSlot(QObject)
|
||||||
def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None:
|
def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None:
|
||||||
if self._active_printer != printer:
|
if self._active_printer != printer:
|
||||||
if self._active_printer and self._active_printer.camera:
|
|
||||||
self._active_printer.camera.stop()
|
|
||||||
self._active_printer = printer
|
self._active_printer = printer
|
||||||
self.activePrinterChanged.emit()
|
self.activePrinterChanged.emit()
|
||||||
|
|
||||||
@pyqtSlot(QObject)
|
@pyqtProperty(QUrl, notify = activeCameraUrlChanged)
|
||||||
def setActiveCamera(self, camera: Optional[NetworkCamera]) -> None:
|
def activeCameraUrl(self) -> "QUrl":
|
||||||
if self._active_camera != camera:
|
return self._active_camera_url
|
||||||
if self._active_camera:
|
|
||||||
self._active_camera.stop()
|
|
||||||
|
|
||||||
self._active_camera = camera
|
@pyqtSlot(QUrl)
|
||||||
|
def setActiveCameraUrl(self, camera_url: "QUrl") -> None:
|
||||||
if self._active_camera:
|
if self._active_camera_url != camera_url:
|
||||||
self._active_camera.start()
|
self._active_camera_url = camera_url
|
||||||
|
self.activeCameraUrlChanged.emit()
|
||||||
self.activeCameraChanged.emit()
|
|
||||||
|
|
||||||
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
|
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
|
||||||
if self._progress_message:
|
if self._progress_message:
|
||||||
|
@ -349,15 +342,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 +403,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 +457,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
|
||||||
|
|
||||||
|
@ -533,12 +538,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||||
def _createPrinterModel(self, data: Dict[str, Any]) -> PrinterOutputModel:
|
def _createPrinterModel(self, data: Dict[str, Any]) -> PrinterOutputModel:
|
||||||
printer = PrinterOutputModel(output_controller = ClusterUM3PrinterOutputController(self),
|
printer = PrinterOutputModel(output_controller = ClusterUM3PrinterOutputController(self),
|
||||||
number_of_extruders = self._number_of_extruders)
|
number_of_extruders = self._number_of_extruders)
|
||||||
printer.setCamera(NetworkCamera("http://" + data["ip_address"] + ":8080/?action=stream"))
|
printer.setCameraUrl(QUrl("http://" + data["ip_address"] + ":8080/?action=stream"))
|
||||||
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 +563,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 +579,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 +646,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 +690,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
|
||||||
|
|
29
plugins/UM3NetworkPrinting/src/ConfigurationChangeModel.py
Normal file
29
plugins/UM3NetworkPrinting/src/ConfigurationChangeModel.py
Normal 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
|
|
@ -7,7 +7,6 @@ from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutp
|
||||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
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.Settings.ContainerManager import ContainerManager
|
from cura.Settings.ContainerManager import ContainerManager
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
@ -18,7 +17,7 @@ from UM.i18n import i18nCatalog
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
|
|
||||||
from PyQt5.QtNetwork import QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkRequest
|
||||||
from PyQt5.QtCore import QTimer
|
from PyQt5.QtCore import QTimer, QUrl
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from .LegacyUM3PrinterOutputController import LegacyUM3PrinterOutputController
|
from .LegacyUM3PrinterOutputController import LegacyUM3PrinterOutputController
|
||||||
|
@ -568,7 +567,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||||
# Quickest way to get the firmware version is to grab it from the zeroconf.
|
# Quickest way to get the firmware version is to grab it from the zeroconf.
|
||||||
firmware_version = self._properties.get(b"firmware_version", b"").decode("utf-8")
|
firmware_version = self._properties.get(b"firmware_version", b"").decode("utf-8")
|
||||||
self._printers = [PrinterOutputModel(output_controller=self._output_controller, number_of_extruders=self._number_of_extruders, firmware_version=firmware_version)]
|
self._printers = [PrinterOutputModel(output_controller=self._output_controller, number_of_extruders=self._number_of_extruders, firmware_version=firmware_version)]
|
||||||
self._printers[0].setCamera(NetworkCamera("http://" + self._address + ":8080/?action=stream"))
|
self._printers[0].setCameraUrl(QUrl("http://" + self._address + ":8080/?action=stream"))
|
||||||
for extruder in self._printers[0].extruders:
|
for extruder in self._printers[0].extruders:
|
||||||
extruder.activeMaterialChanged.connect(self.materialIdChanged)
|
extruder.activeMaterialChanged.connect(self.materialIdChanged)
|
||||||
extruder.hotendIDChanged.connect(self.hotendIdChanged)
|
extruder.hotendIDChanged.connect(self.hotendIdChanged)
|
||||||
|
|
29
plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py
Normal file
29
plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py
Normal 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()
|
|
@ -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,15 +225,18 @@ 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)
|
||||||
|
@ -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])
|
||||||
|
|
||||||
|
|
|
@ -753,8 +753,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic ABS",
|
"display_name": "Generic ABS",
|
||||||
"description": "The generic ABS profile which other profiles can be based upon.",
|
"description": "The generic ABS profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -771,8 +771,44 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic BAM",
|
"display_name": "Generic BAM",
|
||||||
"description": "The generic BAM profile which other profiles can be based upon.",
|
"description": "The generic BAM profile which other profiles can be based upon.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Generic",
|
||||||
|
"display_name": "Generic",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"description": "Professional 3D printing made accessible."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GenericCFFCPE": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "GenericCFFCPE",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Generic CFF CPE",
|
||||||
|
"description": "The generic CFF CPE profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.0.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Generic",
|
||||||
|
"display_name": "Generic",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"description": "Professional 3D printing made accessible."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GenericCFFPA": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "GenericCFFPA",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Generic CFF PA",
|
||||||
|
"description": "The generic CFF PA profile which other profiles can be based upon.",
|
||||||
|
"package_version": "1.0.0",
|
||||||
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -789,8 +825,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic CPE",
|
"display_name": "Generic CPE",
|
||||||
"description": "The generic CPE profile which other profiles can be based upon.",
|
"description": "The generic CPE profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -807,8 +843,44 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic CPE+",
|
"display_name": "Generic CPE+",
|
||||||
"description": "The generic CPE+ profile which other profiles can be based upon.",
|
"description": "The generic CPE+ profile which other profiles can be based upon.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Generic",
|
||||||
|
"display_name": "Generic",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"description": "Professional 3D printing made accessible."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GenericGFFCPE": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "GenericGFFCPE",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Generic GFF CPE",
|
||||||
|
"description": "The generic GFF CPE profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.0.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Generic",
|
||||||
|
"display_name": "Generic",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
|
"description": "Professional 3D printing made accessible."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GenericGFFPA": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "GenericGFFPA",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Generic GFF PA",
|
||||||
|
"description": "The generic GFF PA profile which other profiles can be based upon.",
|
||||||
|
"package_version": "1.0.0",
|
||||||
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -826,7 +898,7 @@
|
||||||
"display_name": "Generic HIPS",
|
"display_name": "Generic HIPS",
|
||||||
"description": "The generic HIPS profile which other profiles can be based upon.",
|
"description": "The generic HIPS profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.0.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -843,8 +915,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic Nylon",
|
"display_name": "Generic Nylon",
|
||||||
"description": "The generic Nylon profile which other profiles can be based upon.",
|
"description": "The generic Nylon profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -861,8 +933,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic PC",
|
"display_name": "Generic PC",
|
||||||
"description": "The generic PC profile which other profiles can be based upon.",
|
"description": "The generic PC profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -880,7 +952,7 @@
|
||||||
"display_name": "Generic PETG",
|
"display_name": "Generic PETG",
|
||||||
"description": "The generic PETG profile which other profiles can be based upon.",
|
"description": "The generic PETG profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.0.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -897,8 +969,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic PLA",
|
"display_name": "Generic PLA",
|
||||||
"description": "The generic PLA profile which other profiles can be based upon.",
|
"description": "The generic PLA profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -915,8 +987,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic PP",
|
"display_name": "Generic PP",
|
||||||
"description": "The generic PP profile which other profiles can be based upon.",
|
"description": "The generic PP profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -933,8 +1005,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic PVA",
|
"display_name": "Generic PVA",
|
||||||
"description": "The generic PVA profile which other profiles can be based upon.",
|
"description": "The generic PVA profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -951,8 +1023,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic Tough PLA",
|
"display_name": "Generic Tough PLA",
|
||||||
"description": "The generic Tough PLA profile which other profiles can be based upon.",
|
"description": "The generic Tough PLA profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.0.1",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -969,8 +1041,8 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Generic TPU",
|
"display_name": "Generic TPU",
|
||||||
"description": "The generic TPU profile which other profiles can be based upon.",
|
"description": "The generic TPU profile which other profiles can be based upon.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 6,
|
"sdk_version": 5,
|
||||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Generic",
|
"author_id": "Generic",
|
||||||
|
@ -1077,7 +1149,7 @@
|
||||||
"website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/",
|
"website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/",
|
||||||
"author": {
|
"author": {
|
||||||
"author_id": "Fiberlogy",
|
"author_id": "Fiberlogy",
|
||||||
"diplay_name": "Fiberlogy S.A.",
|
"display_name": "Fiberlogy S.A.",
|
||||||
"email": "grzegorz.h@fiberlogy.com",
|
"email": "grzegorz.h@fiberlogy.com",
|
||||||
"website": "http://fiberlogy.com"
|
"website": "http://fiberlogy.com"
|
||||||
}
|
}
|
||||||
|
@ -1225,7 +1297,7 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker ABS",
|
"display_name": "Ultimaker ABS",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/abs",
|
"website": "https://ultimaker.com/products/materials/abs",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1238,13 +1310,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UltimakerBAM": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "UltimakerBAM",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Ultimaker Breakaway",
|
||||||
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://ultimaker.com/products/materials/breakaway",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Ultimaker",
|
||||||
|
"display_name": "Ultimaker B.V.",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"description": "Professional 3D printing made accessible.",
|
||||||
|
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"UltimakerCPE": {
|
"UltimakerCPE": {
|
||||||
"package_info": {
|
"package_info": {
|
||||||
"package_id": "UltimakerCPE",
|
"package_id": "UltimakerCPE",
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker CPE",
|
"display_name": "Ultimaker CPE",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/abs",
|
"website": "https://ultimaker.com/products/materials/abs",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1257,13 +1348,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UltimakerCPEP": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "UltimakerCPEP",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Ultimaker CPE+",
|
||||||
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://ultimaker.com/products/materials/cpe",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Ultimaker",
|
||||||
|
"display_name": "Ultimaker B.V.",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"description": "Professional 3D printing made accessible.",
|
||||||
|
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"UltimakerNylon": {
|
"UltimakerNylon": {
|
||||||
"package_info": {
|
"package_info": {
|
||||||
"package_id": "UltimakerNylon",
|
"package_id": "UltimakerNylon",
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker Nylon",
|
"display_name": "Ultimaker Nylon",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/abs",
|
"website": "https://ultimaker.com/products/materials/abs",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1282,7 +1392,7 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker PC",
|
"display_name": "Ultimaker PC",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/pc",
|
"website": "https://ultimaker.com/products/materials/pc",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1301,7 +1411,7 @@
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker PLA",
|
"display_name": "Ultimaker PLA",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/abs",
|
"website": "https://ultimaker.com/products/materials/abs",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1314,13 +1424,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UltimakerPP": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "UltimakerPP",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Ultimaker PP",
|
||||||
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://ultimaker.com/products/materials/pp",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Ultimaker",
|
||||||
|
"display_name": "Ultimaker B.V.",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"description": "Professional 3D printing made accessible.",
|
||||||
|
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"UltimakerPVA": {
|
"UltimakerPVA": {
|
||||||
"package_info": {
|
"package_info": {
|
||||||
"package_id": "UltimakerPVA",
|
"package_id": "UltimakerPVA",
|
||||||
"package_type": "material",
|
"package_type": "material",
|
||||||
"display_name": "Ultimaker PVA",
|
"display_name": "Ultimaker PVA",
|
||||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
"package_version": "1.0.0",
|
"package_version": "1.1.0",
|
||||||
"sdk_version": 5,
|
"sdk_version": 5,
|
||||||
"website": "https://ultimaker.com/products/materials/abs",
|
"website": "https://ultimaker.com/products/materials/abs",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1333,6 +1462,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UltimakerTPU": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "UltimakerTPU",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Ultimaker TPU 95A",
|
||||||
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
|
"package_version": "1.1.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://ultimaker.com/products/materials/tpu-95a",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Ultimaker",
|
||||||
|
"display_name": "Ultimaker B.V.",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"description": "Professional 3D printing made accessible.",
|
||||||
|
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"UltimakerTPLA": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "UltimakerTPLA",
|
||||||
|
"package_type": "material",
|
||||||
|
"display_name": "Ultimaker Tough PLA",
|
||||||
|
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||||
|
"package_version": "1.0.0",
|
||||||
|
"sdk_version": 5,
|
||||||
|
"website": "https://ultimaker.com/products/materials/tough-pla",
|
||||||
|
"author": {
|
||||||
|
"author_id": "Ultimaker",
|
||||||
|
"display_name": "Ultimaker B.V.",
|
||||||
|
"email": "materials@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"description": "Professional 3D printing made accessible.",
|
||||||
|
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"VertexDeltaABS": {
|
"VertexDeltaABS": {
|
||||||
"package_info": {
|
"package_info": {
|
||||||
"package_id": "VertexDeltaABS",
|
"package_id": "VertexDeltaABS",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "3Dator GmbH",
|
"author": "3Dator GmbH",
|
||||||
"manufacturer": "3Dator GmbH",
|
"manufacturer": "3Dator GmbH",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"supports_usb_connection": true,
|
"supports_usb_connection": true,
|
||||||
"platform": "3dator_platform.stl",
|
"platform": "3dator_platform.stl",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
88
resources/definitions/anycubic_4max.def.json
Normal file
88
resources/definitions/anycubic_4max.def.json
Normal 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"}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
"author": "TheTobby",
|
"author": "TheTobby",
|
||||||
"manufacturer": "Anycubic",
|
"manufacturer": "Anycubic",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "anycubic_i3_mega_platform.stl",
|
"platform": "anycubic_i3_mega_platform.stl",
|
||||||
"has_materials": false,
|
"has_materials": false,
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
resources/definitions/deltacomb.def.json
Normal file → Executable file
5
resources/definitions/deltacomb.def.json
Normal file → Executable file
|
@ -8,7 +8,6 @@
|
||||||
"manufacturer": "Deltacomb 3D",
|
"manufacturer": "Deltacomb 3D",
|
||||||
"category": "Other",
|
"category": "Other",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "deltacomb.stl",
|
"platform": "deltacomb.stl",
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
@ -33,6 +32,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 +55,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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"category": "Other",
|
"category": "Other",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"platform": "fabtotum_platform.stl",
|
"platform": "fabtotum_platform.stl",
|
||||||
"icon": "fabtotum_platform.png",
|
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
"has_variants": true,
|
"has_variants": true,
|
||||||
"variants_name": "Head",
|
"variants_name": "Head",
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Simon Cor",
|
"author": "Simon Cor",
|
||||||
"manufacturer": "German RepRap",
|
"manufacturer": "German RepRap",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker.png",
|
|
||||||
"platform": "grr_neo_platform.stl",
|
"platform": "grr_neo_platform.stl",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Claudio Sampaio (Patola)",
|
"author": "Claudio Sampaio (Patola)",
|
||||||
"manufacturer": "Other",
|
"manufacturer": "Other",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "kossel_platform.stl",
|
"platform": "kossel_platform.stl",
|
||||||
"platform_offset": [0, -0.25, 0],
|
"platform_offset": [0, -0.25, 0],
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Chris Petersen",
|
"author": "Chris Petersen",
|
||||||
"manufacturer": "OpenBeam",
|
"manufacturer": "OpenBeam",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "kossel_pro_build_platform.stl",
|
"platform": "kossel_pro_build_platform.stl",
|
||||||
"platform_offset": [0, -0.25, 0],
|
"platform_offset": [0, -0.25, 0],
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "makeR",
|
"author": "makeR",
|
||||||
"manufacturer": "makeR",
|
"manufacturer": "makeR",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "makeR_pegasus_platform.stl",
|
"platform": "makeR_pegasus_platform.stl",
|
||||||
"platform_offset": [-200, -10, 200],
|
"platform_offset": [-200, -10, 200],
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "makeR",
|
"author": "makeR",
|
||||||
"manufacturer": "makeR",
|
"manufacturer": "makeR",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "makeR_prusa_tairona_i3_platform.stl",
|
"platform": "makeR_prusa_tairona_i3_platform.stl",
|
||||||
"platform_offset": [-2, 0, 0],
|
"platform_offset": [-2, 0, 0],
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "tvlgiao",
|
"author": "tvlgiao",
|
||||||
"manufacturer": "3DMaker",
|
"manufacturer": "3DMaker",
|
||||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj",
|
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"platform": "makerstarter_platform.stl",
|
"platform": "makerstarter_platform.stl",
|
||||||
"preferred_quality_type": "draft",
|
"preferred_quality_type": "draft",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Quillford",
|
"author": "Quillford",
|
||||||
"manufacturer": "Prusajr",
|
"manufacturer": "Prusajr",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "prusai3_platform.stl",
|
"platform": "prusai3_platform.stl",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Apsu, Nounours2099",
|
"author": "Apsu, Nounours2099",
|
||||||
"manufacturer": "Prusa Research",
|
"manufacturer": "Prusa Research",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "prusai3_platform.stl",
|
"platform": "prusai3_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "guigashm",
|
"author": "guigashm",
|
||||||
"manufacturer": "Prusajr",
|
"manufacturer": "Prusajr",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"platform": "prusai3_xl_platform.stl",
|
"platform": "prusai3_xl_platform.stl",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "PouncingIguana, JJ",
|
"author": "PouncingIguana, JJ",
|
||||||
"manufacturer": "SeeMeCNC",
|
"manufacturer": "SeeMeCNC",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "artemis_platform.stl",
|
"platform": "artemis_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "PouncingIguana, JJ",
|
"author": "PouncingIguana, JJ",
|
||||||
"manufacturer": "SeeMeCNC",
|
"manufacturer": "SeeMeCNC",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "rostock_platform.stl",
|
"platform": "rostock_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "TheTobby",
|
"author": "TheTobby",
|
||||||
"manufacturer": "Tevo",
|
"manufacturer": "Tevo",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"has_materials": false,
|
"has_materials": false,
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
"platform": "tevo_blackwidow.stl",
|
"platform": "tevo_blackwidow.stl",
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"author": "TheAssassin",
|
"author": "TheAssassin",
|
||||||
"manufacturer": "Tevo",
|
"manufacturer": "Tevo",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "prusai3_platform.stl",
|
"platform": "prusai3_platform.stl",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "nean",
|
"author": "nean",
|
||||||
"manufacturer": "Tevo",
|
"manufacturer": "Tevo",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"machine_extruder_trains": {
|
"machine_extruder_trains": {
|
||||||
"0": "tevo_tornado_extruder_0"
|
"0": "tevo_tornado_extruder_0"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"manufacturer": "uBuild-3D",
|
"manufacturer": "uBuild-3D",
|
||||||
"category": "Other",
|
"category": "Other",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_uBuild-3D",
|
|
||||||
"platform": "mr_bot_280_platform.stl",
|
"platform": "mr_bot_280_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"preferred_quality_type": "draft",
|
"preferred_quality_type": "draft",
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"manufacturer": "Ultimaker B.V.",
|
"manufacturer": "Ultimaker B.V.",
|
||||||
"weight": 3,
|
"weight": 3,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"platform": "ultimaker2_platform.obj",
|
"platform": "ultimaker2_platform.obj",
|
||||||
"platform_texture": "Ultimaker2backplate.png",
|
"platform_texture": "Ultimaker2backplate.png",
|
||||||
"platform_offset": [9, 0, 0],
|
"platform_offset": [9, 0, 0],
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"quality_definition": "ultimaker2",
|
"quality_definition": "ultimaker2",
|
||||||
"weight": 3,
|
"weight": 3,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"platform": "ultimaker2_platform.obj",
|
"platform": "ultimaker2_platform.obj",
|
||||||
"platform_texture": "Ultimaker2Extendedbackplate.png",
|
"platform_texture": "Ultimaker2Extendedbackplate.png",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"quality_definition": "ultimaker2",
|
"quality_definition": "ultimaker2",
|
||||||
"weight": 3,
|
"weight": 3,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"platform": "ultimaker2go_platform.obj",
|
"platform": "ultimaker2go_platform.obj",
|
||||||
"platform_texture": "Ultimaker2Gobackplate.png",
|
"platform_texture": "Ultimaker2Gobackplate.png",
|
||||||
"platform_offset": [0, 0, 0],
|
"platform_offset": [0, 0, 0],
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"manufacturer": "Ultimaker B.V.",
|
"manufacturer": "Ultimaker B.V.",
|
||||||
"weight": 4,
|
"weight": 4,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker.png",
|
|
||||||
"platform": "ultimaker_platform.stl",
|
"platform": "ultimaker_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"manufacturer": "Ultimaker B.V.",
|
"manufacturer": "Ultimaker B.V.",
|
||||||
"weight": 4,
|
"weight": 4,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker.png",
|
|
||||||
"platform": "ultimaker_platform.stl",
|
"platform": "ultimaker_platform.stl",
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"has_machine_quality": true,
|
"has_machine_quality": true,
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"manufacturer": "Ultimaker B.V.",
|
"manufacturer": "Ultimaker B.V.",
|
||||||
"weight": 4,
|
"weight": 4,
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker.png",
|
|
||||||
"platform": "ultimaker2_platform.obj",
|
"platform": "ultimaker2_platform.obj",
|
||||||
"platform_texture": "UltimakerPlusbackplate.png",
|
"platform_texture": "UltimakerPlusbackplate.png",
|
||||||
"quality_definition": "ultimaker_original",
|
"quality_definition": "ultimaker_original",
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
"author": "Unimatech",
|
"author": "Unimatech",
|
||||||
"manufacturer": "Unimatech",
|
"manufacturer": "Unimatech",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2.png",
|
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
{
|
{
|
||||||
"0": "uniqbot_one_extruder_0"
|
"0": "uniqbot_one_extruder_0"
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"manufacturer": "Velleman",
|
"manufacturer": "Velleman",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "Vertex_build_panel.stl",
|
"platform": "Vertex_build_panel.stl",
|
||||||
"platform_offset": [0, -3, 0],
|
"platform_offset": [0, -3, 0],
|
||||||
"supports_usb_connection": true,
|
"supports_usb_connection": true,
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"manufacturer": "Velleman",
|
"manufacturer": "Velleman",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker2",
|
|
||||||
"platform": "Vertex_build_panel.stl",
|
"platform": "Vertex_build_panel.stl",
|
||||||
"platform_offset": [0, -3, 0],
|
"platform_offset": [0, -3, 0],
|
||||||
"machine_extruder_trains": {
|
"machine_extruder_trains": {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_225_145_platform.obj",
|
"platform": "wanhao_225_145_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_200_200_platform.obj",
|
"platform": "wanhao_200_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_200_200_platform.obj",
|
"platform": "wanhao_200_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_300_200_platform.obj",
|
"platform": "wanhao_300_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_300_200_platform.obj",
|
"platform": "wanhao_300_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_200_200_platform.obj",
|
"platform": "wanhao_200_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_110_110_platform.obj",
|
"platform": "wanhao_110_110_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"author": "Ricardo Snoek",
|
"author": "Ricardo Snoek",
|
||||||
"manufacturer": "Wanhao",
|
"manufacturer": "Wanhao",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "wanhao-icon.png",
|
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"platform": "wanhao_200_200_platform.obj",
|
"platform": "wanhao_200_200_platform.obj",
|
||||||
"platform_texture": "Wanhaobackplate.png",
|
"platform_texture": "Wanhaobackplate.png",
|
||||||
|
|
16
resources/extruders/anycubic_4max_extruder_0.def.json
Normal file
16
resources/extruders/anycubic_4max_extruder_0.def.json
Normal 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
0
resources/extruders/deltacomb_extruder_0.def.json
Normal file → Executable file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue