Merge branch '3.5' into CURA-5682_material_manager_leftovers

This commit is contained in:
Diego Prado Gesto 2018-09-12 17:12:40 +02:00
commit 7e1a833e9c
120 changed files with 62031 additions and 1842 deletions

13
Jenkinsfile vendored
View File

@ -12,6 +12,19 @@ parallel_nodes(['linux && cura', 'windows && cura']) {
// If any error occurs during building, we want to catch it and continue with the "finale" stage.
catchError {
stage('Pre Checks') {
if (isUnix()) {
try {
sh """
echo 'Check for duplicate shortcut keys in all translation files.'
${env.CURA_ENVIRONMENT_PATH}/master/bin/python3 scripts/check_shortcut_keys.py
"""
} catch(e) {
currentBuild.result = "UNSTABLE"
}
}
}
// Building and testing should happen in a subdirectory.
dir('build') {
// Perform the "build". Since Uranium is Python code, this basically only ensures CMake is setup.

View File

@ -528,7 +528,7 @@ class BuildVolume(SceneNode):
def _onStackChanged(self):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
for extruder in extruders:
extruder.propertyChanged.disconnect(self._onSettingPropertyChanged)
@ -536,7 +536,7 @@ class BuildVolume(SceneNode):
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingPropertyChanged)

View File

@ -19,5 +19,11 @@ class CameraImageProvider(QQuickImageProvider):
return image, QSize(15, 15)
except AttributeError:
pass
try:
image = output_device.activeCamera.getImage()
return image, QSize(15, 15)
except AttributeError:
pass
return QImage(), QSize(15, 15)

View File

@ -93,6 +93,7 @@ from . import CuraActions
from cura.Scene import ZOffsetDecorator
from . import CuraSplashScreen
from . import CameraImageProvider
from . import PrintJobPreviewImageProvider
from . import MachineActionManager
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
@ -428,6 +429,7 @@ class CuraApplication(QtApplication):
# Readers & Writers:
"GCodeWriter",
"STLReader",
"3MFWriter",
# Tools:
"CameraTool",
@ -504,6 +506,7 @@ class CuraApplication(QtApplication):
def _onEngineCreated(self):
self._qml_engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
self._qml_engine.addImageProvider("print_job_preview", PrintJobPreviewImageProvider.PrintJobPreviewImageProvider())
@pyqtProperty(bool)
def needToShowUserAgreement(self):

View File

@ -108,6 +108,6 @@ class QualityProfilesDropDownMenuModel(ListModel):
layer_height = container.getProperty("layer_height", "value")
if isinstance(layer_height, SettingFunction):
layer_height = layer_height()
layer_height = layer_height(global_stack)
return float(layer_height)

View File

@ -200,12 +200,19 @@ class QualityManager(QObject):
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
# This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks
has_variant_materials = parseBool(machine.getMetaDataEntry("has_variant_materials", False))
has_machine_specific_qualities = machine.getHasMachineQuality()
# To find the quality container for the GlobalStack, check in the following fall-back manner:
# (1) the machine-specific node
# (2) the generic node
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
# Check if this machine has specific quality profiles for its extruders, if so, when looking up extruder
# qualities, we should not fall back to use the global qualities.
has_extruder_specific_qualities = False
if machine_node:
if machine_node.children_map:
has_extruder_specific_qualities = True
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
nodes_to_check = [machine_node, default_machine_node]
@ -213,12 +220,10 @@ class QualityManager(QObject):
quality_group_dict = {}
for node in nodes_to_check:
if node and node.quality_type_map:
# Only include global qualities
if has_variant_materials:
quality_node = list(node.quality_type_map.values())[0]
is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
if not is_global_quality:
continue
quality_node = list(node.quality_type_map.values())[0]
is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
if not is_global_quality:
continue
for quality_type, quality_node in node.quality_type_map.items():
quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
@ -300,9 +305,9 @@ class QualityManager(QObject):
else:
nodes_to_check += [default_machine_node]
for node in nodes_to_check:
for node_idx, node in enumerate(nodes_to_check):
if node and node.quality_type_map:
if has_variant_materials:
if has_extruder_specific_qualities:
# Only include variant qualities; skip non global qualities
quality_node = list(node.quality_type_map.values())[0]
is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
@ -318,6 +323,12 @@ class QualityManager(QObject):
if position not in quality_group.nodes_for_extruders:
quality_group.nodes_for_extruders[position] = quality_node
# If the machine has its own specific qualities, for extruders, it should skip the global qualities
# and use the material/variant specific qualities.
if has_extruder_specific_qualities:
if node_idx == len(nodes_to_check) - 1:
break
# Update availabilities for each quality group
self._updateQualityGroupsAvailability(machine, quality_group_dict.values())

View File

@ -0,0 +1,27 @@
from PyQt5.QtGui import QImage
from PyQt5.QtQuick import QQuickImageProvider
from PyQt5.QtCore import QSize
from UM.Application import Application
class PrintJobPreviewImageProvider(QQuickImageProvider):
def __init__(self):
super().__init__(QQuickImageProvider.Image)
## Request a new image.
def requestImage(self, id: str, size: QSize) -> QImage:
# The id will have an uuid and an increment separated by a slash. As we don't care about the value of the
# increment, we need to strip that first.
uuid = id[id.find("/") + 1:]
for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
if not hasattr(output_device, "printJobs"):
continue
for print_job in output_device.printJobs:
if print_job.key == uuid:
if print_job.getPreviewImage():
return print_job.getPreviewImage(), QSize(15, 15)
else:
return QImage(), QSize(15, 15)
return QImage(), QSize(15,15)

View File

@ -27,7 +27,13 @@ class ConfigurationModel(QObject):
return self._printer_type
def setExtruderConfigurations(self, extruder_configurations):
self._extruder_configurations = extruder_configurations
if self._extruder_configurations != extruder_configurations:
self._extruder_configurations = extruder_configurations
for extruder_configuration in self._extruder_configurations:
extruder_configuration.extruderConfigurationChanged.connect(self.configurationChanged)
self.configurationChanged.emit()
@pyqtProperty("QVariantList", fset = setExtruderConfigurations, notify = configurationChanged)
def extruderConfigurations(self):

View File

@ -1,56 +1,67 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
class ExtruderConfigurationModel(QObject):
extruderConfigurationChanged = pyqtSignal()
def __init__(self):
def __init__(self, position: int = -1) -> None:
super().__init__()
self._position = -1
self._material = None
self._hotend_id = None
self._position = position # type: int
self._material = None # type: Optional[MaterialOutputModel]
self._hotend_id = None # type: Optional[str]
def setPosition(self, position):
def setPosition(self, position: int) -> None:
self._position = position
@pyqtProperty(int, fset = setPosition, notify = extruderConfigurationChanged)
def position(self):
def position(self) -> int:
return self._position
def setMaterial(self, material):
self._material = material
def setMaterial(self, material: Optional[MaterialOutputModel]) -> None:
if self._hotend_id != material:
self._material = material
self.extruderConfigurationChanged.emit()
@pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged)
def material(self):
def activeMaterial(self) -> Optional[MaterialOutputModel]:
return self._material
def setHotendID(self, hotend_id):
self._hotend_id = hotend_id
@pyqtProperty(QObject, fset=setMaterial, notify=extruderConfigurationChanged)
def material(self) -> Optional[MaterialOutputModel]:
return self._material
def setHotendID(self, hotend_id: Optional[str]) -> None:
if self._hotend_id != hotend_id:
self._hotend_id = hotend_id
self.extruderConfigurationChanged.emit()
@pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged)
def hotendID(self):
def hotendID(self) -> Optional[str]:
return self._hotend_id
## This method is intended to indicate whether the configuration is valid or not.
# The method checks if the mandatory fields are or not set
# At this moment is always valid since we allow to have empty material and variants.
def isValid(self):
def isValid(self) -> bool:
return True
def __str__(self):
def __str__(self) -> str:
message_chunks = []
message_chunks.append("Position: " + str(self._position))
message_chunks.append("-")
message_chunks.append("Material: " + self.material.type if self.material else "empty")
message_chunks.append("Material: " + self.activeMaterial.type if self.activeMaterial else "empty")
message_chunks.append("-")
message_chunks.append("HotendID: " + self.hotendID if self.hotendID else "empty")
return " ".join(message_chunks)
def __eq__(self, other):
def __eq__(self, other) -> bool:
return hash(self) == hash(other)
# Calculating a hash function using the position of the extruder, the material GUID and the hotend id to check if is

View File

@ -12,64 +12,61 @@ if TYPE_CHECKING:
class ExtruderOutputModel(QObject):
hotendIDChanged = pyqtSignal()
targetHotendTemperatureChanged = pyqtSignal()
hotendTemperatureChanged = pyqtSignal()
activeMaterialChanged = pyqtSignal()
extruderConfigurationChanged = pyqtSignal()
isPreheatingChanged = pyqtSignal()
def __init__(self, printer: "PrinterOutputModel", position, parent=None) -> None:
def __init__(self, printer: "PrinterOutputModel", position: int, parent=None) -> None:
super().__init__(parent)
self._printer = printer
self._printer = printer # type: PrinterOutputModel
self._position = position
self._target_hotend_temperature = 0 # type: float
self._hotend_temperature = 0 # type: float
self._hotend_id = ""
self._active_material = None # type: Optional[MaterialOutputModel]
self._extruder_configuration = ExtruderConfigurationModel()
self._extruder_configuration.position = self._position
self._target_hotend_temperature = 0.0 # type: float
self._hotend_temperature = 0.0 # type: float
self._is_preheating = False
def getPrinter(self):
# The extruder output model wraps the configuration model. This way we can use the same config model for jobs
# and extruders alike.
self._extruder_configuration = ExtruderConfigurationModel()
self._extruder_configuration.position = self._position
self._extruder_configuration.extruderConfigurationChanged.connect(self.extruderConfigurationChanged)
def getPrinter(self) -> "PrinterOutputModel":
return self._printer
def getPosition(self):
def getPosition(self) -> int:
return self._position
# Does the printer support pre-heating the bed at all
@pyqtProperty(bool, constant=True)
def canPreHeatHotends(self):
def canPreHeatHotends(self) -> bool:
if self._printer:
return self._printer.canPreHeatHotends
return False
@pyqtProperty(QObject, notify = activeMaterialChanged)
@pyqtProperty(QObject, notify = extruderConfigurationChanged)
def activeMaterial(self) -> Optional["MaterialOutputModel"]:
return self._active_material
return self._extruder_configuration.activeMaterial
def updateActiveMaterial(self, material: Optional["MaterialOutputModel"]):
if self._active_material != material:
self._active_material = material
self._extruder_configuration.material = self._active_material
self.activeMaterialChanged.emit()
self.extruderConfigurationChanged.emit()
def updateActiveMaterial(self, material: Optional["MaterialOutputModel"]) -> None:
self._extruder_configuration.setMaterial(material)
## Update the hotend temperature. This only changes it locally.
def updateHotendTemperature(self, temperature: float):
def updateHotendTemperature(self, temperature: float) -> None:
if self._hotend_temperature != temperature:
self._hotend_temperature = temperature
self.hotendTemperatureChanged.emit()
def updateTargetHotendTemperature(self, temperature: float):
def updateTargetHotendTemperature(self, temperature: float) -> None:
if self._target_hotend_temperature != temperature:
self._target_hotend_temperature = temperature
self.targetHotendTemperatureChanged.emit()
## Set the target hotend temperature. This ensures that it's actually sent to the remote.
@pyqtSlot(float)
def setTargetHotendTemperature(self, temperature: float):
def setTargetHotendTemperature(self, temperature: float) -> None:
self._printer.getController().setTargetHotendTemperature(self._printer, self, temperature)
self.updateTargetHotendTemperature(temperature)
@ -81,30 +78,26 @@ class ExtruderOutputModel(QObject):
def hotendTemperature(self) -> float:
return self._hotend_temperature
@pyqtProperty(str, notify = hotendIDChanged)
@pyqtProperty(str, notify = extruderConfigurationChanged)
def hotendID(self) -> str:
return self._hotend_id
return self._extruder_configuration.hotendID
def updateHotendID(self, id: str):
if self._hotend_id != id:
self._hotend_id = id
self._extruder_configuration.hotendID = self._hotend_id
self.hotendIDChanged.emit()
self.extruderConfigurationChanged.emit()
def updateHotendID(self, hotend_id: str) -> None:
self._extruder_configuration.setHotendID(hotend_id)
@pyqtProperty(QObject, notify = extruderConfigurationChanged)
def extruderConfiguration(self):
def extruderConfiguration(self) -> Optional[ExtruderConfigurationModel]:
if self._extruder_configuration.isValid():
return self._extruder_configuration
return None
def updateIsPreheating(self, pre_heating):
def updateIsPreheating(self, pre_heating: bool) -> None:
if self._is_preheating != pre_heating:
self._is_preheating = pre_heating
self.isPreheatingChanged.emit()
@pyqtProperty(bool, notify=isPreheatingChanged)
def isPreheating(self):
def isPreheating(self) -> bool:
return self._is_preheating
## Pre-heats the extruder before printer.
@ -113,9 +106,9 @@ class ExtruderOutputModel(QObject):
# Celsius.
# \param duration How long the bed should stay warm, in seconds.
@pyqtSlot(float, float)
def preheatHotend(self, temperature, duration):
def preheatHotend(self, temperature: float, duration: float) -> None:
self._printer._controller.preheatHotend(self, temperature, duration)
@pyqtSlot()
def cancelPreheatHotend(self):
self._printer._controller.cancelPreheatHotend(self)
def cancelPreheatHotend(self) -> None:
self._printer._controller.cancelPreheatHotend(self)

View File

@ -188,40 +188,55 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
if reply in self._kept_alive_multiparts:
del self._kept_alive_multiparts[reply]
def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
def _validateManager(self) -> None:
if self._manager is None:
self._createNetworkManager()
assert (self._manager is not None)
def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
self._validateManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.put(request, data.encode())
self._registerOnFinishedCallback(reply, on_finished)
if self._manager is not None:
reply = self._manager.put(request, data.encode())
self._registerOnFinishedCallback(reply, on_finished)
else:
Logger.log("e", "Could not find manager.")
def delete(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
self._validateManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
if self._manager is not None:
reply = self._manager.deleteResource(request)
self._registerOnFinishedCallback(reply, on_finished)
else:
Logger.log("e", "Could not find manager.")
def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if self._manager is None:
self._createNetworkManager()
assert (self._manager is not None)
self._validateManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.get(request)
self._registerOnFinishedCallback(reply, on_finished)
if self._manager is not None:
reply = self._manager.get(request)
self._registerOnFinishedCallback(reply, on_finished)
else:
Logger.log("e", "Could not find manager.")
def post(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
if self._manager is None:
self._createNetworkManager()
assert (self._manager is not None)
self._validateManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.post(request, data)
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
if self._manager is not None:
reply = self._manager.post(request, data)
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
else:
Logger.log("e", "Could not find manager.")
def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> QNetworkReply:
if self._manager is None:
self._createNetworkManager()
assert (self._manager is not None)
self._validateManager()
request = self._createEmptyRequest(target, content_type=None)
multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
for part in parts:
@ -229,15 +244,18 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._last_request_time = time()
reply = self._manager.post(request, multi_post_part)
if self._manager is not None:
reply = self._manager.post(request, multi_post_part)
self._kept_alive_multiparts[reply] = multi_post_part
self._kept_alive_multiparts[reply] = multi_post_part
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
return reply
return reply
else:
Logger.log("e", "Could not find manager.")
def postForm(self, target: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
post_part = QHttpPart()

View File

@ -2,11 +2,15 @@
# 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
from typing import Optional, TYPE_CHECKING, List
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QImage
if TYPE_CHECKING:
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
class PrintJobOutputModel(QObject):
@ -17,6 +21,9 @@ class PrintJobOutputModel(QObject):
keyChanged = pyqtSignal()
assignedPrinterChanged = pyqtSignal()
ownerChanged = pyqtSignal()
configurationChanged = pyqtSignal()
previewImageChanged = pyqtSignal()
compatibleMachineFamiliesChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None) -> None:
super().__init__(parent)
@ -29,6 +36,48 @@ class PrintJobOutputModel(QObject):
self._assigned_printer = None # type: Optional[PrinterOutputModel]
self._owner = "" # Who started/owns the print job?
self._configuration = None # type: Optional[ConfigurationModel]
self._compatible_machine_families = [] # type: List[str]
self._preview_image_id = 0
self._preview_image = None # type: Optional[QImage]
@pyqtProperty("QStringList", notify=compatibleMachineFamiliesChanged)
def compatibleMachineFamilies(self):
# Hack; Some versions of cluster will return a family more than once...
return set(self._compatible_machine_families)
def setCompatibleMachineFamilies(self, compatible_machine_families: List[str]) -> None:
if self._compatible_machine_families != compatible_machine_families:
self._compatible_machine_families = compatible_machine_families
self.compatibleMachineFamiliesChanged.emit()
@pyqtProperty(QUrl, notify=previewImageChanged)
def previewImageUrl(self):
self._preview_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://print_job_preview/" + str(self._preview_image_id) + "/" + self._key
return QUrl(temp, QUrl.TolerantMode)
def getPreviewImage(self) -> Optional[QImage]:
return self._preview_image
def updatePreviewImage(self, preview_image: Optional[QImage]) -> None:
if self._preview_image != preview_image:
self._preview_image = preview_image
self.previewImageChanged.emit()
@pyqtProperty(QObject, notify=configurationChanged)
def configuration(self) -> Optional["ConfigurationModel"]:
return self._configuration
def updateConfiguration(self, configuration: Optional["ConfigurationModel"]) -> None:
if self._configuration != configuration:
self._configuration = configuration
self.configurationChanged.emit()
@pyqtProperty(str, notify=ownerChanged)
def owner(self):
return self._owner

View File

@ -35,7 +35,7 @@ class PrinterOutputModel(QObject):
self._key = "" # Unique identifier
self._controller = output_controller
self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
self._printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
self._printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
self._head_position = Vector(0, 0, 0)
self._active_print_job = None # type: Optional[PrintJobOutputModel]
self._firmware_version = firmware_version
@ -43,9 +43,9 @@ class PrinterOutputModel(QObject):
self._is_preheating = False
self._printer_type = ""
self._buildplate_name = None
# Update the printer configuration every time any of the extruders changes its configuration
for extruder in self._extruders:
extruder.extruderConfigurationChanged.connect(self._updateExtruderConfiguration)
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
self._extruders]
self._camera = None
@ -282,8 +282,4 @@ class PrinterOutputModel(QObject):
def printerConfiguration(self):
if self._printer_configuration.isValid():
return self._printer_configuration
return None
def _updateExtruderConfiguration(self):
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in self._extruders]
self.configurationChanged.emit()
return None

View File

@ -304,7 +304,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._global_stack:
self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged)
self._global_stack.containersChanged.disconnect(self._onChanged)
extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_stack.getId())
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
for extruder in extruders:
extruder.propertyChanged.disconnect(self._onSettingValueChanged)
@ -314,7 +314,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
self._global_stack.propertyChanged.connect(self._onSettingValueChanged)
self._global_stack.containersChanged.connect(self._onChanged)
extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_stack.getId())
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingValueChanged)

View File

@ -4,7 +4,7 @@
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt.
from UM.FlameProfiler import pyqtSlot
import cura.CuraApplication #To get the global container stack to find the current machine.
import cura.CuraApplication # To get the global container stack to find the current machine.
from UM.Logger import Logger
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
@ -12,15 +12,15 @@ from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
from UM.Settings.SettingFunction import SettingFunction
from UM.Settings.SettingInstance import SettingInstance
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
from typing import Optional, List, TYPE_CHECKING, Union, Dict
from typing import Optional, TYPE_CHECKING, Dict, List, Any
if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
from UM.Scene.SceneNode import SceneNode
## Manages all existing extruder stacks.
@ -38,9 +38,10 @@ class ExtruderManager(QObject):
self._application = cura.CuraApplication.CuraApplication.getInstance()
self._extruder_trains = {} # Per machine, a dictionary of extruder container stack IDs. Only for separately defined extruders.
# Per machine, a dictionary of extruder container stack IDs. Only for separately defined extruders.
self._extruder_trains = {} # type: Dict[str, Dict[str, ExtruderStack]]
self._active_extruder_index = -1 # Indicates the index of the active extruder stack. -1 means no active extruder stack
self._selected_object_extruders = []
self._selected_object_extruders = [] # type: List[str]
self._addCurrentMachineExtruders()
Selection.selectionChanged.connect(self.resetSelectedObjectExtruders)
@ -68,7 +69,7 @@ class ExtruderManager(QObject):
## Return extruder count according to extruder trains.
@pyqtProperty(int, notify = extrudersChanged)
def extruderCount(self):
def extruderCount(self) -> int:
if not self._application.getGlobalContainerStack():
return 0 # No active machine, so no extruders.
try:
@ -79,28 +80,14 @@ class ExtruderManager(QObject):
## Gets a dict with the extruder stack ids with the extruder number as the key.
@pyqtProperty("QVariantMap", notify = extrudersChanged)
def extruderIds(self) -> Dict[str, str]:
extruder_stack_ids = {}
extruder_stack_ids = {} # type: Dict[str, str]
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
global_stack_id = global_container_stack.getId()
if global_stack_id in self._extruder_trains:
for position in self._extruder_trains[global_stack_id]:
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
extruder_stack_ids = {position: extruder.id for position, extruder in global_container_stack.extruders.items()}
return extruder_stack_ids
@pyqtSlot(str, result = str)
def getQualityChangesIdByExtruderStackId(self, extruder_stack_id: str) -> str:
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack is not None:
for position in self._extruder_trains[global_container_stack.getId()]:
extruder = self._extruder_trains[global_container_stack.getId()][position]
if extruder.getId() == extruder_stack_id:
return extruder.qualityChanges.getId()
return ""
## Changes the active extruder by index.
#
# \param index The index of the new active extruder.
@ -117,9 +104,9 @@ class ExtruderManager(QObject):
#
# \param index The index of the extruder whose name to get.
@pyqtSlot(int, result = str)
def getExtruderName(self, index):
def getExtruderName(self, index: int) -> str:
try:
return list(self.getActiveExtruderStacks())[index].getName()
return self.getActiveExtruderStacks()[index].getName()
except IndexError:
return ""
@ -133,7 +120,7 @@ class ExtruderManager(QObject):
object_extruders = set()
# First, build a list of the actual selected objects (including children of groups, excluding group nodes)
selected_nodes = []
selected_nodes = [] # type: List["SceneNode"]
for node in Selection.getAllSelectedObjects():
if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
@ -145,14 +132,13 @@ class ExtruderManager(QObject):
selected_nodes.append(node)
# Then, figure out which nodes are used by those selected nodes.
global_stack = self._application.getGlobalContainerStack()
current_extruder_trains = self._extruder_trains.get(global_stack.getId())
current_extruder_trains = self.getActiveExtruderStacks()
for node in selected_nodes:
extruder = node.callDecoration("getActiveExtruder")
if extruder:
object_extruders.add(extruder)
elif current_extruder_trains:
object_extruders.add(current_extruder_trains["0"].getId())
object_extruders.add(current_extruder_trains[0].getId())
self._selected_object_extruders = list(object_extruders)
@ -168,14 +154,7 @@ class ExtruderManager(QObject):
@pyqtSlot(result = QObject)
def getActiveExtruderStack(self) -> Optional["ExtruderStack"]:
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
if global_container_stack.getId() in self._extruder_trains:
if str(self._active_extruder_index) in self._extruder_trains[global_container_stack.getId()]:
return self._extruder_trains[global_container_stack.getId()][str(self._active_extruder_index)]
return None
return self.getExtruderStack(self._active_extruder_index)
## Get an extruder stack by index
def getExtruderStack(self, index) -> Optional["ExtruderStack"]:
@ -186,16 +165,7 @@ class ExtruderManager(QObject):
return self._extruder_trains[global_container_stack.getId()][str(index)]
return None
## Get all extruder stacks
def getExtruderStacks(self) -> List["ExtruderStack"]:
result = []
for i in range(self.extruderCount):
stack = self.getExtruderStack(i)
if stack:
result.append(stack)
return result
def registerExtruder(self, extruder_train, machine_id):
def registerExtruder(self, extruder_train: "ExtruderStack", machine_id: str) -> None:
changed = False
if machine_id not in self._extruder_trains:
@ -214,23 +184,20 @@ class ExtruderManager(QObject):
if changed:
self.extrudersChanged.emit(machine_id)
def getAllExtruderValues(self, setting_key):
return self.getAllExtruderSettings(setting_key, "value")
## Gets a property of a setting for all extruders.
#
# \param setting_key \type{str} The setting to get the property of.
# \param property \type{str} The property to get.
# \return \type{List} the list of results
def getAllExtruderSettings(self, setting_key: str, prop: str):
def getAllExtruderSettings(self, setting_key: str, prop: str) -> List:
result = []
for index in self.extruderIds:
extruder_stack_id = self.extruderIds[str(index)]
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack_id)[0]
for extruder_stack in self.getActiveExtruderStacks():
result.append(extruder_stack.getProperty(setting_key, prop))
return result
def extruderValueWithDefault(self, value):
def extruderValueWithDefault(self, value: str) -> str:
machine_manager = self._application.getMachineManager()
if value == "-1":
return machine_manager.defaultExtruderPosition
@ -321,7 +288,7 @@ class ExtruderManager(QObject):
## Removes the container stack and user profile for the extruders for a specific machine.
#
# \param machine_id The machine to remove the extruders for.
def removeMachineExtruders(self, machine_id: str):
def removeMachineExtruders(self, machine_id: str) -> None:
for extruder in self.getMachineExtruders(machine_id):
ContainerRegistry.getInstance().removeContainer(extruder.userChanges.getId())
ContainerRegistry.getInstance().removeContainer(extruder.getId())
@ -331,24 +298,11 @@ class ExtruderManager(QObject):
## Returns extruders for a specific machine.
#
# \param machine_id The machine to get the extruders of.
def getMachineExtruders(self, machine_id: str):
def getMachineExtruders(self, machine_id: str) -> List["ExtruderStack"]:
if machine_id not in self._extruder_trains:
return []
return [self._extruder_trains[machine_id][name] for name in self._extruder_trains[machine_id]]
## Returns a list containing the global stack and active extruder stacks.
#
# The first element is the global container stack, followed by any extruder stacks.
# \return \type{List[ContainerStack]}
def getActiveGlobalAndExtruderStacks(self) -> Optional[List[Union["ExtruderStack", "GlobalStack"]]]:
global_stack = self._application.getGlobalContainerStack()
if not global_stack:
return None
result = [global_stack]
result.extend(self.getActiveExtruderStacks())
return result
## Returns the list of active extruder stacks, taking into account the machine extruder count.
#
# \return \type{List[ContainerStack]} a list of
@ -357,14 +311,11 @@ class ExtruderManager(QObject):
if not global_stack:
return []
result = []
if global_stack.getId() in self._extruder_trains:
for extruder in sorted(self._extruder_trains[global_stack.getId()]):
result.append(self._extruder_trains[global_stack.getId()][extruder])
result_tuple_list = sorted(list(global_stack.extruders.items()), key = lambda x: int(x[0]))
result_list = [item[1] for item in result_tuple_list]
machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value")
return result[:machine_extruder_count]
return result_list[:machine_extruder_count]
def _globalContainerStackChanged(self) -> None:
# If the global container changed, the machine changed and might have extruders that were not registered yet
@ -406,7 +357,7 @@ class ExtruderManager(QObject):
# After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing
# "fdmextruder". We need to check a machine here so its extruder definition is correct according to this.
def _fixSingleExtrusionMachineExtruderDefinition(self, global_stack):
def _fixSingleExtrusionMachineExtruderDefinition(self, global_stack: "GlobalStack") -> None:
expected_extruder_definition_0_id = global_stack.getMetaDataEntry("machine_extruder_trains")["0"]
extruder_stack_0 = global_stack.extruders["0"]
if extruder_stack_0.definition.getId() != expected_extruder_definition_0_id:
@ -425,11 +376,11 @@ class ExtruderManager(QObject):
# \return A list of values for all extruders. If an extruder does not have a value, it will not be in the list.
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getExtruderValues(key):
def getExtruderValues(key: str) -> List[Any]:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
for extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
if not extruder.isEnabled:
continue
# only include values from extruders that are "active" for the current machine instance
@ -460,7 +411,7 @@ class ExtruderManager(QObject):
# \return A list of values for all extruders. If an extruder does not have a value, it will not be in the list.
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getDefaultExtruderValues(key):
def getDefaultExtruderValues(key: str) -> List[Any]:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
@ -471,7 +422,7 @@ class ExtruderManager(QObject):
}
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
for extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
# only include values from extruders that are "active" for the current machine instance
if int(extruder.getMetaDataEntry("position")) >= global_stack.getProperty("machine_extruder_count", "value", context = context):
continue
@ -504,7 +455,7 @@ class ExtruderManager(QObject):
#
# \return String representing the extruder values
@pyqtSlot(str, result="QVariant")
def getInstanceExtruderValues(self, key):
def getInstanceExtruderValues(self, key) -> List:
return ExtruderManager.getExtruderValues(key)
## Get the value for a setting from a specific extruder.
@ -517,7 +468,7 @@ class ExtruderManager(QObject):
# \return The value of the setting for the specified extruder or for the
# global stack if not found.
@staticmethod
def getExtruderValue(extruder_index, key):
def getExtruderValue(extruder_index: int, key: str) -> Any:
if extruder_index == -1:
extruder_index = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
@ -542,7 +493,7 @@ class ExtruderManager(QObject):
# \return The value of the setting for the specified extruder or for the
# global stack if not found.
@staticmethod
def getDefaultExtruderValue(extruder_index, key):
def getDefaultExtruderValue(extruder_index: int, key: str) -> Any:
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
context = PropertyEvaluationContext(extruder)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
@ -569,7 +520,7 @@ class ExtruderManager(QObject):
#
# \return The effective value
@staticmethod
def getResolveOrValue(key):
def getResolveOrValue(key: str) -> Any:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
resolved_value = global_stack.getProperty(key, "value")
@ -583,7 +534,7 @@ class ExtruderManager(QObject):
#
# \return The effective value
@staticmethod
def getDefaultResolveOrValue(key):
def getDefaultResolveOrValue(key: str) -> Any:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container

View File

@ -134,7 +134,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
# Link to new extruders
self._active_machine_extruders = []
extruder_manager = Application.getInstance().getExtruderManager()
for extruder in extruder_manager.getExtruderStacks():
for extruder in extruder_manager.getActiveExtruderStacks():
if extruder is None: #This extruder wasn't loaded yet. This happens asynchronously while this model is constructed from QML.
continue
extruder.containersChanged.connect(self._onExtruderStackContainersChanged)
@ -171,7 +171,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
# get machine extruder count for verification
machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value")
for extruder in Application.getInstance().getExtruderManager().getMachineExtruders(global_container_stack.getId()):
for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks():
position = extruder.getMetaDataEntry("position", default = "0") # Get the position
try:
position = int(position)

View File

@ -13,6 +13,8 @@ from UM.Settings.SettingInstance import InstanceState
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import PropertyEvaluationContext
from UM.Logger import Logger
from UM.Util import parseBool
import cura.CuraApplication
from . import Exceptions
@ -188,6 +190,15 @@ class GlobalStack(CuraContainerStack):
def getHeadAndFansCoordinates(self):
return self.getProperty("machine_head_with_fans_polygon", "value")
def getHasMaterials(self) -> bool:
return parseBool(self.getMetaDataEntry("has_materials", False))
def getHasVariants(self) -> bool:
return parseBool(self.getMetaDataEntry("has_variants", False))
def getHasMachineQuality(self) -> bool:
return parseBool(self.getMetaDataEntry("has_machine_quality", False))
## private:
global_stack_mime = MimeType(

View File

@ -385,7 +385,9 @@ class MachineManager(QObject):
# \param definition_id \type{str} definition id that needs to look for
# \param metadata_filter \type{dict} list of metadata keys and values used for filtering
@staticmethod
def getMachine(definition_id: str, metadata_filter: Dict[str, str] = None) -> Optional["GlobalStack"]:
def getMachine(definition_id: str, metadata_filter: Optional[Dict[str, str]] = None) -> Optional["GlobalStack"]:
if metadata_filter is None:
metadata_filter = {}
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for machine in machines:
if machine.definition.getId() == definition_id:
@ -412,7 +414,7 @@ class MachineManager(QObject):
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
count = 1 # we start with the global stack
for stack in extruder_stacks:
md = stack.getMetaData()
@ -435,7 +437,7 @@ class MachineManager(QObject):
if self._global_container_stack.getTop().findInstances():
return True
stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
for stack in stacks:
if stack.getTop().findInstances():
return True
@ -448,7 +450,7 @@ class MachineManager(QObject):
return 0
num_user_settings = 0
num_user_settings += len(self._global_container_stack.getTop().findInstances())
stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
for stack in stacks:
num_user_settings += len(stack.getTop().findInstances())
return num_user_settings
@ -473,7 +475,7 @@ class MachineManager(QObject):
stack = ExtruderManager.getInstance().getActiveExtruderStack()
stacks = [stack]
else:
stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
for stack in stacks:
if stack is not None:
@ -638,7 +640,7 @@ class MachineManager(QObject):
if self._active_container_stack is None or self._global_container_stack is None:
return
new_value = self._active_container_stack.getProperty(key, "value")
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getActiveExtruderStacks()]
# check in which stack the value has to be replaced
for extruder_stack in extruder_stacks:

View File

@ -43,7 +43,9 @@ class UserChangesModel(ListModel):
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
stacks = [global_stack]
stacks.extend(global_stack.extruders.values())
# Check if the definition container has a translation file and ensure it's loaded.
definition = global_stack.getBottom()

View File

@ -225,7 +225,7 @@ class ThreeMFReader(MeshReader):
except Exception:
Logger.logException("e", "An exception occurred in 3mf reader.")
return []
return None
return result

View File

@ -343,7 +343,7 @@ class CuraEngineBackend(QObject, Backend):
if not self._global_container_stack:
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
return
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
error_keys = [] #type: List[str]
for extruder in extruders:
error_keys.extend(extruder.getErrorKeys())

View File

@ -178,7 +178,7 @@ class ProcessSlicedLayersJob(Job):
# Find out colors per extruder
global_container_stack = Application.getInstance().getGlobalContainerStack()
manager = ExtruderManager.getInstance()
extruders = list(manager.getMachineExtruders(global_container_stack.getId()))
extruders = manager.getActiveExtruderStacks()
if extruders:
material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
for extruder in extruders:

View File

@ -333,7 +333,7 @@ class StartSliceJob(Job):
"-1": self._buildReplacementTokens(global_stack)
}
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)

View File

@ -275,7 +275,7 @@ class FlavorParser:
## For showing correct x, y offsets for each extruder
def _extruderOffsets(self) -> Dict[int, List[float]]:
result = {}
for extruder in ExtruderManager.getInstance().getExtruderStacks():
for extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
result[int(extruder.getMetaData().get("position", "0"))] = [
extruder.getProperty("machine_nozzle_offset_x", "value"),
extruder.getProperty("machine_nozzle_offset_y", "value")]

View File

@ -16,6 +16,8 @@ Cura.MachineAction
property var extrudersModel: Cura.ExtrudersModel{}
property int extruderTabsCount: 0
property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : ""
Connections
{
target: base.extrudersModel
@ -511,7 +513,7 @@ Cura.MachineAction
}
return "";
}
return Cura.MachineManager.activeMachineId;
return base.activeMachineId
}
key: settingKey
watchedProperties: [ "value", "description" ]
@ -564,7 +566,7 @@ Cura.MachineAction
}
return "";
}
return Cura.MachineManager.activeMachineId;
return base.activeMachineId
}
key: settingKey
watchedProperties: [ "value", "description" ]
@ -655,7 +657,7 @@ Cura.MachineAction
}
return "";
}
return Cura.MachineManager.activeMachineId;
return base.activeMachineId
}
key: settingKey
watchedProperties: [ "value", "options", "description" ]
@ -754,7 +756,7 @@ Cura.MachineAction
}
return "";
}
return Cura.MachineManager.activeMachineId;
return base.activeMachineId
}
key: settingKey
watchedProperties: [ "value", "description" ]
@ -879,7 +881,7 @@ Cura.MachineAction
{
id: machineExtruderCountProvider
containerStackId: Cura.MachineManager.activeMachineId
containerStackId: base.activeMachineId
key: "machine_extruder_count"
watchedProperties: [ "value", "description" ]
storeIndex: manager.containerIndex
@ -889,7 +891,7 @@ Cura.MachineAction
{
id: machineHeadPolygonProvider
containerStackId: Cura.MachineManager.activeMachineId
containerStackId: base.activeMachineId
key: "machine_head_with_fans_polygon"
watchedProperties: [ "value" ]
storeIndex: manager.containerIndex

View File

@ -17,7 +17,6 @@ Item {
width: childrenRect.width;
height: childrenRect.height;
property var all_categories_except_support: [ "machine_settings", "resolution", "shell", "infill", "material", "speed",
"travel", "cooling", "platform_adhesion", "dual", "meshfix", "blackmagic", "experimental"]
@ -45,7 +44,7 @@ Item {
UM.SettingPropertyProvider
{
id: meshTypePropertyProvider
containerStackId: Cura.MachineManager.activeMachineId
containerStack: Cura.MachineManager.activeMachine
watchedProperties: [ "enabled" ]
}
@ -518,7 +517,7 @@ Item {
{
id: machineExtruderCount
containerStackId: Cura.MachineManager.activeMachineId
containerStack: Cura.MachineManager.activeMachine
key: "machine_extruder_count"
watchedProperties: [ "value" ]
storeIndex: 0
@ -528,7 +527,7 @@ Item {
{
id: printSequencePropertyProvider
containerStackId: Cura.MachineManager.activeMachineId
containerStack: Cura.MachineManager.activeMachine
key: "print_sequence"
watchedProperties: [ "value" ]
storeIndex: 0

View File

@ -384,7 +384,7 @@ UM.Dialog
UM.SettingPropertyProvider
{
id: inheritStackProvider
containerStackId: Cura.MachineManager.activeMachineId
containerStack: Cura.MachineManager.activeMachine
key: model.key ? model.key : "None"
watchedProperties: [ "limit_to_extruder" ]
}

View File

@ -44,12 +44,11 @@ UM.PointingRectangle {
id: valueLabel
anchors {
left: parent.left
leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
width: maximumValue.toString().length * 12 * screenScaleFactor
width: (maximumValue.toString().length + 1) * 10 * screenScaleFactor
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
horizontalAlignment: TextInput.AlignRight

View File

@ -667,8 +667,12 @@ Item
{
target: UM.SimulationView
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
onCurrentLayerChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onCurrentLayerChanged:
{
playButton.pauseSimulation()
pathSlider.setHandleValue(0) // After updating the layer set Path slider to 0
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
// make sure the slider handlers show the correct value after switching views
@ -723,6 +727,7 @@ Item
UM.SimulationView.setSimulationRunning(true)
iconSource = "./resources/simulation_pause.svg"
simulationTimer.start()
status = 1
}
}
@ -766,6 +771,7 @@ Item
{
UM.SimulationView.setCurrentLayer(currentLayer+1)
UM.SimulationView.setCurrentPath(0)
playButton.resumeSimulation()
}
}
else

View File

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM

View File

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM

View File

@ -32,7 +32,7 @@ Item
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
fillMode: Image.PreserveAspectFit
source: details.icon_url || "../images/logobot.svg"
source: details === null ? "" : (details.icon_url || "../images/logobot.svg")
mipmap: true
anchors
{
@ -55,7 +55,7 @@ Item
rightMargin: UM.Theme.getSize("wide_margin").width
bottomMargin: UM.Theme.getSize("default_margin").height
}
text: details.name || ""
text: details === null ? "" : (details.name || "")
font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap
@ -114,7 +114,7 @@ Item
height: childrenRect.height
Label
{
text: details.version || catalog.i18nc("@label", "Unknown")
text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text")
}
@ -122,6 +122,10 @@ Item
{
text:
{
if (details === null)
{
return ""
}
var date = new Date(details.last_updated)
return date.toLocaleString(UM.Preferences.getValue("general/language"))
}
@ -132,6 +136,10 @@ Item
{
text:
{
if (details === null)
{
return ""
}
if (details.author_email)
{
return "<a href=\"mailto:" + details.author_email+"?Subject=Cura: " + details.name + "\">" + details.author_name + "</a>"
@ -148,7 +156,7 @@ Item
}
Label
{
text: details.download_count || catalog.i18nc("@label", "Unknown")
text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text")
}

View File

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM

View File

@ -9,7 +9,7 @@ import UM 1.1 as UM
Item
{
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
height: childrenRect.height
Layout.alignment: Qt.AlignTop | Qt.AlignLeft

View File

@ -12,7 +12,9 @@ ScrollView
width: parent.width
height: parent.height
style: UM.Theme.styles.scrollview
flickableItem.flickableDirection: Flickable.VerticalFlick
Column
{
width: base.width
@ -30,7 +32,7 @@ ScrollView
id: allPlugins
width: parent.width
heading: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins")
model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
model: toolbox.viewCategory == "material" ? toolbox.materialsAvailableModel : toolbox.pluginsAvailableModel
}
ToolboxDownloadsGrid

View File

@ -9,7 +9,7 @@ import UM 1.1 as UM
Rectangle
{
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
id: tileBase
width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)

View File

@ -34,6 +34,7 @@ Item
}
}
/* // NOTE: Remember to re-enable for v3.6!
ToolboxTabButton
{
text: catalog.i18nc("@title:tab", "Materials")
@ -46,6 +47,7 @@ Item
toolbox.viewPage = "overview"
}
}
*/
}
ToolboxTabButton
{

View File

@ -6,6 +6,7 @@ import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM
ScrollView
@ -16,6 +17,7 @@ ScrollView
height: parent.height
style: UM.Theme.styles.scrollview
flickableItem.flickableDirection: Flickable.VerticalFlick
Column
{
spacing: UM.Theme.getSize("default_margin").height

View File

@ -33,6 +33,9 @@ class AuthorsModel(ListModel):
def _update(self):
items = []
if not self._metadata:
self.setItems([])
return
for author in self._metadata:
items.append({

View File

@ -76,7 +76,6 @@ class PackagesModel(ListModel):
if "author_id" not in package["author"] or "display_name" not in package["author"]:
package["author"]["author_id"] = ""
package["author"]["display_name"] = ""
# raise Exception("Detected a package with malformed author data.")
items.append({
"id": package["package_id"],

View File

@ -6,7 +6,7 @@ import json
import os
import tempfile
import platform
from typing import cast, List
from typing import cast, List, TYPE_CHECKING, Tuple, Optional
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
@ -20,9 +20,13 @@ from UM.Version import Version
import cura
from cura.CuraApplication import CuraApplication
from .AuthorsModel import AuthorsModel
from .PackagesModel import PackagesModel
if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack
i18n_catalog = i18nCatalog("cura")
@ -34,19 +38,19 @@ class Toolbox(QObject, Extension):
def __init__(self, application: CuraApplication) -> None:
super().__init__()
self._application = application #type: CuraApplication
self._application = application # type: CuraApplication
self._sdk_version = None # type: Optional[int]
self._cloud_api_version = None # type: Optional[int]
self._cloud_api_root = None # type: Optional[str]
self._api_url = None # type: Optional[str]
self._sdk_version = None # type: Optional[int]
self._cloud_api_version = None # type: Optional[int]
self._cloud_api_root = None # type: Optional[str]
self._api_url = None # type: Optional[str]
# Network:
self._download_request = None #type: Optional[QNetworkRequest]
self._download_reply = None #type: Optional[QNetworkReply]
self._download_progress = 0 #type: float
self._is_downloading = False #type: bool
self._network_manager = None #type: Optional[QNetworkAccessManager]
self._download_request = None # type: Optional[QNetworkRequest]
self._download_reply = None # type: Optional[QNetworkReply]
self._download_progress = 0 # type: float
self._is_downloading = False # type: bool
self._network_manager = None # type: Optional[QNetworkAccessManager]
self._request_header = [
b"User-Agent",
str.encode(
@ -58,9 +62,9 @@ class Toolbox(QObject, Extension):
)
)
]
self._request_urls = {} # type: Dict[str, QUrl]
self._request_urls = {} # type: Dict[str, QUrl]
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
self._old_plugin_ids = [] # type: List[str]
self._old_plugin_ids = [] # type: List[str]
# Data:
self._metadata = {
@ -73,7 +77,7 @@ class Toolbox(QObject, Extension):
"materials_available": [],
"materials_installed": [],
"materials_generic": []
} # type: Dict[str, List[Any]]
} # type: Dict[str, List[Any]]
# Models:
self._models = {
@ -83,42 +87,40 @@ class Toolbox(QObject, Extension):
"plugins_available": PackagesModel(self),
"plugins_installed": PackagesModel(self),
"materials_showcase": AuthorsModel(self),
"materials_available": PackagesModel(self),
"materials_available": AuthorsModel(self),
"materials_installed": PackagesModel(self),
"materials_generic": PackagesModel(self)
} # type: Dict[str, ListModel]
} # type: Dict[str, ListModel]
# These properties are for keeping track of the UI state:
# ----------------------------------------------------------------------
# View category defines which filter to use, and therefore effectively
# which category is currently being displayed. For example, possible
# values include "plugin" or "material", but also "installed".
self._view_category = "plugin" #type: str
self._view_category = "plugin" # type: str
# View page defines which type of page layout to use. For example,
# possible values include "overview", "detail" or "author".
self._view_page = "loading" #type: str
self._view_page = "loading" # type: str
# Active package refers to which package is currently being downloaded,
# installed, or otherwise modified.
self._active_package = None # type: Optional[Dict[str, Any]]
self._active_package = None # type: Optional[Dict[str, Any]]
self._dialog = None #type: Optional[QObject]
self._confirm_reset_dialog = None #type: Optional[QObject]
self._dialog = None # type: Optional[QObject]
self._confirm_reset_dialog = None # type: Optional[QObject]
self._resetUninstallVariables()
self._restart_required = False #type: bool
self._restart_required = False # type: bool
# variables for the license agreement dialog
self._license_dialog_plugin_name = "" #type: str
self._license_dialog_license_content = "" #type: str
self._license_dialog_plugin_file_location = "" #type: str
self._restart_dialog_message = "" #type: str
self._license_dialog_plugin_name = "" # type: str
self._license_dialog_license_content = "" # type: str
self._license_dialog_plugin_file_location = "" # type: str
self._restart_dialog_message = "" # type: str
self._application.initializationFinished.connect(self._onAppInitialized)
# Signals:
# --------------------------------------------------------------------------
# Downloading changes
@ -137,11 +139,11 @@ class Toolbox(QObject, Extension):
showLicenseDialog = pyqtSignal()
uninstallVariablesChanged = pyqtSignal()
def _resetUninstallVariables(self):
self._package_id_to_uninstall = None
def _resetUninstallVariables(self) -> None:
self._package_id_to_uninstall = None # type: Optional[str]
self._package_name_to_uninstall = ""
self._package_used_materials = []
self._package_used_qualities = []
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
@pyqtSlot(result = str)
def getLicenseDialogPluginName(self) -> str:
@ -229,10 +231,12 @@ class Toolbox(QObject, Extension):
# Make remote requests:
self._makeRequestByType("packages")
self._makeRequestByType("authors")
self._makeRequestByType("plugins_showcase")
self._makeRequestByType("materials_showcase")
self._makeRequestByType("materials_available")
self._makeRequestByType("materials_generic")
# TODO: Uncomment in the future when the tag-filtered api calls work in the cloud server
# self._makeRequestByType("plugins_showcase")
# self._makeRequestByType("plugins_available")
# self._makeRequestByType("materials_showcase")
# self._makeRequestByType("materials_available")
# self._makeRequestByType("materials_generic")
# Gather installed packages:
self._updateInstalledModels()
@ -344,26 +348,26 @@ class Toolbox(QObject, Extension):
self.uninstall(package_id)
@pyqtProperty(str, notify = uninstallVariablesChanged)
def pluginToUninstall(self):
def pluginToUninstall(self) -> str:
return self._package_name_to_uninstall
@pyqtProperty(str, notify = uninstallVariablesChanged)
def uninstallUsedMaterials(self):
def uninstallUsedMaterials(self) -> str:
return "\n".join(["%s (%s)" % (str(global_stack.getName()), material) for global_stack, extruder_nr, material in self._package_used_materials])
@pyqtProperty(str, notify = uninstallVariablesChanged)
def uninstallUsedQualities(self):
def uninstallUsedQualities(self) -> str:
return "\n".join(["%s (%s)" % (str(global_stack.getName()), quality) for global_stack, extruder_nr, quality in self._package_used_qualities])
@pyqtSlot()
def closeConfirmResetDialog(self):
def closeConfirmResetDialog(self) -> None:
if self._confirm_reset_dialog is not None:
self._confirm_reset_dialog.close()
## Uses "uninstall variables" to reset qualities and materials, then uninstall
# It's used as an action on Confirm reset on Uninstall
@pyqtSlot()
def resetMaterialsQualitiesAndUninstall(self):
def resetMaterialsQualitiesAndUninstall(self) -> None:
application = CuraApplication.getInstance()
material_manager = application.getMaterialManager()
quality_manager = application.getQualityManager()
@ -376,9 +380,9 @@ class Toolbox(QObject, Extension):
default_quality_group = quality_manager.getDefaultQualityType(global_stack)
machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
self.uninstall(self._package_id_to_uninstall)
if self._package_id_to_uninstall is not None:
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
self.uninstall(self._package_id_to_uninstall)
self._resetUninstallVariables()
self.closeConfirmResetDialog()
@ -514,12 +518,14 @@ class Toolbox(QObject, Extension):
count += 1
return count
# This slot is only used to get the number of material packages by author, not any other type of packages.
@pyqtSlot(str, result = int)
def getTotalNumberOfPackagesByAuthor(self, author_id: str) -> int:
def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int:
count = 0
for package in self._metadata["materials_available"]:
if package["author"]["author_id"] == author_id:
count += 1
for package in self._metadata["packages"]:
if package["package_type"] == "material":
if package["author"]["author_id"] == author_id:
count += 1
return count
@pyqtSlot(str, result = bool)
@ -606,8 +612,22 @@ class Toolbox(QObject, Extension):
self.resetDownload()
return
# HACK: These request are not handled independently at this moment, but together from the "packages" call
do_not_handle = [
"materials_available",
"materials_showcase",
"materials_generic",
"plugins_available",
"plugins_showcase",
]
if reply.operation() == QNetworkAccessManager.GetOperation:
for type, url in self._request_urls.items():
# HACK: Do nothing because we'll handle these from the "packages" call
if type in do_not_handle:
continue
if reply.url() == url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
try:
@ -623,25 +643,16 @@ class Toolbox(QObject, Extension):
if not self._models[type]:
Logger.log("e", "Could not find the %s model.", type)
break
# HACK: Eventually get rid of the code from here...
if type is "plugins_showcase" or type is "materials_showcase":
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
else:
# ...until here.
# This hack arises for multiple reasons but the main
# one is because there are not separate API calls
# for different kinds of showcases.
self._metadata[type] = json_data["data"]
self._models[type].setMetadata(self._metadata[type])
self._metadata[type] = json_data["data"]
self._models[type].setMetadata(self._metadata[type])
# Do some auto filtering
# TODO: Make multiple API calls in the future to handle this
if type is "packages":
self._models[type].setFilter({"type": "plugin"})
self.buildMaterialsModels()
self.buildPluginsModels()
if type is "authors":
self._models[type].setFilter({"package_types": "material"})
if type is "materials_generic":
@ -680,7 +691,7 @@ class Toolbox(QObject, Extension):
self._temp_plugin_file.close()
self._onDownloadComplete(file_path)
def _onDownloadComplete(self, file_path: str):
def _onDownloadComplete(self, file_path: str) -> None:
Logger.log("i", "Toolbox: Download complete.")
package_info = self._package_manager.getPackageInfo(file_path)
if not package_info:
@ -739,9 +750,7 @@ class Toolbox(QObject, Extension):
def viewPage(self) -> str:
return self._view_page
# Expose Models:
# Exposed Models:
# --------------------------------------------------------------------------
@pyqtProperty(QObject, notify = metadataChanged)
def authorsModel(self) -> AuthorsModel:
@ -755,6 +764,10 @@ class Toolbox(QObject, Extension):
def pluginsShowcaseModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_showcase"])
@pyqtProperty(QObject, notify = metadataChanged)
def pluginsAvailableModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_available"])
@pyqtProperty(QObject, notify = metadataChanged)
def pluginsInstalledModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_installed"])
@ -763,6 +776,10 @@ class Toolbox(QObject, Extension):
def materialsShowcaseModel(self) -> AuthorsModel:
return cast(AuthorsModel, self._models["materials_showcase"])
@pyqtProperty(QObject, notify = metadataChanged)
def materialsAvailableModel(self) -> AuthorsModel:
return cast(AuthorsModel, self._models["materials_available"])
@pyqtProperty(QObject, notify = metadataChanged)
def materialsInstalledModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["materials_installed"])
@ -771,8 +788,6 @@ class Toolbox(QObject, Extension):
def materialsGenericModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["materials_generic"])
# Filter Models:
# --------------------------------------------------------------------------
@pyqtSlot(str, str, str)
@ -798,3 +813,48 @@ class Toolbox(QObject, Extension):
return
self._models[model_type].setFilter({})
self.filterChanged.emit()
# HACK(S):
# --------------------------------------------------------------------------
def buildMaterialsModels(self) -> None:
self._metadata["materials_showcase"] = []
self._metadata["materials_available"] = []
processed_authors = [] # type: List[str]
for item in self._metadata["packages"]:
if item["package_type"] == "material":
author = item["author"]
if author["author_id"] in processed_authors:
continue
# Generic materials to be in the same section
if "generic" in item["tags"]:
self._metadata["materials_generic"].append(item)
else:
if "showcase" in item["tags"]:
self._metadata["materials_showcase"].append(author)
else:
self._metadata["materials_available"].append(author)
processed_authors.append(author["author_id"])
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
self._models["materials_available"].setMetadata(self._metadata["materials_available"])
self._models["materials_generic"].setMetadata(self._metadata["materials_generic"])
def buildPluginsModels(self) -> None:
self._metadata["plugins_showcase"] = []
self._metadata["plugins_available"] = []
for item in self._metadata["packages"]:
if item["package_type"] == "plugin":
if "showcase" in item["tags"]:
self._metadata["plugins_showcase"].append(item)
else:
self._metadata["plugins_available"].append(item)
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
self._models["plugins_available"].setMetadata(self._metadata["plugins_available"])

View File

@ -1,241 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
Component
{
Rectangle
{
id: base
property var manager: Cura.MachineManager.printerOutputDevices[0]
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
visible: manager != null
anchors.fill: parent
color: UM.Theme.getColor("viewport_background")
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Label
{
id: activePrintersLabel
font: UM.Theme.getFont("large")
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right:parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: Cura.MachineManager.printerOutputDevices[0].name
elide: Text.ElideRight
}
Rectangle
{
id: printJobArea
border.width: UM.Theme.getSize("default_lining").width
border.color: lineColor
anchors.top: activePrintersLabel.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin:UM.Theme.getSize("default_margin").width
radius: cornerRadius
height: childrenRect.height
Item
{
id: printJobTitleBar
width: parent.width
height: printJobTitleLabel.height + 2 * UM.Theme.getSize("default_margin").height
Label
{
id: printJobTitleLabel
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@title", "Print jobs")
font: UM.Theme.getFont("default")
opacity: 0.75
}
Rectangle
{
anchors.bottom: parent.bottom
height: UM.Theme.getSize("default_lining").width
color: lineColor
width: parent.width
}
}
Column
{
id: printJobColumn
anchors.top: printJobTitleBar.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
Item
{
width: parent.width
height: childrenRect.height
opacity: 0.65
Label
{
text: catalog.i18nc("@label", "Printing")
font: UM.Theme.getFont("very_small")
}
Label
{
text: manager.activePrintJobs.length
font: UM.Theme.getFont("small")
anchors.right: parent.right
}
}
Item
{
width: parent.width
height: childrenRect.height
opacity: 0.65
Label
{
text: catalog.i18nc("@label", "Queued")
font: UM.Theme.getFont("very_small")
}
Label
{
text: manager.queuedPrintJobs.length
font: UM.Theme.getFont("small")
anchors.right: parent.right
}
}
}
OpenPanelButton
{
anchors.top: printJobColumn.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").height
id: configButton
onClicked: base.manager.openPrintJobControlPanel()
text: catalog.i18nc("@action:button", "View print jobs")
}
Item
{
// spacer
anchors.top: configButton.bottom
width: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("default_margin").height
}
}
Rectangle
{
id: printersArea
border.width: UM.Theme.getSize("default_lining").width
border.color: lineColor
anchors.top: printJobArea.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin:UM.Theme.getSize("default_margin").width
radius: cornerRadius
height: childrenRect.height
Item
{
id: printersTitleBar
width: parent.width
height: printJobTitleLabel.height + 2 * UM.Theme.getSize("default_margin").height
Label
{
id: printersTitleLabel
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@label:title", "Printers")
font: UM.Theme.getFont("default")
opacity: 0.75
}
Rectangle
{
anchors.bottom: parent.bottom
height: UM.Theme.getSize("default_lining").width
color: lineColor
width: parent.width
}
}
Column
{
id: printersColumn
anchors.top: printersTitleBar.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
Repeater
{
model: manager.connectedPrintersTypeCount
Item
{
width: parent.width
height: childrenRect.height
opacity: 0.65
Label
{
text: modelData.machine_type
font: UM.Theme.getFont("very_small")
}
Label
{
text: modelData.count
font: UM.Theme.getFont("small")
anchors.right: parent.right
}
}
}
}
OpenPanelButton
{
anchors.top: printersColumn.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").height
id: printerConfigButton
onClicked: base.manager.openPrinterControlPanel()
text: catalog.i18nc("@action:button", "View printers")
}
Item
{
// spacer
anchors.top: printerConfigButton.bottom
width: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("default_margin").height
}
}
}
}

View File

@ -1,118 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
Component
{
Rectangle
{
id: monitorFrame
width: maximumWidth
height: maximumHeight
color: UM.Theme.getColor("viewport_background")
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Label
{
id: activePrintersLabel
font: UM.Theme.getFont("large")
anchors {
top: parent.top
topMargin: UM.Theme.getSize("default_margin").height * 2 // a bit more spacing to give it some breathing room
horizontalCenter: parent.horizontalCenter
}
text: OutputDevice.printers.length == 0 ? catalog.i18nc("@label: arg 1 is group name", "%1 is not set up to host a group of connected Ultimaker 3 printers").arg(Cura.MachineManager.printerOutputDevices[0].name) : ""
visible: OutputDevice.printers.length == 0
}
Item
{
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(800 * screenScaleFactor, maximumWidth)
height: children.height
visible: OutputDevice.printers.length != 0
Label
{
id: addRemovePrintersLabel
anchors.right: parent.right
text: catalog.i18nc("@label link to connect manager", "Add/Remove printers")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
}
MouseArea
{
anchors.fill: addRemovePrintersLabel
hoverEnabled: true
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
onEntered: addRemovePrintersLabel.font.underline = true
onExited: addRemovePrintersLabel.font.underline = false
}
}
ScrollView
{
id: printerScrollView
anchors.margins: UM.Theme.getSize("default_margin").width
anchors.top: activePrintersLabel.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width // To ensure border can be drawn.
anchors.rightMargin: UM.Theme.getSize("default_lining").width
anchors.right: parent.right
ListView
{
anchors.fill: parent
spacing: -UM.Theme.getSize("default_lining").height
model: OutputDevice.printers
delegate: PrinterInfoBlock
{
printer: modelData
width: Math.min(800 * screenScaleFactor, maximumWidth)
height: 125 * screenScaleFactor
// Add a 1 pix margin, as the border is sometimes cut off otherwise.
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
PrinterVideoStream
{
visible: OutputDevice.activePrinter != null
anchors.fill:parent
}
onVisibleChanged:
{
if (!monitorFrame.visible)
{
// After switching the Tab ensure that active printer is Null, the video stream image
// might be active
OutputDevice.setActivePrinter(null)
}
}
}
}

View File

@ -1,71 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import UM 1.1 as UM
Button {
objectName: "openPanelSaveAreaButton"
id: openPanelSaveAreaButton
UM.I18nCatalog { id: catalog; name: "cura"; }
height: UM.Theme.getSize("save_button_save_to_button").height
tooltip: catalog.i18nc("@info:tooltip", "Opens the print jobs page with your default web browser.")
text: catalog.i18nc("@action:button", "View print jobs")
// FIXME: This button style is copied and duplicated from SaveButton.qml
style: ButtonStyle {
background: Rectangle
{
border.width: UM.Theme.getSize("default_lining").width
border.color:
{
if(!control.enabled)
return UM.Theme.getColor("action_button_disabled_border");
else if(control.pressed)
return UM.Theme.getColor("print_button_ready_pressed_border");
else if(control.hovered)
return UM.Theme.getColor("print_button_ready_hovered_border");
else
return UM.Theme.getColor("print_button_ready_border");
}
color:
{
if(!control.enabled)
return UM.Theme.getColor("action_button_disabled");
else if(control.pressed)
return UM.Theme.getColor("print_button_ready_pressed");
else if(control.hovered)
return UM.Theme.getColor("print_button_ready_hovered");
else
return UM.Theme.getColor("print_button_ready");
}
Behavior on color { ColorAnimation { duration: 50; } }
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2)
Label {
id: actualLabel
anchors.centerIn: parent
color:
{
if(!control.enabled)
return UM.Theme.getColor("action_button_disabled_text");
else if(control.pressed)
return UM.Theme.getColor("print_button_ready_text");
else if(control.hovered)
return UM.Theme.getColor("print_button_ready_text");
else
return UM.Theme.getColor("print_button_ready_text");
}
font: UM.Theme.getFont("action_button")
text: control.text;
}
}
label: Item { }
}
}

View File

@ -1,33 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
Item
{
id: extruderInfo
property var printCoreConfiguration
width: Math.round(parent.width / 2)
height: childrenRect.height
Label
{
id: materialLabel
text: printCoreConfiguration.activeMaterial != null ? printCoreConfiguration.activeMaterial.name : ""
elide: Text.ElideRight
width: parent.width
font: UM.Theme.getFont("very_small")
}
Label
{
id: printCoreLabel
text: printCoreConfiguration.hotendID
anchors.top: materialLabel.bottom
elide: Text.ElideRight
width: parent.width
font: UM.Theme.getFont("very_small")
opacity: 0.5
}
}

View File

@ -1,431 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
Rectangle
{
function strPadLeft(string, pad, length)
{
return (new Array(length + 1).join(pad) + string).slice(-length);
}
function getPrettyTime(time)
{
return OutputDevice.formatDuration(time)
}
function formatPrintJobPercent(printJob)
{
if (printJob == null)
{
return "";
}
if (printJob.timeTotal === 0)
{
return "";
}
return Math.min(100, Math.round(printJob.timeElapsed / printJob.timeTotal * 100)) + "%";
}
function printerStatusText(printer)
{
switch (printer.state)
{
case "pre_print":
return catalog.i18nc("@label:status", "Preparing to print")
case "printing":
return catalog.i18nc("@label:status", "Printing");
case "idle":
return catalog.i18nc("@label:status", "Available");
case "unreachable":
return catalog.i18nc("@label:status", "Lost connection with the printer");
case "maintenance":
return catalog.i18nc("@label:status", "Unavailable");
default:
return catalog.i18nc("@label:status", "Unknown");
}
}
id: printerDelegate
property var printer: null
property var printJob: printer != null ? printer.activePrintJob: null
border.width: UM.Theme.getSize("default_lining").width
border.color: mouse.containsMouse ? emphasisColor : lineColor
z: mouse.containsMouse ? 1 : 0 // Push this item up a bit on mouse over to ensure that the highlighted bottom border is visible.
MouseArea
{
id: mouse
anchors.fill:parent
onClicked: OutputDevice.setActivePrinter(printer)
hoverEnabled: true;
// Only clickable if no printer is selected
enabled: OutputDevice.activePrinter == null && printer.state !== "unreachable"
}
Row
{
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: UM.Theme.getSize("default_margin").width
Rectangle
{
width: Math.round(parent.width / 3)
height: parent.height
Label // Print job name
{
id: jobNameLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: printJob != null ? printJob.name : ""
font: UM.Theme.getFont("default_bold")
elide: Text.ElideRight
}
Label
{
id: jobOwnerLabel
anchors.top: jobNameLabel.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: printJob != null ? printJob.owner : ""
opacity: 0.50
elide: Text.ElideRight
}
Label
{
id: totalTimeLabel
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: printJob != null ? getPrettyTime(printJob.timeTotal) : ""
opacity: 0.65
font: UM.Theme.getFont("default")
elide: Text.ElideRight
}
}
Rectangle
{
width: Math.round(parent.width / 3 * 2)
height: parent.height
Label // Friendly machine name
{
id: printerNameLabel
anchors.top: parent.top
anchors.left: parent.left
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width - showCameraIcon.width)
text: printer.name
font: UM.Theme.getFont("default_bold")
elide: Text.ElideRight
}
Label // Machine variant
{
id: printerTypeLabel
anchors.top: printerNameLabel.bottom
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
text: printer.type
anchors.left: parent.left
elide: Text.ElideRight
font: UM.Theme.getFont("very_small")
opacity: 0.50
}
Rectangle // Camera icon
{
id: showCameraIcon
width: 40 * screenScaleFactor
height: width
radius: width
anchors.right: printProgressArea.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
color: emphasisColor
opacity: printer != null && printer.state === "unreachable" ? 0.3 : 1
Image
{
width: parent.width
height: width
anchors.right: parent.right
anchors.rightMargin: parent.rightMargin
source: "camera-icon.svg"
}
}
Row // PrintCore config
{
id: extruderInfo
anchors.bottom: parent.bottom
width: Math.round(parent.width / 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 - extruderSeperator.width) / 2)
printCoreConfiguration: printer.extruders[0]
}
Rectangle
{
id: extruderSeperator
width: UM.Theme.getSize("default_lining").width
height: parent.height
color: lineColor
}
PrintCoreConfiguration
{
id: rightExtruderInfo
width: Math.round((parent.width - extruderSeperator.width) / 2)
printCoreConfiguration: printer.extruders[1]
}
}
Rectangle // Print progress
{
id: printProgressArea
anchors.right: parent.right
anchors.top: parent.top
height: showExtended ? parent.height: printProgressTitleBar.height
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
border.width: UM.Theme.getSize("default_lining").width
border.color: lineColor
radius: cornerRadius
property var showExtended: {
if(printJob != null)
{
var extendStates = ["sent_to_printer", "wait_for_configuration", "printing", "pre_print", "post_print", "wait_cleanup", "queued"];
return extendStates.indexOf(printJob.state) !== -1;
}
return printer.state == "disabled"
}
Item // Status and Percent
{
id: printProgressTitleBar
property var showPercent: {
return printJob != null && (["printing", "post_print", "pre_print", "sent_to_printer"].indexOf(printJob.state) !== -1);
}
width: parent.width
//TODO: hardcoded value
height: 40 * screenScaleFactor
anchors.left: parent.left
Label
{
id: statusText
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: progressText.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.verticalCenter
text: {
if (printer.state == "disabled")
{
return catalog.i18nc("@label:status", "Disabled");
}
if (printer.state === "unreachable")
{
return printerStatusText(printer);
}
if (printJob != null)
{
switch (printJob.state)
{
case "printing":
case "post_print":
return catalog.i18nc("@label:status", "Printing")
case "wait_for_configuration":
return catalog.i18nc("@label:status", "Reserved")
case "wait_cleanup":
case "wait_user_action":
return catalog.i18nc("@label:status", "Finished")
case "pre_print":
case "sent_to_printer":
return catalog.i18nc("@label", "Preparing to print")
case "queued":
return catalog.i18nc("@label:status", "Action required");
case "pausing":
case "paused":
return catalog.i18nc("@label:status", "Paused");
case "resuming":
return catalog.i18nc("@label:status", "Resuming");
case "aborted":
return catalog.i18nc("@label:status", "Print aborted");
default:
// If print job has unknown status show printer.status
return printerStatusText(printer);
}
}
return printerStatusText(printer);
}
elide: Text.ElideRight
font: UM.Theme.getFont("small")
}
Label
{
id: progressText
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.top: statusText.top
text: formatPrintJobPercent(printJob)
visible: printProgressTitleBar.showPercent
//TODO: Hardcoded value
opacity: 0.65
font: UM.Theme.getFont("very_small")
}
Image
{
width: statusText.height
height: width
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.top: statusText.top
visible: !printProgressTitleBar.showPercent
source: {
if (printer.state == "disabled")
{
return "blocked-icon.svg";
}
if (printer.state === "unreachable")
{
return "";
}
if (printJob != null)
{
if(printJob.state === "queued")
{
return "action-required-icon.svg";
}
else if (printJob.state === "wait_cleanup")
{
return "checkmark-icon.svg";
}
}
return ""; // We're not going to show it, so it will not be resolved as a url.
}
}
Rectangle
{
//TODO: This will become a progress bar in the future
width: parent.width
height: UM.Theme.getSize("default_lining").height
anchors.bottom: parent.bottom
anchors.left: parent.left
visible: printProgressArea.showExtended
color: lineColor
}
}
Column
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.top: printProgressTitleBar.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
visible: printProgressArea.showExtended
Label // Status detail
{
text:
{
if (printer.state == "disabled")
{
return catalog.i18nc("@label", "Not accepting print jobs");
}
if (printer.state === "unreachable")
{
return "";
}
if(printJob != null)
{
switch (printJob.state)
{
case "printing":
case "post_print":
return catalog.i18nc("@label", "Finishes at: ") + OutputDevice.getTimeCompleted(printJob.timeTotal - printJob.timeElapsed)
case "wait_cleanup":
return catalog.i18nc("@label", "Clear build plate")
case "sent_to_printer":
case "pre_print":
return catalog.i18nc("@label", "Preparing to print")
case "wait_for_configuration":
return catalog.i18nc("@label", "Not accepting print jobs")
case "queued":
return catalog.i18nc("@label", "Waiting for configuration change");
default:
return "";
}
}
return "";
}
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
wrapMode: Text.Wrap
font: UM.Theme.getFont("default")
}
Label // Status 2nd row
{
text: {
if(printJob != null)
{
if(printJob.state == "printing" || printJob.state == "post_print")
{
return OutputDevice.getDateCompleted(printJob.timeTotal - printJob.timeElapsed)
}
}
return "";
}
elide: Text.ElideRight
font: UM.Theme.getFont("default")
}
}
}
}
}
}

View File

@ -1,54 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
Rectangle
{
id: base
width: 250 * screenScaleFactor
height: 250 * screenScaleFactor
signal clicked()
MouseArea
{
anchors.fill:parent
onClicked: base.clicked()
}
Rectangle
{
// TODO: Actually add UM icon / picture
width: 100 * screenScaleFactor
height: 100 * screenScaleFactor
border.width: UM.Theme.getSize("default_lining").width
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
Label
{
id: nameLabel
anchors.bottom: ipLabel.top
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: modelData.friendly_name.toString()
font: UM.Theme.getFont("large")
elide: Text.ElideMiddle;
height: UM.Theme.getSize("section").height;
}
Label
{
id: ipLabel
text: modelData.ip_address.toString()
anchors.bottom: parent.bottom
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
font: UM.Theme.getFont("default")
height:10 * screenScaleFactor
anchors.horizontalCenter: parent.horizontalCenter
}
}

View File

@ -1,11 +1,11 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from . import DiscoverUM3Action
from .src import DiscoverUM3Action
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
from . import UM3OutputDevicePlugin
from .src import UM3OutputDevicePlugin
def getMetaData():
return {}

View File

@ -0,0 +1,717 @@
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 2.0 as Controls2
import UM 1.3 as UM
import Cura 1.0 as Cura
Component
{
Rectangle
{
id: base
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
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
{
id: printingLabel
font: UM.Theme.getFont("large")
anchors
{
margins: 2 * UM.Theme.getSize("default_margin").width
leftMargin: 4 * UM.Theme.getSize("default_margin").width
top: parent.top
left: parent.left
right: parent.right
}
text: catalog.i18nc("@label", "Printing")
elide: Text.ElideRight
}
Label
{
id: managePrintersLabel
anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width
anchors.right: printerScrollView.right
anchors.bottom: printingLabel.bottom
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
}
style: UM.Theme.styles.scrollview
ListView
{
anchors
{
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.right
leftMargin: 2 * UM.Theme.getSize("default_margin").width
rightMargin: 2 * UM.Theme.getSize("default_margin").width
}
spacing: UM.Theme.getSize("default_margin").height -10
model: OutputDevice.printers
delegate: Item
{
width: parent.width
height: base.height + 2 * base.shadowRadius // To ensure that the shadow doesn't get cut off.
Rectangle
{
width: parent.width - 2 * shadowRadius
height: childrenRect.height + UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
id: base
property var shadowRadius: 5
property var collapsed: true
layer.enabled: true
layer.effect: DropShadow
{
radius: base.shadowRadius
verticalOffset: 2
color: "#3F000000" // 25% shadow
}
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: base.collapsed = !base.collapsed
}
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("setting_control_disabled")
}
if(modelData.activePrintJob != undefined)
{
return UM.Theme.getColor("primary")
}
return UM.Theme.getColor("setting_control_disabled")
}
}
}
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")
opacity: 0.6
}
}
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: UM.Theme.getColor("viewport_background")
height: 2
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: 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()
{
if (popup.visible)
{
popup.close()
}
else
{
popup.open()
}
}
Controls2.Button
{
id: contextButton
text: "\u22EE" //Unicode; Three stacked points.
font.pixelSize: 25
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")
}
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: Controls2.Popup.CloseOnPressOutsideParent
x: parent.width - width
y: contextButton.height
width: 160
height: contentItem.height + 2 * padding
visible: false
transformOrigin: Controls2.Popup.Top
contentItem: Item
{
width: popup.width - 2 * popup.padding
height: childrenRect.height + 15
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
anchors.top: parent.top
anchors.topMargin: 10
hoverEnabled: true
background: Rectangle
{
opacity: pauseButton.down || pauseButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
}
Controls2.Button
{
id: abortButton
text: catalog.i18nc("@label", "Abort")
onClicked:
{
modelData.activePrintJob.setState("abort")
popup.close()
}
width: parent.width
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")
}
}
}
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
height: parent.height -10
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
id: point
height: 13
width: 13
color: UM.Theme.getColor("setting_control")
transform: Rotation { angle: 45}
anchors.right: bloop.right
y: 1
}
Rectangle
{
id: bloop
color: UM.Theme.getColor("setting_control")
width: parent.width
anchors.top: parent.top
anchors.topMargin: 10
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
}
}
}
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"
}
Rectangle
{
id: showCameraIcon
width: 35 * screenScaleFactor
height: width
radius: 0.5 * width
anchors.left: parent.left
anchors.bottom: printJobPreview.bottom
color: UM.Theme.getColor("setting_control_border_highlight")
Image
{
width: parent.width
height: width
anchors.right: parent.right
anchors.rightMargin: parent.rightMargin
source: "../svg/camera-icon.svg"
}
MouseArea
{
anchors.fill:parent
onClicked:
{
OutputDevice.setActiveCamera(modelData.camera)
}
}
}
}
}
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 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":
case "wait_user_action":
return catalog.i18nc("@label:status", "Aborted")
case "pausing":
return catalog.i18nc("@label:status", "Pausing")
case "paused":
return catalog.i18nc("@label:status", "Paused")
case "resuming":
return catalog.i18nc("@label:status", "Resuming")
case "queued":
return catalog.i18nc("@label:status", "Action required")
default:
OutputDevice.formatDuration(modelData.activePrintJob.timeTotal - modelData.activePrintJob.timeElapsed)
}
}
background: Rectangle
{
implicitWidth: 100
implicitHeight: visible ? 24 : 0
color: UM.Theme.getColor("viewport_background")
}
progress: Rectangle
{
color: UM.Theme.getColor("primary")
id: progressItem
function getTextOffset()
{
if(progressItem.width + progressLabel.width < control.width)
{
return progressItem.width + UM.Theme.getSize("default_margin").width
}
else
{
return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width
}
}
Label
{
id: progressLabel
anchors.left: parent.left
anchors.leftMargin: getTextOffset()
text: progressText
anchors.verticalCenter: parent.verticalCenter
color: progressItem.width + progressLabel.width < control.width ? "black" : "white"
width: contentWidth
font: UM.Theme.getFont("default")
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,108 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
Component
{
Rectangle
{
id: monitorFrame
width: maximumWidth
height: maximumHeight
color: UM.Theme.getColor("viewport_background")
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Label
{
id: manageQueueLabel
anchors.rightMargin: 4 * 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
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)
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
delegate: PrintJobInfoBlock
{
printJob: modelData
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").height
height: 175 * screenScaleFactor
}
}
}
PrinterVideoStream
{
visible: OutputDevice.activeCamera != null
anchors.fill: parent
camera: OutputDevice.activeCamera
}
onVisibleChanged:
{
if (monitorFrame != null && !monitorFrame.visible)
{
OutputDevice.setActiveCamera(null)
}
}
}
}

View File

@ -0,0 +1,93 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
Item
{
id: extruderInfo
property var printCoreConfiguration
width: Math.round(parent.width / 2)
height: childrenRect.height
Item
{
id: extruderCircle
width: 30
height: 30
anchors.verticalCenter: printAndMaterialLabel.verticalCenter
opacity:
{
if(printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null)
{
return 0.5
}
return 1
}
Rectangle
{
anchors.fill: parent
radius: Math.round(width / 2)
border.width: 2
border.color: "black"
}
Label
{
anchors.centerIn: parent
font: UM.Theme.getFont("default_bold")
text: printCoreConfiguration.position + 1
}
}
Item
{
id: printAndMaterialLabel
anchors
{
right: parent.right
left: extruderCircle.right
margins: UM.Theme.getSize("default_margin").width
}
height: childrenRect.height
Label
{
id: materialLabel
text:
{
if(printCoreConfiguration != undefined && printCoreConfiguration.activeMaterial != undefined)
{
return printCoreConfiguration.activeMaterial.name
}
return ""
}
font: UM.Theme.getFont("default_bold")
elide: Text.ElideRight
width: parent.width
}
Label
{
id: printCoreLabel
text:
{
if(printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined)
{
return printCoreConfiguration.hotendID
}
return ""
}
anchors.top: materialLabel.bottom
elide: Text.ElideRight
width: parent.width
opacity: 0.6
font: UM.Theme.getFont("default")
}
}
}

View File

@ -0,0 +1,378 @@
import QtQuick 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import UM 1.3 as UM
Item
{
id: base
property var printJob: null
property var shadowRadius: 5
function getPrettyTime(time)
{
return OutputDevice.formatDuration(time)
}
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Rectangle
{
id: background
anchors
{
top: parent.top
topMargin: 3
left: parent.left
leftMargin: base.shadowRadius
rightMargin: base.shadowRadius
right: parent.right
bottom: parent.bottom
bottomMargin: base.shadowRadius
}
layer.enabled: true
layer.effect: DropShadow
{
radius: base.shadowRadius
verticalOffset: 2
color: "#3F000000" // 25% shadow
}
Item
{
// Content on the left of the infobox
anchors
{
top: parent.top
bottom: parent.bottom
left: parent.left
right: parent.horizontalCenter
margins: 2 * UM.Theme.getSize("default_margin").width
rightMargin: UM.Theme.getSize("default_margin").width
}
Label
{
id: printJobName
text: printJob.name
font: UM.Theme.getFont("default_bold")
width: parent.width
elide: Text.ElideRight
}
Label
{
id: ownerName
anchors.top: printJobName.bottom
text: printJob.owner
font: UM.Theme.getFont("default")
opacity: 0.6
width: parent.width
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
opacity: 0.6
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
}
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
}
}
anchors
{
left: parent.left
right: contextButton.left
rightMargin: UM.Theme.getSize("default_margin").width
}
}
function switchPopupState()
{
popup.visible ? popup.close() : popup.open()
}
Button
{
id: contextButton
text: "\u22EE" //Unicode; Three stacked points.
font.pixelSize: 25
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")
}
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.CloseOnPressOutsideParent
x: parent.width - width
y: contextButton.height
width: 160
height: contentItem.height + 2 * padding
visible: false
transformOrigin: Popup.Top
contentItem: Item
{
width: popup.width - 2 * popup.padding
height: childrenRect.height + 15
Button
{
id: sendToTopButton
text: catalog.i18nc("@label", "Move to top")
onClicked:
{
OutputDevice.sendJobToTop(printJob.key)
popup.close()
}
width: parent.width
enabled: OutputDevice.queuedPrintJobs[0].key != printJob.key
anchors.top: parent.top
anchors.topMargin: 10
hoverEnabled: true
background: Rectangle
{
opacity: sendToTopButton.down || sendToTopButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
}
Button
{
id: deleteButton
text: catalog.i18nc("@label", "Delete")
onClicked:
{
OutputDevice.deleteJobFromQueue(printJob.key)
popup.close()
}
width: parent.width
anchors.top: sendToTopButton.bottom
hoverEnabled: true
background: Rectangle
{
opacity: deleteButton.down || deleteButton.hovered ? 1 : 0
color: UM.Theme.getColor("viewport_background")
}
}
}
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
height: parent.height -10
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
id: point
height: 13
width: 13
color: UM.Theme.getColor("setting_control")
transform: Rotation { angle: 45}
anchors.right: bloop.right
y: 1
}
Rectangle
{
id: bloop
color: UM.Theme.getColor("setting_control")
width: parent.width
anchors.top: parent.top
anchors.topMargin: 10
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
}
}
}
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
}
Row
{
id: printerFamilyPills
spacing: 0.5 * UM.Theme.getSize("default_margin").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
{
text: modelData
color: UM.Theme.getColor("viewport_background")
padding: 3
}
}
}
// PrintCore && Material config
Row
{
id: extrudersInfo
anchors.bottom: parent.bottom
anchors
{
left: parent.left
right: parent.right
}
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
PrintCoreConfiguration
{
id: leftExtruderInfo
width: Math.round(parent.width / 2)
printCoreConfiguration: printJob.configuration.extruderConfigurations[0]
}
PrintCoreConfiguration
{
id: rightExtruderInfo
width: Math.round(parent.width / 2)
printCoreConfiguration: printJob.configuration.extruderConfigurations[1]
}
}
}
Rectangle
{
color: UM.Theme.getColor("viewport_background")
width: 2
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
}
}
}

View File

@ -0,0 +1,28 @@
import QtQuick 2.2
import QtQuick.Controls 1.4
import UM 1.2 as UM
Item
{
property alias color: background.color
property alias text: familyNameLabel.text
property var padding: 0
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
{
id: background
height: parent.height
width: parent.width
color: parent.color
anchors.right: parent.right
anchors.horizontalCenter: parent.horizontalCenter
radius: 0.5 * height
}
Label
{
id: familyNameLabel
anchors.centerIn: parent
text: ""
}
}

View File

@ -7,6 +7,8 @@ import UM 1.3 as UM
Item
{
property var camera: null
Rectangle
{
anchors.fill:parent
@ -17,7 +19,7 @@ Item
MouseArea
{
anchors.fill: parent
onClicked: OutputDevice.setActivePrinter(null)
onClicked: OutputDevice.setActiveCamera(null)
z: 0
}
@ -32,7 +34,7 @@ Item
width: 20 * screenScaleFactor
height: 20 * screenScaleFactor
onClicked: OutputDevice.setActivePrinter(null)
onClicked: OutputDevice.setActiveCamera(null)
style: ButtonStyle
{
@ -65,23 +67,24 @@ Item
{
if(visible)
{
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
if(camera != null)
{
OutputDevice.activePrinter.camera.start()
camera.start()
}
} else
{
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
if(camera != null)
{
OutputDevice.activePrinter.camera.stop()
camera.stop()
}
}
}
source:
{
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null && OutputDevice.activePrinter.camera.latestImage)
if(camera != null && camera.latestImage != null)
{
return OutputDevice.activePrinter.camera.latestImage;
return camera.latestImage;
}
return "";
}
@ -92,7 +95,7 @@ Item
anchors.fill: cameraImage
onClicked:
{
OutputDevice.setActivePrinter(null)
OutputDevice.setActiveCamera(null)
}
z: 1
}

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.16 48"><defs><style>.cls-1{fill:#9a9a9a;}</style></defs><title>UM3-icon</title><g id="Symbols"><g id="system_overview_inactive" data-name="system overview inactive"><path id="Shape" class="cls-1" d="M18.4,12.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27a.22.22,0,0,0-.2-.2H16.67a.22.22,0,0,0-.2.2L18.2,12C18.3,12.2,18.3,12.2,18.4,12.2Z"/><path id="Shape-2" data-name="Shape" class="cls-1" d="M38.33,35.08H7.72a.48.48,0,0,0-.5.51V37a.48.48,0,0,0,.5.51H38.44a.48.48,0,0,0,.5-.51V35.59A.64.64,0,0,0,38.33,35.08Z"/><path id="Shape-3" data-name="Shape" class="cls-1" d="M0,0V48H3.76a2.86,2.86,0,0,1,2.13-1H40.27a2.86,2.86,0,0,1,2.13,1h3.76V0ZM41.28,37a2.83,2.83,0,0,1-2.84,2.84H7.72A2.84,2.84,0,0,1,4.88,37V5.49a.65.65,0,0,1,.61-.61H40.67a.65.65,0,0,1,.61.61Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.16 58"><defs><style>.cls-1{fill:#9a9a9a;}</style></defs><title>UM3x-icon</title><g id="Symbols"><g id="system_overview_inactive" data-name="system overview inactive"><path id="Shape" class="cls-1" d="M18.4,12.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27a.22.22,0,0,0-.2-.2H16.67a.22.22,0,0,0-.2.2L18.2,12C18.3,12.2,18.3,12.2,18.4,12.2Z"/><path id="Shape-2" data-name="Shape" class="cls-1" d="M38.33,45.08H7.72a.48.48,0,0,0-.5.51V47a.48.48,0,0,0,.5.51H38.44a.48.48,0,0,0,.5-.51V45.59A.64.64,0,0,0,38.33,45.08Z"/><path id="Shape-3" data-name="Shape" class="cls-1" d="M0,0V58H3.76a2.86,2.86,0,0,1,2.13-1H40.27a2.86,2.86,0,0,1,2.13,1h3.76V0ZM41.28,35.32V47a2.83,2.83,0,0,1-2.84,2.84H7.72A2.84,2.84,0,0,1,4.88,47V5.49a.65.65,0,0,1,.61-.61H40.67a.65.65,0,0,1,.61.61Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 854 B

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><defs><style>.cls-1{fill:none;}.cls-2{fill:#9a9a9a;}</style></defs><title>UMs5-icon</title><path class="cls-1" d="M33.83,12.33c-.1.2-.1.2-.2.2H24.37c-.1,0-.1,0-.2-.2L22.44,8.06a.22.22,0,0,1,.2-.2H35.36a.22.22,0,0,1,.2.2Z"/><path class="cls-2" d="M35.36,7.86H22.64a.22.22,0,0,0-.2.2l1.73,4.27c.1.2.1.2.2.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27A.22.22,0,0,0,35.36,7.86Z"/><path class="cls-2" d="M0,0V58H3.75a2.85,2.85,0,0,1,2.12-1H52.13a2.85,2.85,0,0,1,2.12,1H58V0ZM37.5,53.82a1.5,1.5,0,0,1-1.5,1.5H22a1.5,1.5,0,0,1-1.5-1.5v-4a1.5,1.5,0,0,1,1.5-1.5H36a1.5,1.5,0,0,1,1.5,1.5Zm15.63-18.5V47a2.83,2.83,0,0,1-2.83,2.84H38.5v0a2.5,2.5,0,0,0-2.5-2.5H22a2.5,2.5,0,0,0-2.5,2.5v0H7.7A2.83,2.83,0,0,1,4.87,47V5.49a.65.65,0,0,1,.6-.61H52.53a.65.65,0,0,1,.6.61Z"/></svg>

After

Width:  |  Height:  |  Size: 842 B

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>aborted-icon</title><path d="M16,0A16,16,0,1,0,32,16,16,16,0,0,0,16,0Zm1.69,28.89a13,13,0,1,1,11.2-11.2A13,13,0,0,1,17.69,28.89Z"/><polygon points="20.6 9.28 16 13.88 11.4 9.28 9.28 11.4 13.88 16 9.28 20.6 11.4 22.72 16 18.12 20.6 22.72 22.72 20.6 18.12 16 22.72 11.4 20.6 9.28"/></svg>

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 844 B

After

Width:  |  Height:  |  Size: 844 B

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>approved-icon</title><path d="M16,29A13,13,0,1,0,3,16,13,13,0,0,0,16,29ZM8,14.59l6,5.3L23.89,9l2.22,2L14.18,24.11,6,16.83Z" fill="none"/><path d="M16,32A16,16,0,1,0,0,16,16,16,0,0,0,16,32ZM16,3A13,13,0,1,1,3,16,13,13,0,0,1,16,3Z"/><polygon points="26.11 11.01 23.89 8.99 13.96 19.89 8 14.59 6 16.83 14.18 24.11 26.11 11.01"/></svg>

After

Width:  |  Height:  |  Size: 431 B

View File

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 311 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 194 B

After

Width:  |  Height:  |  Size: 194 B

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>paused-icon</title><path d="M16,0A16,16,0,1,0,32,16,16,16,0,0,0,16,0Zm0,29A13,13,0,1,1,29,16,13,13,0,0,1,16,29Z"/><rect x="11.5" y="9" width="3" height="14"/><rect x="17.5" y="9" width="3" height="14"/></svg>

After

Width:  |  Height:  |  Size: 308 B

View File

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

After

Width:  |  Height:  |  Size: 684 B

View File

@ -4,19 +4,21 @@
from typing import Any, cast, Optional, Set, Tuple, Union
from UM.FileHandler.FileHandler import FileHandler
from UM.FileHandler.FileWriter import FileWriter #To choose based on the output file mode (text vs. binary).
from UM.FileHandler.WriteFileJob import WriteFileJob #To call the file writer asynchronously.
from UM.FileHandler.FileWriter import FileWriter # To choose based on the output file mode (text vs. binary).
from UM.FileHandler.WriteFileJob import WriteFileJob # To call the file writer asynchronously.
from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
from UM.Mesh.MeshWriter import MeshWriter # For typing
from UM.Message import Message
from UM.Qt.Duration import Duration, DurationFormat
from UM.OutputDevice import OutputDeviceError #To show that something went wrong when writing.
from UM.Scene.SceneNode import SceneNode #For typing.
from UM.Version import Version #To check against firmware versions for support.
from UM.OutputDevice import OutputDeviceError # To show that something went wrong when writing.
from UM.Scene.SceneNode import SceneNode # For typing.
from UM.Version import Version # To check against firmware versions for support.
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
@ -27,14 +29,14 @@ from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
from .SendMaterialJob import SendMaterialJob
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtGui import QDesktopServices, QImage
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
from time import time
from datetime import datetime
from typing import Optional, Dict, List, Set
from typing import Optional, Dict, List
import io #To create the correct buffers for sending data to the printer.
import io # To create the correct buffers for sending data to the printer.
import json
import os
@ -44,6 +46,7 @@ i18n_catalog = i18nCatalog("cura")
class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
printJobsChanged = pyqtSignal()
activePrinterChanged = pyqtSignal()
activeCameraChanged = 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.
# Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions.
@ -59,24 +62,24 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._print_jobs = [] # type: List[PrintJobOutputModel]
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterMonitorItem.qml")
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterControlItem.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")
# See comments about this hack with the clusterPrintersChanged signal
self.printersChanged.connect(self.clusterPrintersChanged)
self._accepts_commands = True #type: bool
self._accepts_commands = True # type: bool
# Cluster does not have authentication, so default to authenticated
self._authentication_state = AuthState.Authenticated
self._error_message = None #type: Optional[Message]
self._write_job_progress_message = None #type: Optional[Message]
self._progress_message = None #type: Optional[Message]
self._error_message = None # type: Optional[Message]
self._write_job_progress_message = None # type: Optional[Message]
self._progress_message = None # type: Optional[Message]
self._active_printer = None # type: Optional[PrinterOutputModel]
self._printer_selection_dialog = None #type: QObject
self._printer_selection_dialog = None # type: QObject
self.setPriority(3) # Make sure the output device gets selected above local file output
self.setName(self._id)
@ -87,32 +90,35 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str]
self._finished_jobs = [] # type: List[PrintJobOutputModel]
self._finished_jobs = [] # type: List[PrintJobOutputModel]
self._cluster_size = int(properties.get(b"cluster_size", 0))
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._active_camera = None # type: Optional[NetworkCamera]
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.sendMaterialProfiles()
#Formats supported by this application (file types that we can actually write).
# Formats supported by this application (file types that we can actually write).
if file_handler:
file_formats = file_handler.getSupportedFileTypesWrite()
else:
file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
#Create a list from the supported file formats string.
# Create a list from the supported file formats string.
if not global_stack:
Logger.log("e", "Missing global stack!")
return
machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";")
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
# Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"):
machine_file_formats = ["application/x-ufp"] + machine_file_formats
@ -125,7 +131,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
raise OutputDeviceError.WriteRequestFailedError(i18n_catalog.i18nc("@info:status", "There are no file formats available to write with!"))
preferred_format = file_formats[0]
#Just take the first file format available.
# Just take the first file format available.
if file_handler is not None:
writer = file_handler.getWriterByMimeType(cast(str, preferred_format["mime_type"]))
else:
@ -135,29 +141,30 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
Logger.log("e", "Unexpected error when trying to get the FileWriter")
return
#This function pauses with the yield, waiting on instructions on which printer it needs to print with.
# This function pauses with the yield, waiting on instructions on which printer it needs to print with.
if not writer:
Logger.log("e", "Missing file or mesh writer!")
return
self._sending_job = self._sendPrintJob(writer, preferred_format, nodes)
self._sending_job.send(None) #Start the generator.
if self._sending_job is not None:
self._sending_job.send(None) # Start the generator.
if len(self._printers) > 1: #We need to ask the user.
self._spawnPrinterSelectionDialog()
is_job_sent = True
else: #Just immediately continue.
self._sending_job.send("") #No specifically selected printer.
is_job_sent = self._sending_job.send(None)
if len(self._printers) > 1: # We need to ask the user.
self._spawnPrinterSelectionDialog()
is_job_sent = True
else: # Just immediately continue.
self._sending_job.send("") # No specifically selected printer.
is_job_sent = self._sending_job.send(None)
def _spawnPrinterSelectionDialog(self):
if self._printer_selection_dialog is None:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PrintWindow.qml")
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/PrintWindow.qml")
self._printer_selection_dialog = CuraApplication.getInstance().createQmlComponent(path, {"OutputDevice": self})
if self._printer_selection_dialog is not None:
self._printer_selection_dialog.show()
@pyqtProperty(int, constant=True)
def clusterSize(self):
def clusterSize(self) -> int:
return self._cluster_size
## Allows the user to choose a printer to print with from the printer
@ -165,7 +172,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# \param target_printer The name of the printer to target.
@pyqtSlot(str)
def selectPrinter(self, target_printer: str = "") -> None:
self._sending_job.send(target_printer)
if self._sending_job is not None:
self._sending_job.send(target_printer)
@pyqtSlot()
def cancelPrintSelection(self) -> None:
@ -214,8 +222,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
job.start()
yield True #Return that we had success!
yield #To prevent having to catch the StopIteration exception.
yield True # Return that we had success!
yield # To prevent having to catch the StopIteration exception.
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None:
if self._write_job_progress_message:
@ -240,7 +248,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
file_name = CuraApplication.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"]
output = stream.getvalue() #Either str or bytes depending on the output mode.
output = stream.getvalue() # Either str or bytes depending on the output mode.
if isinstance(stream, io.StringIO):
output = cast(str, output).encode("utf-8")
output = cast(bytes, output)
@ -253,6 +261,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
def activePrinter(self) -> Optional[PrinterOutputModel]:
return self._active_printer
@pyqtProperty(QObject, notify=activeCameraChanged)
def activeCamera(self) -> Optional[NetworkCamera]:
return self._active_camera
@pyqtSlot(QObject)
def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None:
if self._active_printer != printer:
@ -261,6 +273,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._active_printer = printer
self.activePrinterChanged.emit()
@pyqtSlot(QObject)
def setActiveCamera(self, camera: Optional[NetworkCamera]) -> None:
if self._active_camera != camera:
if self._active_camera:
self._active_camera.stop()
self._active_camera = camera
if self._active_camera:
self._active_camera.start()
self.activeCameraChanged.emit()
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
if self._progress_message:
self._progress_message.hide()
@ -279,8 +304,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# If successfully sent:
if bytes_sent == bytes_total:
# Show a confirmation to the user so they know the job was sucessful and provide the option to switch to the
# monitor tab.
# Show a confirmation to the user so they know the job was sucessful and provide the option to switch to
# the monitor tab.
self._success_message = Message(
i18n_catalog.i18nc("@info:status", "Print job was successfully sent to the printer."),
lifetime=5, dismissable=True,
@ -329,7 +354,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
@pyqtProperty("QVariantList", notify = printJobsChanged)
def queuedPrintJobs(self) -> List[PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.state == "queued"]
return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"]
@pyqtProperty("QVariantList", notify = printJobsChanged)
def activePrintJobs(self) -> List[PrintJobOutputModel]:
@ -348,6 +373,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
result.append({"machine_type": machine_type, "count": str(printer_count[machine_type])})
return result
@pyqtProperty("QVariantList", notify=clusterPrintersChanged)
def printers(self):
return self._printers
@pyqtSlot(int, result = str)
def formatDuration(self, seconds: int) -> str:
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
@ -364,6 +393,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
return (datetime_completed.strftime("%a %b ") + "{day}".format(day=datetime_completed.day)).upper()
@pyqtSlot(str)
def sendJobToTop(self, print_job_uuid: str) -> None:
# This function is part of the output device (and not of the printjob output model) as this type of operation
# is a modification of the cluster queue and not of the actual job.
data = "{\"to_position\": 0}"
self.put("print_jobs/{uuid}/move_to_position".format(uuid = print_job_uuid), data, on_finished=None)
@pyqtSlot(str)
def deleteJobFromQueue(self, print_job_uuid: str) -> None:
# This function is part of the output device (and not of the printjob output model) as this type of operation
# 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)
def _printJobStateChanged(self) -> None:
username = self._getUserName()
@ -392,11 +434,26 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
super().connect()
self.sendMaterialProfiles()
def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None:
reply_url = reply.url().toString()
uuid = reply_url[reply_url.find("print_jobs/")+len("print_jobs/"):reply_url.rfind("/preview_image")]
print_job = findByKey(self._print_jobs, uuid)
if print_job:
image = QImage()
image.loadFromData(reply.readAll())
print_job.updatePreviewImage(image)
def _update(self) -> None:
super()._update()
self.get("printers/", on_finished = self._onGetPrintersDataFinished)
self.get("print_jobs/", on_finished = self._onGetPrintJobsFinished)
for print_job in self._print_jobs:
if print_job.getPreviewImage() is None:
self.get("print_jobs/{uuid}/preview_image".format(uuid=print_job.key), on_finished=self._onGetPreviewImageFinished)
def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
if not checkValidGetReply(reply):
return
@ -407,16 +464,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
print_jobs_seen = []
job_list_changed = False
for print_job_data in result:
for idx, print_job_data in enumerate(result):
print_job = findByKey(self._print_jobs, print_job_data["uuid"])
if print_job is None:
print_job = self._createPrintJobModel(print_job_data)
job_list_changed = True
elif not job_list_changed:
# Check if the order of the jobs has changed since the last check
if self._print_jobs.index(print_job) != idx:
job_list_changed = True
self._updatePrintJob(print_job, print_job_data)
if print_job.state != "queued": # Print job should be assigned to a printer.
if print_job.state != "queued" and print_job.state != "error": # Print job should be assigned to a printer.
if print_job.state in ["failed", "finished", "aborted", "none"]:
# Print job was already completed, so don't attach it to a printer.
printer = None
@ -437,6 +497,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
job_list_changed = job_list_changed or self._removeJob(removed_job)
if job_list_changed:
# Override the old list with the new list (either because jobs were removed / added or order changed)
self._print_jobs = print_jobs_seen
self.printJobsChanged.emit() # Do a single emit for all print job changes.
def _onGetPrintersDataFinished(self, reply: QNetworkReply) -> None:
@ -478,16 +540,59 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
def _createPrintJobModel(self, data: Dict[str, Any]) -> PrintJobOutputModel:
print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
key=data["uuid"], name= data["name"])
configuration = ConfigurationModel()
extruders = [ExtruderConfigurationModel(position = idx) for idx in range(0, self._number_of_extruders)]
for index in range(0, self._number_of_extruders):
try:
extruder_data = data["configuration"][index]
except IndexError:
continue
extruder = extruders[int(data["configuration"][index]["extruder_index"])]
extruder.setHotendID(extruder_data.get("print_core_id", ""))
extruder.setMaterial(self._createMaterialOutputModel(extruder_data.get("material", {})))
configuration.setExtruderConfigurations(extruders)
print_job.updateConfiguration(configuration)
print_job.setCompatibleMachineFamilies(data.get("compatible_machine_families", []))
print_job.stateChanged.connect(self._printJobStateChanged)
self._print_jobs.append(print_job)
return print_job
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict[str, Any]) -> None:
print_job.updateTimeTotal(data["time_total"])
print_job.updateTimeElapsed(data["time_elapsed"])
print_job.updateState(data["status"])
impediments_to_printing = data.get("impediments_to_printing", [])
print_job.updateOwner(data["owner"])
status_set_by_impediment = False
for impediment in impediments_to_printing:
if impediment["severity"] == "UNFIXABLE":
status_set_by_impediment = True
print_job.updateState("error")
break
if not status_set_by_impediment:
print_job.updateState(data["status"])
def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel:
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"])
if containers:
color = containers[0].getMetaDataEntry("color_code")
brand = containers[0].getMetaDataEntry("brand")
material_type = containers[0].getMetaDataEntry("material")
name = containers[0].getName()
else:
Logger.log("w",
"Unable to find material with guid {guid}. Using data as provided by cluster".format(
guid=material_data["guid"]))
color = material_data["color"]
brand = material_data["brand"]
material_type = material_data["material"]
name = "Empty" if material_data["material"] == "empty" else "Unknown"
return MaterialOutputModel(guid=material_data["guid"], type=material_type,
brand=brand, color=color, name=name)
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None:
# For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer.
# Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping.
@ -523,24 +628,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
material_data = extruder_data["material"]
if extruder.activeMaterial is None or extruder.activeMaterial.guid != material_data["guid"]:
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material",
GUID=material_data["guid"])
if containers:
color = containers[0].getMetaDataEntry("color_code")
brand = containers[0].getMetaDataEntry("brand")
material_type = containers[0].getMetaDataEntry("material")
name = containers[0].getName()
else:
Logger.log("w",
"Unable to find material with guid {guid}. Using data as provided by cluster".format(
guid=material_data["guid"]))
color = material_data["color"]
brand = material_data["brand"]
material_type = material_data["material"]
name = "Empty" if material_data["material"] == "empty" else "Unknown"
material = MaterialOutputModel(guid=material_data["guid"], type=material_type,
brand=brand, color=color, name=name)
material = self._createMaterialOutputModel(material_data)
extruder.updateActiveMaterial(material)
def _removeJob(self, job: PrintJobOutputModel) -> bool:
@ -568,6 +656,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
job = SendMaterialJob(device = self)
job.run()
def loadJsonFromReply(reply: QNetworkReply) -> Optional[List[Dict[str, Any]]]:
try:
result = json.loads(bytes(reply.readAll()).decode("utf-8"))
@ -586,8 +675,8 @@ def checkValidGetReply(reply: QNetworkReply) -> bool:
return True
def findByKey(list: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]:
for item in list:
def findByKey(lst: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]:
for item in lst:
if item.key == key:
return item
return None

View File

@ -6,8 +6,6 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
MYPY = False
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
class ClusterUM3PrinterOutputController(PrinterOutputController):
def __init__(self, output_device):

View File

@ -24,7 +24,7 @@ class DiscoverUM3Action(MachineAction):
def __init__(self) -> None:
super().__init__("DiscoverUM3Action", catalog.i18nc("@action","Connect via Network"))
self._qml_url = "DiscoverUM3Action.qml"
self._qml_url = "resources/qml/DiscoverUM3Action.qml"
self._network_plugin = None #type: Optional[UM3OutputDevicePlugin]
@ -174,7 +174,7 @@ class DiscoverUM3Action(MachineAction):
plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
if not plugin_path:
return
path = os.path.join(plugin_path, "UM3InfoComponents.qml")
path = os.path.join(plugin_path, "resources/qml/UM3InfoComponents.qml")
self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
if not self.__additional_components_view:
Logger.log("w", "Could not create ui components for UM3.")

View File

@ -76,7 +76,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self.setIconName("print")
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml")
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorItem.qml")
self._output_controller = LegacyUM3PrinterOutputController(self)

View File

@ -60,8 +60,8 @@ _RENAMED_MATERIAL_PROFILES = {
}
## Upgrades configurations from the state they were in at version 3.4 to the
# state they should be in at version 4.0.
class VersionUpgrade34to40(VersionUpgrade):
# state they should be in at version 3.5.
class VersionUpgrade34to35(VersionUpgrade):
## Gets the version number from a CFG file in Uranium's 3.3 format.
#

View File

@ -1,9 +1,9 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from . import VersionUpgrade34to40
from . import VersionUpgrade34to35
upgrade = VersionUpgrade34to40.VersionUpgrade34to40()
upgrade = VersionUpgrade34to35.VersionUpgrade34to35()
def getMetaData():

View File

@ -1,8 +1,8 @@
{
"name": "Version Upgrade 3.4 to 4.0",
"name": "Version Upgrade 3.4 to 3.5",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 3.4 to Cura 4.0.",
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
"api": 5,
"i18n-catalog": "cura"
}

View File

@ -4,12 +4,12 @@
import configparser #To parse the resulting config files.
import pytest #To register tests with.
import VersionUpgrade34to40 #The module we're testing.
import VersionUpgrade34to35 #The module we're testing.
## Creates an instance of the upgrader to test with.
@pytest.fixture
def upgrader():
return VersionUpgrade34to40.VersionUpgrade34to40()
return VersionUpgrade34to35.VersionUpgrade34to35()
test_upgrade_version_nr_data = [
("Empty config file",

View File

@ -269,7 +269,7 @@ class XmlMaterialProfile(InstanceContainer):
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
buildplate_dict = {} # type: Dict[str, Any]
for variant_name, variant_dict in machine_variant_map[definition_id].items():
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
variant_type = variant_dict["variant_node"].getMetaDataEntry("hardware_type", str(VariantType.NOZZLE))
variant_type = VariantType(variant_type)
if variant_type == VariantType.NOZZLE:
# The hotend identifier is not the containers name, but its "name".

View File

@ -6,7 +6,7 @@
"display_name": "3MF Reader",
"description": "Provides support for reading 3MF files.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -23,7 +23,7 @@
"display_name": "3MF Writer",
"description": "Provides support for writing 3MF files.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -40,7 +40,7 @@
"display_name": "Change Log",
"description": "Shows changes since latest checked version.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -57,7 +57,7 @@
"display_name": "CuraEngine Backend",
"description": "Provides the link to the CuraEngine slicing backend.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -74,7 +74,7 @@
"display_name": "Cura Profile Reader",
"description": "Provides support for importing Cura profiles.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -91,7 +91,7 @@
"display_name": "Cura Profile Writer",
"description": "Provides support for exporting Cura profiles.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -108,7 +108,7 @@
"display_name": "Firmware Update Checker",
"description": "Checks for firmware updates.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -125,7 +125,7 @@
"display_name": "Compressed G-code Reader",
"description": "Reads g-code from a compressed archive.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -142,7 +142,7 @@
"display_name": "Compressed G-code Writer",
"description": "Writes g-code to a compressed archive.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -159,7 +159,7 @@
"display_name": "G-Code Profile Reader",
"description": "Provides support for importing profiles from g-code files.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -176,7 +176,7 @@
"display_name": "G-Code Reader",
"description": "Allows loading and displaying G-code files.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "VictorLarchenko",
@ -193,7 +193,7 @@
"display_name": "G-Code Writer",
"description": "Writes g-code to a file.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -210,7 +210,7 @@
"display_name": "Image Reader",
"description": "Enables ability to generate printable geometry from 2D image files.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -227,7 +227,7 @@
"display_name": "Legacy Cura Profile Reader",
"description": "Provides support for importing profiles from legacy Cura versions.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -244,7 +244,7 @@
"display_name": "Machine Settings Action",
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "fieldOfView",
@ -261,7 +261,7 @@
"display_name": "Model Checker",
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
"package_version": "0.1.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -278,7 +278,7 @@
"display_name": "Monitor Stage",
"description": "Provides a monitor stage in Cura.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -295,7 +295,7 @@
"display_name": "Per-Object Settings Tool",
"description": "Provides the per-model settings.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -312,7 +312,7 @@
"display_name": "Post Processing",
"description": "Extension that allows for user created scripts for post processing.",
"package_version": "2.2.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -329,7 +329,7 @@
"display_name": "Prepare Stage",
"description": "Provides a prepare stage in Cura.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -346,7 +346,7 @@
"display_name": "Removable Drive Output Device",
"description": "Provides removable drive hotplugging and writing support.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -363,7 +363,7 @@
"display_name": "Simulation View",
"description": "Provides the Simulation view.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -380,7 +380,7 @@
"display_name": "Slice Info",
"description": "Submits anonymous slice info. Can be disabled through preferences.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -397,7 +397,7 @@
"display_name": "Solid View",
"description": "Provides a normal solid mesh view.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -414,7 +414,7 @@
"display_name": "Support Eraser Tool",
"description": "Creates an eraser mesh to block the printing of support in certain places.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -431,7 +431,7 @@
"display_name": "Toolbox",
"description": "Find, manage and install new Cura packages.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -448,7 +448,7 @@
"display_name": "UFP Writer",
"description": "Provides support for writing Ultimaker Format Packages.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -465,7 +465,7 @@
"display_name": "Ultimaker Machine Actions",
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -482,7 +482,7 @@
"display_name": "UM3 Network Printing",
"description": "Manages network connections to Ultimaker 3 printers.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -499,7 +499,7 @@
"display_name": "USB Printing",
"description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -516,7 +516,7 @@
"display_name": "User Agreement",
"description": "Ask the user once if he/she agrees with our license.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -533,7 +533,7 @@
"display_name": "Version Upgrade 2.1 to 2.2",
"description": "Upgrades configurations from Cura 2.1 to Cura 2.2.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -550,7 +550,7 @@
"display_name": "Version Upgrade 2.2 to 2.4",
"description": "Upgrades configurations from Cura 2.2 to Cura 2.4.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -567,7 +567,7 @@
"display_name": "Version Upgrade 2.5 to 2.6",
"description": "Upgrades configurations from Cura 2.5 to Cura 2.6.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -584,7 +584,7 @@
"display_name": "Version Upgrade 2.6 to 2.7",
"description": "Upgrades configurations from Cura 2.6 to Cura 2.7.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -601,7 +601,7 @@
"display_name": "Version Upgrade 2.7 to 3.0",
"description": "Upgrades configurations from Cura 2.7 to Cura 3.0.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -618,7 +618,7 @@
"display_name": "Version Upgrade 3.0 to 3.1",
"description": "Upgrades configurations from Cura 3.0 to Cura 3.1.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -635,7 +635,7 @@
"display_name": "Version Upgrade 3.2 to 3.3",
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -652,7 +652,7 @@
"display_name": "Version Upgrade 3.3 to 3.4",
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -662,14 +662,14 @@
}
}
},
"VersionUpgrade34to40": {
"VersionUpgrade34to35": {
"package_info": {
"package_id": "VersionUpgrade34to40",
"package_id": "VersionUpgrade34to35",
"package_type": "plugin",
"display_name": "Version Upgrade 3.4 to 4.0",
"description": "Upgrades configurations from Cura 3.4 to Cura 4.0.",
"display_name": "Version Upgrade 3.4 to 3.5",
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -686,7 +686,7 @@
"display_name": "X3D Reader",
"description": "Provides support for reading X3D files.",
"package_version": "0.5.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "SevaAlekseyev",
@ -703,7 +703,7 @@
"display_name": "XML Material Profiles",
"description": "Provides capabilities to read and write XML-based material profiles.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -720,7 +720,7 @@
"display_name": "X-Ray View",
"description": "Provides the X-Ray view.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "Ultimaker",
@ -971,7 +971,7 @@
"display_name": "Dagoma Chromatik PLA",
"description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://dagoma.fr/boutique/filaments.html",
"author": {
"author_id": "Dagoma",
@ -988,7 +988,7 @@
"display_name": "FABtotum ABS",
"description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so its compatible with all versions of the FABtotum Personal fabricator.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40",
"author": {
"author_id": "FABtotum",
@ -1005,7 +1005,7 @@
"display_name": "FABtotum Nylon",
"description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53",
"author": {
"author_id": "FABtotum",
@ -1022,7 +1022,7 @@
"display_name": "FABtotum PLA",
"description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starchs sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotums one is between 185° and 195°.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39",
"author": {
"author_id": "FABtotum",
@ -1039,7 +1039,7 @@
"display_name": "FABtotum TPU Shore 98A",
"description": "",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66",
"author": {
"author_id": "FABtotum",
@ -1056,7 +1056,7 @@
"display_name": "Fiberlogy HD PLA",
"description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/",
"author": {
"author_id": "Fiberlogy",
@ -1073,7 +1073,7 @@
"display_name": "Filo3D PLA",
"description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://dagoma.fr",
"author": {
"author_id": "Dagoma",
@ -1090,7 +1090,7 @@
"display_name": "IMADE3D JellyBOX PETG",
"description": "",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://shop.imade3d.com/filament.html",
"author": {
"author_id": "IMADE3D",
@ -1107,7 +1107,7 @@
"display_name": "IMADE3D JellyBOX PLA",
"description": "",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://shop.imade3d.com/filament.html",
"author": {
"author_id": "IMADE3D",
@ -1124,7 +1124,7 @@
"display_name": "Octofiber PLA",
"description": "PLA material from Octofiber.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://nl.octofiber.com/3d-printing-filament/pla.html",
"author": {
"author_id": "Octofiber",
@ -1141,7 +1141,7 @@
"display_name": "PolyFlex™ PLA",
"description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://www.polymaker.com/shop/polyflex/",
"author": {
"author_id": "Polymaker",
@ -1158,7 +1158,7 @@
"display_name": "PolyMax™ PLA",
"description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://www.polymaker.com/shop/polymax/",
"author": {
"author_id": "Polymaker",
@ -1175,7 +1175,7 @@
"display_name": "PolyPlus™ PLA True Colour",
"description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://www.polymaker.com/shop/polyplus-true-colour/",
"author": {
"author_id": "Polymaker",
@ -1192,7 +1192,7 @@
"display_name": "PolyWood™ PLA",
"description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "http://www.polymaker.com/shop/polywood/",
"author": {
"author_id": "Polymaker",
@ -1209,7 +1209,7 @@
"display_name": "Ultimaker ABS",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "Ultimaker",
@ -1228,7 +1228,7 @@
"display_name": "Ultimaker CPE",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "Ultimaker",
@ -1247,7 +1247,7 @@
"display_name": "Ultimaker Nylon",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "Ultimaker",
@ -1266,7 +1266,7 @@
"display_name": "Ultimaker PC",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/pc",
"author": {
"author_id": "Ultimaker",
@ -1285,7 +1285,7 @@
"display_name": "Ultimaker PLA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "Ultimaker",
@ -1304,7 +1304,7 @@
"display_name": "Ultimaker PVA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://ultimaker.com/products/materials/abs",
"author": {
"author_id": "Ultimaker",
@ -1323,7 +1323,7 @@
"display_name": "Vertex Delta ABS",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1340,7 +1340,7 @@
"display_name": "Vertex Delta PET",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1357,7 +1357,7 @@
"display_name": "Vertex Delta PLA",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",
@ -1374,7 +1374,7 @@
"display_name": "Vertex Delta TPU",
"description": "ABS material and quality files for the Delta Vertex K8800.",
"package_version": "1.0.0",
"sdk_version": 4,
"sdk_version": 5,
"website": "https://vertex3dprinter.eu",
"author": {
"author_id": "Velleman",

View File

@ -11,7 +11,6 @@
"has_machine_quality": true,
"has_materials": true,
"has_machine_materials": true,
"has_variant_materials": true,
"has_variants": true,
"variants_name": "Tool",

View File

@ -0,0 +1,92 @@
{
"name": "Creality Ender-3",
"version": 2,
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Sacha Telgenhof",
"manufacturer": "Creality3D",
"file_formats": "text/x-gcode",
"platform": "creality_ender3_platform.stl",
"preferred_quality_type": "draft"
},
"overrides": {
"machine_name": {
"default_value": "Creality Ender-3"
},
"machine_width": {
"default_value": 220
},
"machine_height": {
"default_value": 250
},
"machine_depth": {
"default_value": 220
},
"machine_heated_bed": {
"default_value": true
},
"gantry_height": {
"default_value": 30
},
"machine_head_polygon": {
"default_value": [
[-30, 34],
[-30, -32],
[30, -32],
[30, 34]
]
},
"material_diameter": {
"default_value": 1.75
},
"acceleration_enabled": {
"default_value": true
},
"acceleration_print": {
"default_value": 500
},
"acceleration_travel": {
"default_value": 500
},
"jerk_enabled": {
"default_value": true
},
"jerk_travel": {
"default_value": 20
},
"layer_height": {
"default_value": 0.15
},
"layer_height_0": {
"default_value": 0.2
},
"adhesion_type": {
"default_value": "skirt"
},
"top_bottom_thickness": {
"default_value": 0.6
},
"retraction_amount": {
"default_value": 5
},
"retraction_speed": {
"default_value": 40
},
"cool_min_layer_time": {
"default_value": 10
},
"skirt_line_count": {
"default_value": 4
},
"skirt_gap": {
"default_value": 5
},
"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"
},
"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"
}
}
}

View File

@ -1297,8 +1297,8 @@
"default_value": 0,
"type": "float",
"enabled": "travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_mesh": true,
"settable_per_extruder": false
},
"wall_min_flow_retract":
{
@ -1307,8 +1307,8 @@
"type": "bool",
"default_value": false,
"enabled": "(travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled) and wall_min_flow > 0",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_mesh": true,
"settable_per_extruder": false
},
"fill_perimeter_gaps":
{
@ -6496,6 +6496,30 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"wall_overhang_angle":
{
"label": "Overhanging Wall Angle",
"description": "Walls that overhang more than this angle will be printed using overhanging wall settings. When the value is 90, no walls will be treated as overhanging.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "2",
"maximum_value": "90",
"default_value": 90,
"settable_per_mesh": true
},
"wall_overhang_speed_factor":
{
"label": "Overhanging Wall Speed",
"description": "Overhanging walls will be printed at this percentage of their normal print speed.",
"unit": "%",
"type": "float",
"default_value": 100,
"minimum_value": "10",
"minimum_value_warning": "25",
"maximum_value": "100",
"settable_per_mesh": true
},
"bridge_settings_enabled":
{
"label": "Enable Bridge Settings",
@ -6515,8 +6539,8 @@
"minimum_value": "0",
"default_value": 5,
"enabled": "bridge_settings_enabled",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_mesh": true,
"settable_per_extruder": false
},
"bridge_skin_support_threshold":
{
@ -6530,18 +6554,6 @@
"enabled": "bridge_settings_enabled",
"settable_per_mesh": true
},
"bridge_wall_max_overhang":
{
"label": "Bridge Wall Max Overhang",
"description": "The maximum allowed width of the region of air below a wall line before the wall is printed using bridge settings. Expressed as a percentage of the wall line width. When the air gap is wider than this, the wall line is printed using the bridge settings. Otherwise, the wall line is printed using the normal settings. The lower the value, the more likely it is that overhung wall lines will be printed using bridge settings.",
"unit": "%",
"default_value": 100,
"type": "float",
"minimum_value": "0",
"maximum_value": "100",
"enabled": "bridge_settings_enabled",
"settable_per_mesh": true
},
"bridge_wall_coast":
{
"label": "Bridge Wall Coasting",
@ -6552,7 +6564,7 @@
"minimum_value": "0",
"maximum_value": "500",
"enabled": "bridge_settings_enabled",
"settable_per_mesh": false
"settable_per_mesh": true
},
"bridge_wall_speed":
{

View File

@ -13,7 +13,6 @@
"has_machine_quality": true,
"has_materials": true,
"has_machine_materials": true,
"has_variant_materials": true,
"has_variants": true,
"preferred_variant_name": "AA 0.4",
"preferred_quality_type": "normal",

View File

@ -13,7 +13,6 @@
"platform_offset": [0, 0, 0],
"has_machine_quality": true,
"has_machine_materials": true,
"has_variant_materials": true,
"has_materials": true,
"has_variants": true,
"preferred_variant_name": "AA 0.4",

View File

@ -15,7 +15,6 @@
"has_machine_quality": true,
"has_materials": true,
"has_machine_materials": true,
"has_variant_materials": true,
"has_variant_buildplates": true,
"has_variants": true,
"preferred_variant_name": "AA 0.4",

View File

@ -17,10 +17,10 @@
"machine_nozzle_offset_y": { "default_value": 0 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "default_value": 310 },
"machine_extruder_start_pos_x": { "default_value": 330 },
"machine_extruder_start_pos_y": { "default_value": 237 },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "default_value": 310 },
"machine_extruder_end_pos_x": { "default_value": 330 },
"machine_extruder_end_pos_y": { "default_value": 237 },
"machine_nozzle_head_distance": { "default_value": 2.7 },
"extruder_prime_pos_x": { "default_value": -3 },

View File

@ -17,10 +17,10 @@
"machine_nozzle_offset_y": { "default_value": 0 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "default_value": 310 },
"machine_extruder_start_pos_x": { "default_value": 330 },
"machine_extruder_start_pos_y": { "default_value": 219 },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "default_value": 310 },
"machine_extruder_end_pos_x": { "default_value": 330 },
"machine_extruder_end_pos_y": { "default_value": 219 },
"machine_nozzle_head_distance": { "default_value": 4.2 },
"extruder_prime_pos_x": { "default_value": 333 },

View File

@ -3860,7 +3860,7 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgid "Toggle Full Screen"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
@ -3880,27 +3880,27 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgid "3D View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgid "Front View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgid "Top View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgid "Left Side View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgid "Right Side View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
@ -3961,8 +3961,8 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] ""
msgstr[1] ""
@ -4012,17 +4012,17 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgid "Select All Models"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgid "Clear Build Plate"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgid "Reload All Models"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
@ -4047,7 +4047,7 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgid "Reset All Model Transformations"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
@ -4195,7 +4195,7 @@ msgid "&Settings"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr ""
@ -4222,7 +4222,7 @@ msgid "Disable Extruder"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr ""

View File

@ -3707,8 +3707,8 @@ msgstr "Geschätzte verbleibende Zeit"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "Umschalten auf Vo&llbild-Modus"
msgid "Toggle Full Screen"
msgstr "Umschalten auf Vollbild-Modus"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3727,28 +3727,28 @@ msgstr "&Beenden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "&3D-Ansicht"
msgid "3D View"
msgstr "3D-Ansicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "&Vorderansicht"
msgid "Front View"
msgstr "Vorderansicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "&Draufsicht"
msgid "Top View"
msgstr "Draufsicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "&Ansicht von links"
msgid "Left Side View"
msgstr "Ansicht von links"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "&Ansicht von rechts"
msgid "Right Side View"
msgstr "Ansicht von rechts"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3783,7 +3783,7 @@ msgstr "&Aktuelle Änderungen verwerfen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:196
msgctxt "@action:inmenu menubar:profile"
msgid "&Create profile from current settings/overrides..."
msgstr "&Profil von aktuellen Einstellungen/Überschreibungen erstellen..."
msgstr "P&rofil von aktuellen Einstellungen/Überschreibungen erstellen..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:202
msgctxt "@action:inmenu menubar:profile"
@ -3808,10 +3808,10 @@ msgstr "&Über..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "&Ausgewähltes Modell löschen"
msgstr[1] "&Ausgewählte Modelle löschen"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Ausgewähltes Modell löschen"
msgstr[1] "Ausgewählte Modelle löschen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3859,18 +3859,18 @@ msgstr "Modell &multiplizieren"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "Alle Modelle &wählen"
msgid "Select All Models"
msgstr "Alle Modelle wählen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "Druckplatte &reinigen"
msgid "Clear Build Plate"
msgstr "Druckplatte reinigen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "Alle Modelle neu &laden"
msgid "Reload All Models"
msgstr "Alle Modelle neu laden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3894,8 +3894,8 @@ msgstr "Alle Modellpositionen zurücksetzen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgstr "Alle Modell&transformationen zurücksetzen"
msgid "Reset All Model Transformations"
msgstr "Alle Modelltransformationen zurücksetzen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
msgctxt "@action:inmenu menubar:file"
@ -4039,7 +4039,7 @@ msgid "&Settings"
msgstr "&Konfiguration"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "Dr&ucker"
@ -4066,7 +4066,7 @@ msgid "Disable Extruder"
msgstr "Extruder deaktivieren"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profil"

View File

@ -3582,7 +3582,7 @@ msgstr "&Posición de la cámara"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:54
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
msgstr "&Placa de impresión"
msgstr "P&laca de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:13
msgctxt "@action:inmenu"
@ -3719,8 +3719,8 @@ msgstr "Tiempo restante estimado"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "A&lternar pantalla completa"
msgid "Toggle Full Screen"
msgstr "Alternar pantalla completa"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3739,28 +3739,28 @@ msgstr "&Salir"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "&Vista en 3D"
msgid "3D View"
msgstr "Vista en 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "&Vista frontal"
msgid "Front View"
msgstr "Vista frontal"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "&Vista superior"
msgid "Top View"
msgstr "Vista superior"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "&Vista del lado izquierdo"
msgid "Left Side View"
msgstr "Vista del lado izquierdo"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "&Vista del lado derecho"
msgid "Right Side View"
msgstr "Vista del lado derecho"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3820,10 +3820,10 @@ msgstr "&Acerca de..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Eliminar modelo &seleccionado"
msgstr[1] "Eliminar modelos &seleccionados"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Eliminar modelo seleccionado"
msgstr[1] "Eliminar modelos seleccionados"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3871,18 +3871,18 @@ msgstr "&Multiplicar modelo..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "&Seleccionar todos los modelos"
msgid "Select All Models"
msgstr "Seleccionar todos los modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Borrar placa de impresión"
msgid "Clear Build Plate"
msgstr "Borrar placa de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "&Recargar todos los modelos"
msgid "Reload All Models"
msgstr "Recargar todos los modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3906,8 +3906,8 @@ msgstr "Restablecer las posiciones de todos los modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgstr "Restablecer las &transformaciones de todos los modelos"
msgid "Reset All Model Transformations"
msgstr "Restablecer las transformaciones de todos los modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
msgctxt "@action:inmenu menubar:file"
@ -4023,7 +4023,7 @@ msgstr "&Archivo"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:119
msgctxt "@action:inmenu menubar:file"
msgid "&Save Selection to File"
msgstr "Guardar &selección en archivo"
msgstr "&Guardar selección en archivo"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:128
msgctxt "@title:menu menubar:file"
@ -4051,7 +4051,7 @@ msgid "&Settings"
msgstr "A&justes"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "&Impresora"
@ -4078,7 +4078,7 @@ msgid "Disable Extruder"
msgstr "Deshabilitar extrusor"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Perfil"

View File

@ -3726,8 +3726,8 @@ msgstr "Aikaa jäljellä arviolta"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "Vaihda &koko näyttöön"
msgid "Toggle Full Screen"
msgstr "Vaihda koko näyttöön"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3746,27 +3746,27 @@ msgstr "&Lopeta"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgid "3D View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgid "Front View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgid "Top View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgid "Left Side View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgid "Right Side View"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
@ -3827,10 +3827,10 @@ msgstr "Ti&etoja..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Poista &valittu malli"
msgstr[1] "Poista &valitut mallit"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Poista valittu malli"
msgstr[1] "Poista valitut mallit"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3878,18 +3878,18 @@ msgstr "&Kerro malli..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "&Valitse kaikki mallit"
msgid "Select All Models"
msgstr "Valitse kaikki mallit"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Tyhjennä tulostusalusta"
msgid "Clear Build Plate"
msgstr "Tyhjennä tulostusalusta"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "&Lataa kaikki mallit uudelleen"
msgid "Reload All Models"
msgstr "Lataa kaikki mallit uudelleen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3913,8 +3913,8 @@ msgstr "Määritä kaikkien mallien positiot uudelleen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgstr "Määritä kaikkien mallien &muutokset uudelleen"
msgid "Reset All Model Transformations"
msgstr "Määritä kaikkien mallien muutokset uudelleen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
msgctxt "@action:inmenu menubar:file"
@ -4025,7 +4025,7 @@ msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:102
msgctxt "@title:menu menubar:toplevel"
msgid "&File"
msgstr "&Tiedosto"
msgstr "Tie&dosto"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:119
msgctxt "@action:inmenu menubar:file"
@ -4058,7 +4058,7 @@ msgid "&Settings"
msgstr "&Asetukset"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "&Tulostin"
@ -4085,7 +4085,7 @@ msgid "Disable Extruder"
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profiili"

View File

@ -3707,8 +3707,8 @@ msgstr "Durée restante estimée"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "Passer en P&lein écran"
msgid "Toggle Full Screen"
msgstr "Passer en Plein écran"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3727,28 +3727,28 @@ msgstr "&Quitter"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "Vue &3D"
msgid "3D View"
msgstr "Vue 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "Vue de &face"
msgid "Front View"
msgstr "Vue de face"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "Vue du dess&us"
msgid "Top View"
msgstr "Vue du dessus"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "Vue latérale &gauche"
msgid "Left Side View"
msgstr "Vue latérale gauche"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "Vue latérale &droite"
msgid "Right Side View"
msgstr "Vue latérale droite"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3808,10 +3808,10 @@ msgstr "&À propos de..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Supprimer le modèle &sélectionné"
msgstr[1] "Supprimer les modèles &sélectionnés"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Supprimer le modèle sélectionné"
msgstr[1] "Supprimer les modèles sélectionnés"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3859,18 +3859,18 @@ msgstr "&Multiplier le modèle..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "&Sélectionner tous les modèles"
msgid "Select All Models"
msgstr "Sélectionner tous les modèles"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Supprimer les objets du plateau"
msgid "Clear Build Plate"
msgstr "Supprimer les objets du plateau"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "Rechar&ger tous les modèles"
msgid "Reload All Models"
msgstr "Recharger tous les modèles"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3894,7 +3894,7 @@ msgstr "Réinitialiser toutes les positions des modèles"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgid "Reset All Model Transformations"
msgstr "Réinitialiser tous les modèles et transformations"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
@ -4039,7 +4039,7 @@ msgid "&Settings"
msgstr "&Paramètres"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "Im&primante"
@ -4066,7 +4066,7 @@ msgid "Disable Extruder"
msgstr "Désactiver l'extrudeuse"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profil"

View File

@ -3568,7 +3568,7 @@ msgstr "&Posizione fotocamera"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:54
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
msgstr "&Piano di stampa"
msgstr "P&iano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:13
msgctxt "@action:inmenu"
@ -3705,8 +3705,8 @@ msgstr "Tempo residuo stimato"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "Att&iva/disattiva schermo intero"
msgid "Toggle Full Screen"
msgstr "Attiva/disattiva schermo intero"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3721,32 +3721,32 @@ msgstr "Ri&peti"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:105
msgctxt "@action:inmenu menubar:file"
msgid "&Quit"
msgstr "E&sci"
msgstr "&Esci"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "&Visualizzazione 3D"
msgid "3D View"
msgstr "Visualizzazione 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "&Visualizzazione frontale"
msgid "Front View"
msgstr "Visualizzazione frontale"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "&Visualizzazione superiore"
msgid "Top View"
msgstr "Visualizzazione superiore"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "&Visualizzazione lato sinistro"
msgid "Left Side View"
msgstr "Visualizzazione lato sinistro"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "&Visualizzazione lato destro"
msgid "Right Side View"
msgstr "Visualizzazione lato destro"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3756,12 +3756,12 @@ msgstr "Configura Cura..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:155
msgctxt "@action:inmenu menubar:printer"
msgid "&Add Printer..."
msgstr "A&ggiungi stampante..."
msgstr "&Aggiungi stampante..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:161
msgctxt "@action:inmenu menubar:printer"
msgid "Manage Pr&inters..."
msgstr "&Gestione stampanti..."
msgstr "Gestione stampanti..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:168
msgctxt "@action:inmenu"
@ -3806,10 +3806,10 @@ msgstr "I&nformazioni..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Cancella &modello selezionato"
msgstr[1] "Cancella modelli &selezionati"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Cancella modello selezionato"
msgstr[1] "Cancella modelli selezionati"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3857,18 +3857,18 @@ msgstr "Mo&ltiplica modello"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "Sel&eziona tutti i modelli"
msgid "Select All Models"
msgstr "Seleziona tutti i modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Cancellare piano di stampa"
msgid "Clear Build Plate"
msgstr "Cancellare piano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "R&icarica tutti i modelli"
msgid "Reload All Models"
msgstr "Ricarica tutti i modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3892,8 +3892,8 @@ msgstr "Reimposta tutte le posizioni dei modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgstr "Reimposta tutte le &trasformazioni dei modelli"
msgid "Reset All Model Transformations"
msgstr "Reimposta tutte le trasformazioni dei modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
msgctxt "@action:inmenu menubar:file"
@ -3908,7 +3908,7 @@ msgstr "&Nuovo Progetto..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:411
msgctxt "@action:inmenu menubar:help"
msgid "Show Engine &Log..."
msgstr "M&ostra log motore..."
msgstr "Mostra &log motore..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:419
msgctxt "@action:inmenu menubar:help"
@ -4037,7 +4037,7 @@ msgid "&Settings"
msgstr "&Impostazioni"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "S&tampante"
@ -4064,7 +4064,7 @@ msgid "Disable Extruder"
msgstr "Disabilita estrusore"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profilo"

View File

@ -3711,7 +3711,7 @@ msgstr "残り時間"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgid "Toggle Full Screen"
msgstr "留め金 フルスクリーン"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
@ -3731,28 +3731,28 @@ msgstr "&やめる"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "3Dビュー (&3)"
msgid "3D View"
msgstr "3Dビュー "
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "フロントビュー (&F)"
msgid "Front View"
msgstr "フロントビュー"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "トップビュー (&T)"
msgid "Top View"
msgstr "トップビュー"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "左サイドビュー (&L)"
msgid "Left Side View"
msgstr "左サイドビュー"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "右サイドビュー (&R)"
msgid "Right Side View"
msgstr "右サイドビュー"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3813,8 +3813,8 @@ msgstr "アバウト..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "&選択したモデルを削除"
# cant enter japanese text
@ -3854,7 +3854,7 @@ msgstr "モデルを非グループ化"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:314
msgctxt "@action:inmenu menubar:edit"
msgid "&Merge Models"
msgstr "&モデルの合体"
msgstr "モ&デルの合体"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:324
msgctxt "@action:inmenu"
@ -3863,17 +3863,17 @@ msgstr "&モデルを増倍する…"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "&すべてのモデル選択"
msgid "Select All Models"
msgstr "すべてのモデル選択"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&ビルドプレート上のクリア"
msgid "Clear Build Plate"
msgstr "ビルドプレート上のクリア"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgid "Reload All Models"
msgstr "すべてのモデルを読み込む"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
@ -3898,7 +3898,7 @@ msgstr "すべてのモデルのポジションをリセットする"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgid "Reset All Model Transformations"
msgstr "すべてのモデル&変更点をリセットする"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
@ -4015,7 +4015,7 @@ msgstr "&ファイル"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:119
msgctxt "@action:inmenu menubar:file"
msgid "&Save Selection to File"
msgstr "&ファイルに選択したものを保存"
msgstr "フ&ァイルに選択したものを保存"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:128
msgctxt "@title:menu menubar:file"
@ -4043,7 +4043,7 @@ msgid "&Settings"
msgstr "&設定"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "&プリンター"
@ -4070,7 +4070,7 @@ msgid "Disable Extruder"
msgstr "エクストルーダーを無効にする"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&プロファイル"

View File

@ -3703,7 +3703,7 @@ msgstr "예상 남은 시간"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgid "Toggle Full Screen"
msgstr "전채 화면 전환"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
@ -3723,28 +3723,28 @@ msgstr "종료(&Q)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "3D 보기(&3)"
msgid "3D View"
msgstr "3D 보기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "앞에서 보기(&F)"
msgid "Front View"
msgstr "앞에서 보기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "위에서 보기(&T)"
msgid "Top View"
msgstr "위에서 보기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "왼쪽에서 보기(&L)"
msgid "Left Side View"
msgstr "왼쪽에서 보기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "오른쪽에서 보기(&R)"
msgid "Right Side View"
msgstr "오른쪽에서 보기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3804,8 +3804,8 @@ msgstr "소개..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "선택한 모델 삭제"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
@ -3852,17 +3852,17 @@ msgstr "모델 복제..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgid "Select All Models"
msgstr "모든 모델 선택"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgid "Clear Build Plate"
msgstr "빌드 플레이트 지우기"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgid "Reload All Models"
msgstr "모든 모델 다시 로드"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
@ -3887,7 +3887,7 @@ msgstr "모든 모델의 위치 재설정"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgid "Reset All Model Transformations"
msgstr "모든 모델의 변환 재설정"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
@ -4032,7 +4032,7 @@ msgid "&Settings"
msgstr "설정"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "프린터"
@ -4059,7 +4059,7 @@ msgid "Disable Extruder"
msgstr "익스트루더 사용하지 않음"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "프로파일"

View File

@ -3705,8 +3705,8 @@ msgstr "Geschatte resterende tijd"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgstr "Vo&lledig Scherm In-/Uitschakelen"
msgid "Toggle Full Screen"
msgstr "Volledig Scherm In-/Uitschakelen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
msgctxt "@action:inmenu menubar:edit"
@ -3725,28 +3725,28 @@ msgstr "&Afsluiten"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "&3D-weergave"
msgid "3D View"
msgstr "3D-weergave"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "Weergave &voorzijde"
msgid "Front View"
msgstr "Weergave voorzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "Weergave &bovenzijde"
msgid "Top View"
msgstr "Weergave bovenzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "Weergave &linkerzijde"
msgid "Left Side View"
msgstr "Weergave linkerzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "Weergave &rechterzijde"
msgid "Right Side View"
msgstr "Weergave rechterzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3806,10 +3806,10 @@ msgstr "&Over..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Ge&selecteerd model verwijderen"
msgstr[1] "Ge&selecteerde modellen verwijderen"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Geselecteerd model verwijderen"
msgstr[1] "Geselecteerde modellen verwijderen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3857,18 +3857,18 @@ msgstr "&Model verveelvoudigen..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "Alle Modellen &Selecteren"
msgid "Select All Models"
msgstr "Alle Modellen Selecteren"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Platform Leegmaken"
msgid "Clear Build Plate"
msgstr "Platform Leegmaken"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgstr "Alle Modellen Opnieuw &Laden"
msgid "Reload All Models"
msgstr "Alle Modellen Opnieuw Laden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
msgctxt "@action:inmenu menubar:edit"
@ -3892,8 +3892,8 @@ msgstr "Alle Modelposities Herstellen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgstr "Alle Model&transformaties Herstellen"
msgid "Reset All Model Transformations"
msgstr "Alle Modeltransformaties Herstellen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
msgctxt "@action:inmenu menubar:file"
@ -4037,7 +4037,7 @@ msgid "&Settings"
msgstr "In&stellingen"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "&Printer"
@ -4064,7 +4064,7 @@ msgid "Disable Extruder"
msgstr "Extruder uitschakelen"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profiel"

View File

@ -3602,7 +3602,7 @@ msgstr "&Pozycja kamery"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:54
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
msgstr "&Pole robocze"
msgstr "P&ole robocze"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:13
msgctxt "@action:inmenu"
@ -3741,7 +3741,7 @@ msgstr "Szacowany czas pozostały"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:78
msgctxt "@action:inmenu"
msgid "Toggle Fu&ll Screen"
msgid "Toggle Full Screen"
msgstr "Przełącz tryb pełnoekranowy"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:85
@ -3761,28 +3761,28 @@ msgstr "&Zamknij"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:113
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
msgstr "&Widok 3D"
msgid "3D View"
msgstr "Widok 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:120
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
msgstr "&Widok z przodu"
msgid "Front View"
msgstr "Widok z przodu"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:127
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
msgstr "&Widok z góry"
msgid "Top View"
msgstr "Widok z góry"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:134
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
msgstr "&Widok z lewej strony"
msgid "Left Side View"
msgstr "Widok z lewej strony"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:141
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
msgstr "&Widok z prawej strony"
msgid "Right Side View"
msgstr "Widok z prawej strony"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:148
msgctxt "@action:inmenu"
@ -3842,10 +3842,10 @@ msgstr "&O..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:232
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:242
msgctxt "@action:inmenu menubar:edit"
msgid "Delete &Selected Model"
msgid_plural "Delete &Selected Models"
msgstr[0] "Usuń &wybrany model"
msgstr[1] "Usuń &wybrane modele"
msgid "Delete Selected Model"
msgid_plural "Delete Selected Models"
msgstr[0] "Usuń wybrany model"
msgstr[1] "Usuń wybrane modele"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:252
msgctxt "@action:inmenu menubar:edit"
@ -3884,7 +3884,7 @@ msgstr "Rozgrupuj modele "
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:314
msgctxt "@action:inmenu menubar:edit"
msgid "&Merge Models"
msgstr "&Połącz modele"
msgstr "Połącz modele"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:324
msgctxt "@action:inmenu"
@ -3893,17 +3893,17 @@ msgstr "&Powiel model..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:331
msgctxt "@action:inmenu menubar:edit"
msgid "&Select All Models"
msgstr "&Wybierz wszystkie modele"
msgid "Select All Models"
msgstr "Wybierz wszystkie modele"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:341
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
msgstr "&Wyczyść stół"
msgid "Clear Build Plate"
msgstr "Wyczyść stół"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
msgid "Reload All Models"
msgstr "Przeładuj wszystkie modele"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:360
@ -3928,7 +3928,7 @@ msgstr "Zresetuj wszystkie pozycje modelu"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:389
msgctxt "@action:inmenu menubar:edit"
msgid "Reset All Model &Transformations"
msgid "Reset All Model Transformations"
msgstr "Zresetuj wszystkie przekształcenia modelu"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:396
@ -4045,7 +4045,7 @@ msgstr "&Plik"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:119
msgctxt "@action:inmenu menubar:file"
msgid "&Save Selection to File"
msgstr "&Zapisz wybór w pliku"
msgstr "Z&apisz wybór w pliku"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:128
msgctxt "@title:menu menubar:file"
@ -4073,7 +4073,7 @@ msgid "&Settings"
msgstr "&Ustawienia"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
msgctxt "@title:menu menubar:toplevel"
msgctxt "@title:menu menubar:settings"
msgid "&Printer"
msgstr "&Drukarka"
@ -4100,7 +4100,7 @@ msgid "Disable Extruder"
msgstr "Wyłącz Ekstruder"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:230
msgctxt "@title:menu"
msgctxt "@title:settings"
msgid "&Profile"
msgstr "&Profil"
@ -4122,7 +4122,7 @@ msgstr "Preferencje"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:289
msgctxt "@title:menu menubar:toplevel"
msgid "&Help"
msgstr "&Pomoc"
msgstr "P&omoc"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:335
msgctxt "@label"

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