Merge branch 'master' of github.com:Ultimaker/Cura

This commit is contained in:
Jaime van Kessel 2018-03-13 16:54:09 +01:00
commit 7b3598e992
38 changed files with 1367 additions and 227 deletions

View File

@ -60,12 +60,13 @@ from cura.Machines.Models.BuildPlateModel import BuildPlateModel
from cura.Machines.Models.NozzleModel import NozzleModel
from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel
from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
from cura.Machines.Models.QualityManagementModel import QualityManagementModel
from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
from cura.Machines.Models.MachineManagementModel import MachineManagementModel
from cura.Machines.MachineErrorChecker import MachineErrorChecker
@ -73,7 +74,6 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
from cura.Machines.VariantManager import VariantManager
from cura.Machines.Models.QualityManagementModel import QualityManagementModel
from . import PlatformPhysics
from . import BuildVolume
@ -90,7 +90,6 @@ from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.UserChangesModel import UserChangesModel
from cura.Settings.ExtrudersModel import ExtrudersModel
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
from cura.Settings.ContainerManager import ContainerManager
from cura.ObjectsModel import ObjectsModel
@ -974,6 +973,7 @@ class CuraApplication(QtApplication):
qmlRegisterType(BrandMaterialsModel, "Cura", 1, 0, "BrandMaterialsModel")
qmlRegisterType(MaterialManagementModel, "Cura", 1, 0, "MaterialManagementModel")
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
"QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)

View File

@ -16,10 +16,11 @@ from cura.Machines.MaterialNode import MaterialNode #For type checking.
# so "generic_abs_ultimaker3", "generic_abs_ultimaker3_AA_0.4", etc.
#
class MaterialGroup:
__slots__ = ("name", "root_material_node", "derived_material_node_list")
__slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
def __init__(self, name: str, root_material_node: MaterialNode):
self.name = name
self.is_read_only = False
self.root_material_node = root_material_node
self.derived_material_node_list = [] #type: List[MaterialNode]

View File

@ -86,6 +86,7 @@ class MaterialManager(QObject):
root_material_id = material_metadata.get("base_file")
if root_material_id not in self._material_group_map:
self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
group = self._material_group_map[root_material_id]
#Store this material in the group of the appropriate root material.
@ -331,6 +332,35 @@ class MaterialManager(QObject):
return material_node
#
# Gets MaterialNode for the given extruder and machine with the given material type.
# Returns None if:
# 1. the given machine doesn't have materials;
# 2. cannot find any material InstanceContainers with the given settings.
#
def getMaterialNodeByType(self, global_stack: "GlobalStack", extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
node = None
machine_definition = global_stack.definition
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
material_diameter = machine_definition.getProperty("material_diameter", "value")
if isinstance(material_diameter, SettingFunction):
material_diameter = material_diameter(global_stack)
# Look at the guid to material dictionary
root_material_id = None
for material_group in self._guid_material_groups_map[material_guid]:
if material_group.is_read_only:
root_material_id = material_group.root_material_node.metadata["id"]
break
if not root_material_id:
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
return None
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
material_diameter, root_material_id)
return node
#
# Used by QualityManager. Built-in quality profiles may be based on generic material IDs such as "generic_pla".
# For materials such as ultimaker_pla_orange, no quality profiles may be found, so we should fall back to use

View File

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

View File

@ -25,7 +25,7 @@ ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
#
# VariantManager is THE place to look for a specific variant. It maintains a variant lookup table with the following
# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
# structure:
#
# [machine_definition_id] -> [variant_type] -> [variant_name] -> ContainerNode(metadata / container)
@ -35,6 +35,9 @@ ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
# -> "BB 0.8"
# -> ...
#
# [machine_definition_id] -> [machine_buildplate_type] -> ContainerNode(metadata / container)
# Example: "ultimaker3" -> "glass" (this is different from the variant name) -> ContainerNode
#
# Note that the "container" field is not loaded in the beginning because it would defeat the purpose of lazy-loading.
# A container is loaded when getVariant() is called to load a variant InstanceContainer.
#
@ -44,6 +47,7 @@ class VariantManager:
self._container_registry = container_registry # type: ContainerRegistry
self._machine_to_variant_dict_map = dict() # <machine_type> -> <variant_dict>
self._machine_to_buildplate_dict_map = dict()
self._exclude_variant_id_list = ["empty_variant"]
@ -53,6 +57,7 @@ class VariantManager:
#
def initialize(self):
self._machine_to_variant_dict_map = OrderedDict()
self._machine_to_buildplate_dict_map = OrderedDict()
# Cache all variants from the container registry to a variant map for better searching and organization.
variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
@ -78,6 +83,22 @@ class VariantManager:
variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
# If the variant is a buildplate then fill also the buildplate map
if variant_type == VariantType.BUILD_PLATE:
if variant_definition not in self._machine_to_buildplate_dict_map:
self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])
if not variant_container:
# ERROR: not variant container. This should never happen
raise RuntimeError("Not variant found [%s], type [%s] for machine [%s]" %
(variant_name, variant_type, variant_definition))
buildplate_type = variant_container[0].getProperty("machine_buildplate_type", "value")
if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()
self._machine_to_buildplate_dict_map[variant_definition][buildplate_type] = variant_dict[variant_name]
#
# Gets the variant InstanceContainer with the given information.
# Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present.
@ -117,3 +138,8 @@ class VariantManager:
if preferred_variant_name:
node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
return node
def getBuildplateVariantNode(self, machine_definition_id: str, buildplate_type: str) -> Optional["ContainerNode"]:
if machine_definition_id in self._machine_to_buildplate_dict_map:
return self._machine_to_buildplate_dict_map[machine_definition_id].get(buildplate_type)
return None

View File

@ -0,0 +1,81 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
from typing import List
MYPY = False
if MYPY:
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
class ConfigurationModel(QObject):
configurationChanged = pyqtSignal()
def __init__(self):
super().__init__()
self._printer_type = None
self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
self._buildplate_configuration = None
def setPrinterType(self, printer_type):
self._printer_type = printer_type
@pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
def printerType(self):
return self._printer_type
def setExtruderConfigurations(self, extruder_configurations):
self._extruder_configurations = extruder_configurations
@pyqtProperty("QVariantList", fset = setExtruderConfigurations, notify = configurationChanged)
def extruderConfigurations(self):
return self._extruder_configurations
def setBuildplateConfiguration(self, buildplate_configuration):
self._buildplate_configuration = buildplate_configuration
@pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
def buildplateConfiguration(self):
return self._buildplate_configuration
## This method is intended to indicate whether the configuration is valid or not.
# The method checks if the mandatory fields are or not set
def isValid(self):
if not self._extruder_configurations:
return False
for configuration in self._extruder_configurations:
if configuration is None:
return False
return self._printer_type is not None
def __str__(self):
message_chunks = []
message_chunks.append("Printer type: " + self._printer_type)
message_chunks.append("Extruders: [")
for configuration in self._extruder_configurations:
message_chunks.append(" " + str(configuration))
message_chunks.append("]")
if self._buildplate_configuration is not None:
message_chunks.append("Buildplate: " + self._buildplate_configuration)
return "\n".join(message_chunks)
def __eq__(self, other):
return hash(self) == hash(other)
## The hash function is used to compare and create unique sets. The configuration is unique if the configuration
# of the extruders is unique (the order of the extruders matters), and the type and buildplate is the same.
def __hash__(self):
extruder_hash = hash(0)
first_extruder = None
for configuration in self._extruder_configurations:
extruder_hash ^= hash(configuration)
if configuration.position == 0:
first_extruder = configuration
# To ensure the correct order of the extruders, we add an "and" operation using the first extruder hash value
if first_extruder:
extruder_hash &= hash(first_extruder)
return hash(self._printer_type) ^ extruder_hash ^ hash(self._buildplate_configuration)

View File

@ -0,0 +1,59 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
class ExtruderConfigurationModel(QObject):
extruderConfigurationChanged = pyqtSignal()
def __init__(self):
super().__init__()
self._position = -1
self._material = None
self._hotend_id = None
def setPosition(self, position):
self._position = position
@pyqtProperty(int, fset = setPosition, notify = extruderConfigurationChanged)
def position(self):
return self._position
def setMaterial(self, material):
self._material = material
@pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged)
def material(self):
return self._material
def setHotendID(self, hotend_id):
self._hotend_id = hotend_id
@pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged)
def hotendID(self):
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):
return True
def __str__(self):
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("-")
message_chunks.append("HotendID: " + self.hotendID if self.hotendID else "empty")
return " ".join(message_chunks)
def __eq__(self, other):
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
# unique within a set
def __hash__(self):
return hash(self._position) ^ (hash(self._material.guid) if self._material is not None else hash(0)) ^ hash(self._hotend_id)

View File

@ -1,8 +1,8 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot
from UM.Logger import Logger
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from typing import Optional
@ -17,14 +17,18 @@ class ExtruderOutputModel(QObject):
targetHotendTemperatureChanged = pyqtSignal()
hotendTemperatureChanged = pyqtSignal()
activeMaterialChanged = pyqtSignal()
extruderConfigurationChanged = pyqtSignal()
def __init__(self, printer: "PrinterOutputModel", parent=None):
def __init__(self, printer: "PrinterOutputModel", position, parent=None):
super().__init__(parent)
self._printer = printer
self._position = position
self._target_hotend_temperature = 0
self._hotend_temperature = 0
self._hotend_id = ""
self._active_material = None # type: Optional[MaterialOutputModel]
self._extruder_configuration = ExtruderConfigurationModel()
self._extruder_configuration.position = self._position
@pyqtProperty(QObject, notify = activeMaterialChanged)
def activeMaterial(self) -> "MaterialOutputModel":
@ -33,7 +37,9 @@ class ExtruderOutputModel(QObject):
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()
## Update the hotend temperature. This only changes it locally.
def updateHotendTemperature(self, temperature: float):
@ -56,7 +62,7 @@ class ExtruderOutputModel(QObject):
def targetHotendTemperature(self) -> float:
return self._target_hotend_temperature
@pyqtProperty(float, notify=hotendTemperatureChanged)
@pyqtProperty(float, notify = hotendTemperatureChanged)
def hotendTemperature(self) -> float:
return self._hotend_temperature
@ -67,4 +73,12 @@ class ExtruderOutputModel(QObject):
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()
@pyqtProperty(QObject, notify = extruderConfigurationChanged)
def extruderConfiguration(self):
if self._extruder_configuration.isValid():
return self._extruder_configuration
return None

View File

@ -6,7 +6,7 @@ from UM.Logger import Logger
MYPY = False
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.ExtruderOuputModel import ExtruderOuputModel
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
@ -18,7 +18,7 @@ class PrinterOutputController:
self.can_control_manually = True
self._output_device = output_device
def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOuputModel", temperature: int):
def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOutputModel", temperature: int):
Logger.log("w", "Set target hotend temperature not implemented in controller")
def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):

View File

@ -1,11 +1,11 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot
from UM.Logger import Logger
from typing import Optional, List
from typing import Optional
from UM.Math.Vector import Vector
from cura.PrinterOutput.ExtruderOuputModel import ExtruderOutputModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
MYPY = False
if MYPY:
@ -22,8 +22,10 @@ class PrinterOutputModel(QObject):
nameChanged = pyqtSignal()
headPositionChanged = pyqtSignal()
keyChanged = pyqtSignal()
typeChanged = pyqtSignal()
printerTypeChanged = pyqtSignal()
buildplateChanged = pyqtSignal()
cameraChanged = pyqtSignal()
configurationChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = ""):
super().__init__(parent)
@ -32,13 +34,18 @@ class PrinterOutputModel(QObject):
self._name = ""
self._key = "" # Unique identifier
self._controller = output_controller
self._extruders = [ExtruderOutputModel(printer=self) for i in range(number_of_extruders)]
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._head_position = Vector(0, 0, 0)
self._active_print_job = None # type: Optional[PrintJobOutputModel]
self._firmware_version = firmware_version
self._printer_state = "unknown"
self._is_preheating = False
self._type = ""
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._camera = None
@ -64,14 +71,27 @@ class PrinterOutputModel(QObject):
def camera(self):
return self._camera
@pyqtProperty(str, notify = typeChanged)
@pyqtProperty(str, notify = printerTypeChanged)
def type(self):
return self._type
return self._printer_type
def updateType(self, type):
if self._type != type:
self._type = type
self.typeChanged.emit()
def updateType(self, printer_type):
if self._printer_type != printer_type:
self._printer_type = printer_type
self._printer_configuration.printerType = self._printer_type
self.printerTypeChanged.emit()
self.configurationChanged.emit()
@pyqtProperty(str, notify = buildplateChanged)
def buildplate(self):
return self._buildplate_name
def updateBuildplateName(self, buildplate_name):
if self._buildplate_name != buildplate_name:
self._buildplate_name = buildplate_name
self._printer_configuration.buildplateConfiguration = self._buildplate_name
self.buildplateChanged.emit()
self.configurationChanged.emit()
@pyqtProperty(str, notify=keyChanged)
def key(self):
@ -238,3 +258,14 @@ class PrinterOutputModel(QObject):
if self._controller:
return self._controller.can_control_manually
return False
# Returns the configuration (material, variant and buildplate) of the current printer
@pyqtProperty(QObject, notify = configurationChanged)
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()

View File

@ -1,12 +1,11 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.i18n import i18nCatalog
from UM.OutputDevice.OutputDevice import OutputDevice
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal, QVariant
from PyQt5.QtWidgets import QMessageBox
from UM.Logger import Logger
from UM.Signal import signalemitter
from UM.Application import Application
@ -17,6 +16,7 @@ from typing import List, Optional
MYPY = False
if MYPY:
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
i18n_catalog = i18nCatalog("cura")
@ -44,10 +44,14 @@ class PrinterOutputDevice(QObject, OutputDevice):
# Signal to indicate that the info text about the connection has changed.
connectionTextChanged = pyqtSignal()
# Signal to indicate that the configuration of one of the printers has changed.
uniqueConfigurationsChanged = pyqtSignal()
def __init__(self, device_id, parent = None):
super().__init__(device_id = device_id, parent = parent)
self._printers = [] # type: List[PrinterOutputModel]
self._unique_configurations = [] # type: List[ConfigurationModel]
self._monitor_view_qml_path = ""
self._monitor_component = None
@ -69,6 +73,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._address = ""
self._connection_text = ""
self.printersChanged.connect(self._onPrintersChanged)
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)
@pyqtProperty(str, notify = connectionTextChanged)
def address(self):
@ -175,6 +181,23 @@ class PrinterOutputDevice(QObject, OutputDevice):
self.acceptsCommandsChanged.emit()
# Returns the unique configurations of the printers within this output device
@pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
def uniqueConfigurations(self):
return self._unique_configurations
def _updateUniqueConfigurations(self):
self._unique_configurations = list(set([printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None]))
self._unique_configurations.sort(key = lambda k: k.printerType)
self.uniqueConfigurationsChanged.emit()
def _onPrintersChanged(self):
for printer in self._printers:
printer.configurationChanged.connect(self._updateUniqueConfigurations)
# At this point there may be non-updated configurations
self._updateUniqueConfigurations()
## The current processing state of the backend.
class ConnectionState(IntEnum):

View File

@ -24,8 +24,11 @@ from UM.Settings.SettingFunction import SettingFunction
from UM.Signal import postponeSignals, CompressTechnique
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.Machines.VariantManager import VariantType
from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.Settings.ExtruderManager import ExtruderManager
from .CuraStackBuilder import CuraStackBuilder
@ -103,6 +106,12 @@ class MachineManager(QObject):
# There might already be some output devices by the time the signal is connected
self._onOutputDevicesChanged()
self._current_printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
self.activeMaterialChanged.connect(self._onCurrentConfigurationChanged)
self.activeVariantChanged.connect(self._onCurrentConfigurationChanged)
# Force to compute the current configuration
self._onCurrentConfigurationChanged()
self._application.callLater(self.setInitialActiveMachine)
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
@ -113,7 +122,8 @@ class MachineManager(QObject):
if containers:
containers[0].nameChanged.connect(self._onMaterialNameChanged)
self._material_manager = self._application._material_manager
self._material_manager = self._application.getMaterialManager()
self._variant_manager = self._application.getVariantManager()
self._quality_manager = self._application.getQualityManager()
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
@ -139,6 +149,7 @@ class MachineManager(QObject):
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
outputDevicesChanged = pyqtSignal()
currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
rootMaterialChanged = pyqtSignal()
@ -158,6 +169,39 @@ class MachineManager(QObject):
self.outputDevicesChanged.emit()
@pyqtProperty(QObject, notify = currentConfigurationChanged)
def currentConfiguration(self):
return self._current_printer_configuration
def _onCurrentConfigurationChanged(self) -> None:
if not self._global_container_stack:
return
# Create the configuration model with the current data in Cura
self._current_printer_configuration.printerType = self._global_container_stack.definition.getName()
self._current_printer_configuration.extruderConfigurations = []
for extruder in self._global_container_stack.extruders.values():
extruder_configuration = ExtruderConfigurationModel()
# For compare just the GUID is needed at this moment
mat_type = extruder.material.getMetaDataEntry("material") if extruder.material != self._empty_material_container else None
mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != self._empty_material_container else None
mat_color = extruder.material.getMetaDataEntry("color_name") if extruder.material != self._empty_material_container else None
mat_brand = extruder.material.getMetaDataEntry("brand") if extruder.material != self._empty_material_container else None
mat_name = extruder.material.getMetaDataEntry("name") if extruder.material != self._empty_material_container else None
material_model = MaterialOutputModel(mat_guid, mat_type, mat_color, mat_brand, mat_name)
extruder_configuration.position = int(extruder.getMetaDataEntry("position"))
extruder_configuration.material = material_model
extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != self._empty_variant_container else None
self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != self._empty_variant_container else None
self.currentConfigurationChanged.emit()
@pyqtSlot(QObject, result = bool)
def matchesConfiguration(self, configuration: ConfigurationModel) -> bool:
return self._current_printer_configuration == configuration
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
def printerOutputDevices(self):
return self._printer_output_devices
@ -292,9 +336,13 @@ class MachineManager(QObject):
self.__emitChangedSignals()
## Given a definition id, return the machine with this id.
# Optional: add a list of keys and values to filter the list of machines with the given definition id
# \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) -> Optional["GlobalStack"]:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
def getMachine(definition_id: str, metadata_filter: Dict[str, str] = None) -> Optional["GlobalStack"]:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for machine in machines:
if machine.definition.getId() == definition_id:
return machine
@ -399,6 +447,12 @@ class MachineManager(QObject):
def stacksHaveErrors(self) -> bool:
return bool(self._stacks_have_errors)
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineDefinitionName(self) -> str:
if self._global_container_stack:
return self._global_container_stack.definition.getName()
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineName(self) -> str:
if self._global_container_stack:
@ -411,6 +465,18 @@ class MachineManager(QObject):
return self._global_container_stack.getId()
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineNetworkKey(self) -> str:
if self._global_container_stack:
return self._global_container_stack.getMetaDataEntry("um_network_key")
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineNetworkGroupName(self) -> str:
if self._global_container_stack:
return self._global_container_stack.getMetaDataEntry("connect_group_name")
return ""
@pyqtProperty(QObject, notify = globalContainerChanged)
def activeMachine(self) -> Optional["GlobalStack"]:
return self._global_container_stack
@ -1062,6 +1128,69 @@ class MachineManager(QObject):
self._setMaterial(position, new_material)
continue
## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new
# instance with the same network key.
@pyqtSlot(str)
def switchPrinterType(self, machine_name):
# Don't switch if the user tries to change to the same type of printer
if self.activeMachineDefinitionName == machine_name:
return
# Get the definition id corresponding to this machine name
machine_definition_id = ContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
# Try to find a machine with the same network key
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey})
# If there is no machine, then create a new one and set it to the non-hidden instance
if not new_machine:
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
new_machine.addMetaDataEntry("um_network_key", self.activeMachineNetworkKey)
new_machine.addMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName)
new_machine.addMetaDataEntry("hidden", False)
else:
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey)
new_machine.setMetaDataEntry("hidden", False)
# Set the current printer instance to hidden (the metadata entry must exist)
self._global_container_stack.setMetaDataEntry("hidden", True)
self.setActiveMachine(new_machine.getId())
@pyqtSlot(QObject)
def applyRemoteConfiguration(self, configuration: ConfigurationModel):
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.switchPrinterType(configuration.printerType)
for extruder_configuration in configuration.extruderConfigurations:
position = str(extruder_configuration.position)
variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack, extruder_configuration.hotendID,extruder_configuration.material.guid)
if variant_container_node:
self._setVariantNode(position, variant_container_node)
else:
self._global_container_stack.extruders[position].variant = self._empty_variant_container
if material_container_node:
self._setMaterial(position, material_container_node)
else:
self._global_container_stack.extruders[position].material = self._empty_material_container
self._updateMaterialWithVariant(position)
if configuration.buildplateConfiguration is not None:
global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
if global_variant_container_node:
self._setGlobalVariant(global_variant_container_node)
else:
self._global_container_stack.variant = self._empty_variant_container
else:
self._global_container_stack.variant = self._empty_variant_container
self._updateQualityWithMaterial()
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
def replaceContainersMetadata(self, key: str, value: str, new_value: str):
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
for machine in machines:
if machine.getMetaDataEntry(key) == value:
machine.setMetaDataEntry(key, new_value)
@pyqtSlot("QVariant")
def setGlobalVariant(self, container_node):
self.blurSettings.emit()

View File

@ -22,14 +22,7 @@ class MonitorStage(CuraStage):
def _setActivePrintJob(self, print_job):
if self._active_print_job != print_job:
if self._active_print_job:
self._active_print_job.stateChanged.disconnect(self._updateIconSource)
self._active_print_job = print_job
if self._active_print_job:
self._active_print_job.stateChanged.connect(self._updateIconSource)
# Ensure that the right icon source is returned.
self._updateIconSource()
def _setActivePrinter(self, printer):
if self._active_printer != printer:
@ -43,9 +36,6 @@ class MonitorStage(CuraStage):
else:
self._setActivePrintJob(None)
# Ensure that the right icon source is returned.
self._updateIconSource()
def _onActivePrintJobChanged(self):
self._setActivePrintJob(self._active_printer.activePrintJob)
@ -58,22 +48,13 @@ class MonitorStage(CuraStage):
new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
if new_output_device != self._printer_output_device:
if self._printer_output_device:
self._printer_output_device.acceptsCommandsChanged.disconnect(self._updateIconSource)
self._printer_output_device.connectionStateChanged.disconnect(self._updateIconSource)
self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged)
self._printer_output_device = new_output_device
self._printer_output_device.acceptsCommandsChanged.connect(self._updateIconSource)
self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged)
self._printer_output_device.connectionStateChanged.connect(self._updateIconSource)
self._setActivePrinter(self._printer_output_device.activePrinter)
# Force an update of the icon source
self._updateIconSource()
except IndexError:
#If index error occurs, then the icon on monitor button also should be updated
self._updateIconSource()
pass
def _onEngineCreated(self):
@ -82,7 +63,6 @@ class MonitorStage(CuraStage):
self._onOutputDevicesChanged()
self._updateMainOverlay()
self._updateSidebar()
self._updateIconSource()
def _updateMainOverlay(self):
main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml")
@ -92,46 +72,3 @@ class MonitorStage(CuraStage):
# TODO: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor!
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml")
self.addDisplayComponent("sidebar", sidebar_component_path)
def _updateIconSource(self):
if Application.getInstance().getTheme() is not None:
icon_name = self._getActiveOutputDeviceStatusIcon()
self.setIconSource(Application.getInstance().getTheme().getIcon(icon_name))
## Find the correct status icon depending on the active output device state
def _getActiveOutputDeviceStatusIcon(self):
# We assume that you are monitoring the device with the highest priority.
try:
output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
except IndexError:
return "tab_status_unknown"
if not output_device.acceptsCommands:
return "tab_status_unknown"
if output_device.activePrinter is None:
return "tab_status_connected"
# TODO: refactor to use enum instead of hardcoded strings?
if output_device.activePrinter.state == "maintenance":
return "tab_status_busy"
if output_device.activePrinter.activePrintJob is None:
return "tab_status_connected"
if output_device.activePrinter.activePrintJob.state in ["printing", "pre_print", "pausing", "resuming"]:
return "tab_status_busy"
if output_device.activePrinter.activePrintJob.state == "wait_cleanup":
return "tab_status_finished"
if output_device.activePrinter.activePrintJob.state in ["ready", ""]:
return "tab_status_connected"
if output_device.activePrinter.activePrintJob.state == "paused":
return "tab_status_paused"
if output_device.activePrinter.activePrintJob.state == "error":
return "tab_status_stopped"
return "tab_status_unknown"

View File

@ -376,10 +376,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# 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.
self._printer_uuid_to_unique_name_mapping[data["uuid"]] = data["unique_name"]
machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"])[0]
printer.updateName(data["friendly_name"])
printer.updateKey(data["uuid"])
printer.updateType(data["machine_variant"])
# Do not store the buildplate information that comes from connect if the current printer has not buildplate information
if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False):
printer.updateBuildplateName(data["build_plate"]["type"])
if not data["enabled"]:
printer.updateState("disabled")
else:

View File

@ -97,6 +97,25 @@ class DiscoverUM3Action(MachineAction):
else:
return []
@pyqtSlot(str)
def setGroupName(self, group_name):
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "connect_group_name" in meta_data:
previous_connect_group_name = meta_data["connect_group_name"]
global_container_stack.setMetaDataEntry("connect_group_name", group_name)
# Find all the places where there is the same group name and change it accordingly
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
else:
global_container_stack.addMetaDataEntry("connect_group_name", group_name)
global_container_stack.addMetaDataEntry("hidden", False)
if self._network_plugin:
# Ensure that the connection states are refreshed.
self._network_plugin.reCheckConnections()
@pyqtSlot(str)
def setKey(self, key):
Logger.log("d", "Attempting to set the network key of the active machine to %s", key)
@ -104,11 +123,13 @@ class DiscoverUM3Action(MachineAction):
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data:
previous_network_key= meta_data["um_network_key"]
global_container_stack.setMetaDataEntry("um_network_key", key)
# Delete old authentication data.
Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), key)
global_container_stack.removeMetaDataEntry("network_authentication_id")
global_container_stack.removeMetaDataEntry("network_authentication_key")
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
else:
global_container_stack.addMetaDataEntry("um_network_key", key)

View File

@ -32,10 +32,12 @@ Cura.MachineAction
if(base.selectedDevice && base.completeProperties)
{
var printerKey = base.selectedDevice.key
var printerName = base.selectedDevice.name // TODO To change when the groups have a name
if(manager.getStoredKey() != printerKey)
{
manager.setKey(printerKey);
completed();
manager.setKey(printerKey)
manager.setGroupName(printerName) // TODO To change when the groups have a name
completed()
}
}
}
@ -303,7 +305,7 @@ Cura.MachineAction
Button
{
text: catalog.i18nc("@action:button", "Connect")
enabled: (base.selectedDevice && base.completeProperties) ? true : false
enabled: (base.selectedDevice && base.completeProperties && base.selectedDevice.clusterSize > 0) ? true : false
onClicked: connectToPrinter()
}
}

View File

@ -215,7 +215,7 @@
{
"label": "Number of Extruders that are enabled",
"description": "Number of extruder trains that are enabled; automatically set in software",
"default_value": "machine_extruder_count",
"value": "machine_extruder_count",
"minimum_value": "1",
"maximum_value": "16",
"type": "int",

View File

@ -10,35 +10,30 @@ import UM 1.2 as UM
import Cura 1.0 as Cura
import "Menus"
ToolButton
{
text: Cura.MachineManager.activeMachineName
ToolButton {
id: base
property var isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
property var printerStatus: Cura.MachineManager.printerOutputDevices.length != 0 ? "connected" : "disconnected"
text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName
tooltip: Cura.MachineManager.activeMachineName
style: ButtonStyle
{
background: Rectangle
{
color:
{
if(control.pressed)
{
style: ButtonStyle {
background: Rectangle {
color: {
if (control.pressed) {
return UM.Theme.getColor("sidebar_header_active");
}
else if(control.hovered)
{
else if (control.hovered) {
return UM.Theme.getColor("sidebar_header_hover");
}
else
{
else {
return UM.Theme.getColor("sidebar_header_bar");
}
}
Behavior on color { ColorAnimation { duration: 50; } }
UM.RecolorImage
{
UM.RecolorImage {
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
@ -50,22 +45,43 @@ ToolButton
color: UM.Theme.getColor("text_emphasis")
source: UM.Theme.getIcon("arrow_bottom")
}
Label
{
PrinterStatusIcon {
id: printerStatusIcon
visible: isNetworkPrinter
status: printerStatus
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: UM.Theme.getSize("sidebar_margin").width
}
}
Label {
id: sidebarComboBoxLabel
color: UM.Theme.getColor("sidebar_header_text_active")
text: control.text;
elide: Text.ElideRight;
anchors.left: parent.left;
anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2
anchors.left: isNetworkPrinter ? printerStatusIcon.right : parent.left;
anchors.leftMargin: isNetworkPrinter ? UM.Theme.getSize("sidebar_lining").width : UM.Theme.getSize("sidebar_margin").width
anchors.right: downArrow.left;
anchors.rightMargin: control.rightMargin;
anchors.verticalCenter: parent.verticalCenter;
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("medium_bold")
}
}
label: Label {}
}
menu: PrinterMenu { }
// Make the toolbutton react when the outputdevice changes
Connections
{
target: Cura.MachineManager
onOutputDevicesChanged:
{
base.isNetworkPrinter = Cura.MachineManager.activeMachineNetworkKey != ""
}
}
}

View File

@ -0,0 +1,124 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.0
import UM 1.2 as UM
import Cura 1.0 as Cura
Rectangle
{
id: configurationItem
property var configuration: null
property var selected: false
signal activateConfiguration()
height: childrenRect.height
border.width: UM.Theme.getSize("default_lining").width
border.color: updateBorderColor()
color: selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item")
property var textColor: selected ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text")
function updateBorderColor()
{
border.color = selected ? UM.Theme.getColor("configuration_item_border_active") : UM.Theme.getColor("configuration_item_border")
}
Column
{
id: contentColumn
width: parent.width
padding: UM.Theme.getSize("default_margin").width
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
Row
{
id: extruderRow
width: parent.width - 2 * parent.padding
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
Repeater
{
id: repeater
height: childrenRect.height
model: configuration.extruderConfigurations
delegate: PrintCoreConfiguration
{
width: Math.round(parent.width / 2)
printCoreConfiguration: modelData
mainColor: textColor
}
}
}
//Buildplate row separator
Rectangle
{
id: separator
visible: buildplateInformation.visible
width: parent.width - 2 * parent.padding
height: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
color: textColor
}
Item
{
id: buildplateInformation
width: parent.width - 2 * parent.padding
height: childrenRect.height
visible: configuration.buildplateConfiguration != ""
UM.RecolorImage {
id: buildplateIcon
anchors.left: parent.left
width: UM.Theme.getSize("topbar_button_icon").width
height: UM.Theme.getSize("topbar_button_icon").height
sourceSize.width: width
sourceSize.height: height
source: UM.Theme.getIcon("buildplate")
color: textColor
}
Label
{
id: buildplateLabel
anchors.left: buildplateIcon.right
anchors.verticalCenter: buildplateIcon.verticalCenter
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2)
text: configuration.buildplateConfiguration
color: textColor
}
}
}
MouseArea
{
id: mouse
anchors.fill: parent
onClicked: activateConfiguration()
hoverEnabled: true
onEntered: parent.border.color = UM.Theme.getColor("configuration_item_border_hover")
onExited: updateBorderColor()
}
Connections
{
target: Cura.MachineManager
onCurrentConfigurationChanged: {
configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration)
updateBorderColor()
}
}
Component.onCompleted:
{
configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration)
updateBorderColor()
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Column
{
id: base
property var outputDevice: null
property var computedHeight: container.height + configurationListHeading.height + 3 * padding
height: childrenRect.height + 2 * padding
padding: UM.Theme.getSize("default_margin").width
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
Label
{
id: configurationListHeading
text: catalog.i18nc("@label:header configurations", "Available configurations")
font: UM.Theme.getFont("large")
width: parent.width - 2 * parent.padding
}
Component
{
id: sectionHeading
Rectangle
{
height: childrenRect.height + UM.Theme.getSize("default_margin").height
Label
{
text: section
font: UM.Theme.getFont("default_bold")
}
}
}
ScrollView
{
id: container
width: parent.width - parent.padding
height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor)
style: UM.Theme.styles.scrollview
__wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event
ListView
{
id: configurationList
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
width: container.width
contentHeight: childrenRect.height
section.property: "modelData.printerType"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
model: (ouputDevice != null) ? outputDevice.uniqueConfigurations : []
delegate: ConfigurationItem
{
width: parent.width - UM.Theme.getSize("default_margin").width
configuration: modelData
onActivateConfiguration:
{
Cura.MachineManager.applyRemoteConfiguration(configuration)
}
}
}
}
Connections
{
target: outputDevice
onUniqueConfigurationsChanged:
{
// FIXME For now the model should be removed and then created again, otherwise changes in the printer don't automatically update the UI
configurationList.model = []
configurationList.model = outputDevice.uniqueConfigurations
}
}
}

View File

@ -0,0 +1,66 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Item
{
id: configurationSelector
property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
property var panelWidth: control.width
property var panelVisible: false
SyncButton {
onClicked: configurationSelector.state == "open" ? configurationSelector.state = "closed" : configurationSelector.state = "open"
outputDevice: connectedDevice
}
Popup {
id: popup
clip: true
y: configurationSelector.height - UM.Theme.getSize("default_lining").height
x: configurationSelector.width - width
width: panelWidth
visible: panelVisible && connectedDevice != null
padding: UM.Theme.getSize("default_lining").width
contentItem: ConfigurationListView {
id: configList
width: panelWidth - 2 * popup.padding
outputDevice: connectedDevice
}
background: Rectangle {
color: UM.Theme.getColor("setting_control")
border.color: UM.Theme.getColor("setting_control_border")
}
}
states: [
// This adds a second state to the container where the rectangle is farther to the right
State {
name: "open"
PropertyChanges {
target: popup
height: configList.computedHeight
}
},
State {
name: "closed"
PropertyChanges {
target: popup
height: 0
}
}
]
transitions: [
// This adds a transition that defaults to applying to all state changes
Transition {
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
NumberAnimation { properties: "height"; duration: 200; easing.type: Easing.InOutQuad; }
}
]
}

View File

@ -0,0 +1,87 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.0
import UM 1.2 as UM
Column
{
id: extruderInfo
property var printCoreConfiguration
property var mainColor: "black"
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
height: childrenRect.height
Item
{
id: extruder
width: parent.width
height: childrenRect.height
Label
{
id: extruderLabel
text: catalog.i18nc("@label:extruder label", "Extruder")
elide: Text.ElideRight
anchors.left: parent.left
font: UM.Theme.getFont("default")
color: mainColor
}
// Rounded item to show the extruder number
Item
{
id: extruderIconItem
anchors.verticalCenter: extruderLabel.verticalCenter
anchors.left: extruderLabel.right
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
width: UM.Theme.getSize("section_icon").width
height: UM.Theme.getSize("section_icon").height
UM.RecolorImage {
id: mainCircle
anchors.fill: parent
anchors.centerIn: parent
sourceSize.width: parent.width
sourceSize.height: parent.height
source: UM.Theme.getIcon("extruder_button")
color: mainColor
}
Label
{
id: extruderNumberText
anchors.centerIn: parent
text: printCoreConfiguration.position + 1
font: UM.Theme.getFont("default")
color: mainColor
}
}
}
Label
{
id: materialLabel
text: printCoreConfiguration.material.name
elide: Text.ElideRight
width: parent.width
font: UM.Theme.getFont("default_bold")
color: mainColor
}
Label
{
id: printCoreTypeLabel
text: printCoreConfiguration.hotendID
elide: Text.ElideRight
width: parent.width
font: UM.Theme.getFont("default")
color: mainColor
}
}

View File

@ -0,0 +1,103 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Button
{
id: base
property var outputDevice: null
property var matched: updateOnSync()
text: matched == true ? "Yes" : "No"
width: parent.width
height: parent.height
function updateOnSync() {
if (outputDevice != undefined) {
for (var index in outputDevice.uniqueConfigurations) {
var configuration = outputDevice.uniqueConfigurations[index]
if (Cura.MachineManager.matchesConfiguration(configuration)) {
base.matched = true;
return;
}
}
}
base.matched = false;
}
style: ButtonStyle
{
background: Rectangle
{
color:
{
if(control.pressed)
{
return UM.Theme.getColor("sidebar_header_active");
}
else if(control.hovered)
{
return UM.Theme.getColor("sidebar_header_hover");
}
else
{
return UM.Theme.getColor("sidebar_header_bar");
}
}
Behavior on color { ColorAnimation { duration: 50; } }
UM.RecolorImage
{
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
sourceSize.width: width
sourceSize.height: height
color: UM.Theme.getColor("text_emphasis")
source: UM.Theme.getIcon("arrow_bottom")
}
UM.RecolorImage {
id: sidebarComboBoxLabel
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.verticalCenter;
width: UM.Theme.getSize("printer_sync_icon").width
height: UM.Theme.getSize("printer_sync_icon").height
color: control.matched ? UM.Theme.getColor("printer_config_matched") : UM.Theme.getColor("printer_config_mismatch")
source: UM.Theme.getIcon("tab_status_connected")
sourceSize.width: width
sourceSize.height: height
}
}
label: Label {}
}
onClicked:
{
panelVisible = !panelVisible
}
Connections {
target: outputDevice
onUniqueConfigurationsChanged: {
updateOnSync()
}
}
Connections {
target: Cura.MachineManager
onCurrentConfigurationChanged: {
updateOnSync()
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Instantiator {
model: UM.ContainerStacksModel {
filter: {"type": "machine", "um_network_key": null}
}
MenuItem {
text: model.name;
checkable: true;
checked: Cura.MachineManager.activeMachineId == model.id
exclusiveGroup: group;
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Instantiator {
model: UM.ContainerStacksModel {
filter: {"type": "machine", "um_network_key": "*", "hidden": "False"}
}
MenuItem {
// TODO: Use printer_group icon when it's a cluster. Not use it for now since it doesn't look as expected
// iconSource: UM.Theme.getIcon("printer_single")
text: model.metadata["connect_group_name"]
checkable: true;
checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"]
exclusiveGroup: group;
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}

View File

@ -1,37 +1,60 @@
// Copyright (c) 2016 Ultimaker B.V.
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Menu
{
id: menu;
id: menu
// TODO Enable custom style to the menu
// style: MenuStyle
// {
// frame: Rectangle
// {
// color: "white"
// }
// }
Instantiator
MenuItem
{
model: UM.ContainerStacksModel
{
filter: {"type": "machine"}
}
MenuItem
{
text: model.name;
checkable: true;
checked: Cura.MachineManager.activeMachineId == model.id
exclusiveGroup: group;
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
text: catalog.i18nc("@label:category menu label", "Network enabled printers")
enabled: false
visible: networkPrinterMenu.count > 0
}
NetworkPrinterMenu
{
id: networkPrinterMenu
}
MenuSeparator
{
visible: networkPrinterMenu.count > 0
}
MenuItem
{
text: catalog.i18nc("@label:category menu label", "Local printers")
enabled: false
visible: localPrinterMenu.count > 0
}
LocalPrinterMenu
{
id: localPrinterMenu
}
ExclusiveGroup { id: group; }
MenuSeparator { }
MenuSeparator
{
visible: localPrinterMenu.count > 0
}
MenuItem { action: Cura.Actions.addMachine; }
MenuItem { action: Cura.Actions.configureMachines; }

View File

@ -0,0 +1,27 @@
// Copyright (c) 2017 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import UM 1.2 as UM
import Cura 1.0 as Cura
Item
{
property var status: "disconnected"
width: childrenRect.width
height: childrenRect.height
UM.RecolorImage
{
id: statusIcon
width: UM.Theme.getSize("printer_status_icon").width
height: UM.Theme.getSize("printer_status_icon").height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.getColor("tab_status_" + parent.status)
source: UM.Theme.getIcon(parent.status)
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
Menu
{
id: menu
title: "Printer type"
property var outputDevice: Cura.MachineManager.printerOutputDevices[0]
Instantiator
{
id: printerTypeInstantiator
model: outputDevice != null ? outputDevice.connectedPrintersTypeCount : []
MenuItem
{
text: modelData.machine_type
checkable: true
checked: Cura.MachineManager.activeMachineDefinitionName == modelData.machine_type
exclusiveGroup: group
onTriggered:
{
Cura.MachineManager.switchPrinterType(modelData.machine_type)
}
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
ExclusiveGroup { id: group }
}

View File

@ -14,10 +14,7 @@ UM.ManagementPage
id: base;
title: catalog.i18nc("@title:tab", "Printers");
model: UM.ContainerStacksModel
{
filter: {"type": "machine"}
}
model: Cura.MachineManagementModel { }
activeId: Cura.MachineManager.activeMachineId
activeIndex: activeMachineIndex()
@ -57,7 +54,7 @@ UM.ManagementPage
{
text: catalog.i18nc("@action:button", "Rename");
iconName: "edit-rename";
enabled: base.currentItem != null
enabled: base.currentItem != null && base.currentItem.metadata.connect_group_name == null
onClicked: renameDialog.open();
}
]

View File

@ -8,6 +8,7 @@ import QtQuick.Layouts 1.3
import UM 1.2 as UM
import Cura 1.0 as Cura
import "Menus"
import "Menus/ConfigurationMenu"
Rectangle
{
@ -87,10 +88,30 @@ Rectangle
MachineSelection {
id: machineSelection
width: base.width
width: base.width - configSelection.width - separator.width
height: UM.Theme.getSize("sidebar_header").height
anchors.top: base.top
anchors.left: parent.left
}
Rectangle
{
id: separator
visible: configSelection.visible
width: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
height: UM.Theme.getSize("sidebar_header").height
color: UM.Theme.getColor("sidebar_lining_thin")
anchors.left: machineSelection.right
}
ConfigurationSelection {
id: configSelection
visible: printerConnected && !sidebar.monitoringPrint && !sidebar.hideSettings
width: visible ? Math.round(base.width * 0.15) : 0
height: UM.Theme.getSize("sidebar_header").height
anchors.top: base.top
anchors.right: parent.right
panelWidth: base.width
}
SidebarHeader {

View File

@ -16,6 +16,8 @@ Column
property int currentExtruderIndex: Cura.ExtruderManager.activeExtruderIndex;
property bool currentExtruderVisible: extrudersList.visible;
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property bool hasManyPrinterTypes: printerConnected ? Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount.length > 1 : false
spacing: Math.round(UM.Theme.getSize("sidebar_margin").width * 0.9)
@ -24,16 +26,66 @@ Column
Item
{
id: initialSeparator
anchors
{
left: parent.left
right: parent.right
}
visible: extruderSelectionRow.visible
visible: printerTypeSelectionRow.visible || buildplateRow.visible || extruderSelectionRow.visible
height: UM.Theme.getSize("default_lining").height
width: height
}
// Printer Type Row
Item
{
id: printerTypeSelectionRow
height: UM.Theme.getSize("sidebar_setup").height
visible: printerConnected && hasManyPrinterTypes && !sidebar.monitoringPrint && !sidebar.hideSettings
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("sidebar_margin").width
right: parent.right
rightMargin: UM.Theme.getSize("sidebar_margin").width
}
Label
{
id: configurationLabel
text: catalog.i18nc("@label", "Printer type");
width: Math.round(parent.width * 0.4 - UM.Theme.getSize("default_margin").width)
height: parent.height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
}
ToolButton
{
id: printerTypeSelection
text: Cura.MachineManager.activeMachineDefinitionName
tooltip: Cura.MachineManager.activeMachineDefinitionName
height: UM.Theme.getSize("setting_control").height
width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width
anchors.right: parent.right
style: UM.Theme.styles.sidebar_header_button
activeFocusOnPress: true;
menu: PrinterTypeMenu { }
}
}
Rectangle {
id: headerSeparator
width: parent.width
visible: printerTypeSelectionRow.visible
height: visible ? UM.Theme.getSize("sidebar_lining").height : 0
color: UM.Theme.getColor("sidebar_lining")
}
// Extruder Row
Item
{
@ -261,7 +313,7 @@ Column
id: variantRowSpacer
height: Math.round(UM.Theme.getSize("sidebar_margin").height / 4)
width: height
visible: !extruderSelectionRow.visible
visible: !extruderSelectionRow.visible && !initialSeparator.visible
}
// Material Row
@ -284,6 +336,8 @@ Column
id: materialLabel
text: catalog.i18nc("@label", "Material");
width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
height: parent.height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
}
@ -344,6 +398,8 @@ Column
id: variantLabel
text: Cura.MachineManager.activeDefinitionVariantsName;
width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
height: parent.height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
}
@ -364,17 +420,14 @@ Column
}
}
//Buildplate row separator
Rectangle {
id: separator
id: buildplateSeparator
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width
visible: buildplateRow.visible
width: parent.width - UM.Theme.getSize("sidebar_margin").width * 2
height: visible ? Math.floor(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
color: UM.Theme.getColor("sidebar_lining_thin")
height: visible ? UM.Theme.getSize("sidebar_lining_thin").height : 0
color: UM.Theme.getColor("sidebar_lining")
}
//Buildplate row
@ -397,6 +450,8 @@ Column
id: bulidplateLabel
text: catalog.i18nc("@label", "Build plate");
width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
height: parent.height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
}
@ -420,74 +475,6 @@ Column
}
}
// Material info row
Item
{
id: materialInfoRow
height: Math.round(UM.Theme.getSize("sidebar_setup").height / 2)
visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("sidebar_margin").width
right: parent.right
rightMargin: UM.Theme.getSize("sidebar_margin").width
}
Item {
height: UM.Theme.getSize("sidebar_setup").height
anchors.right: parent.right
width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width)
UM.RecolorImage {
id: warningImage
anchors.right: materialInfoLabel.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.Bottom
source: UM.Theme.getIcon("warning")
width: UM.Theme.getSize("section_icon").width
height: UM.Theme.getSize("section_icon").height
color: UM.Theme.getColor("material_compatibility_warning")
visible: !Cura.MachineManager.isCurrentSetupSupported
}
Label {
id: materialInfoLabel
wrapMode: Text.WordWrap
text: "<a href='%1'>" + catalog.i18nc("@label", "Check compatibility") + "</a>"
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
verticalAlignment: Text.AlignTop
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
// open the material URL with web browser
var version = UM.Application.version;
var machineName = Cura.MachineManager.activeMachine.definition.id;
var url = "https://ultimaker.com/materialcompatibility/" + version + "/" + machineName + "?utm_source=cura&utm_medium=software&utm_campaign=resources";
Qt.openUrlExternally(url);
}
onEntered: {
var content = catalog.i18nc("@tooltip", "Click to check the material compatibility on Ultimaker.com.");
base.showTooltip(
materialInfoRow,
Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0),
catalog.i18nc("@tooltip", content)
);
}
onExited: base.hideTooltip();
}
}
}
}
UM.SettingPropertyProvider
{
id: machineExtruderCount

View File

@ -150,7 +150,7 @@ Rectangle
visible: base.width - allItemsWidth - 1 * this.width > 0
}
// #5 Left view
// #5 Right view
Button
{
iconSource: UM.Theme.getIcon("view_right")

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
<title>icn_buildplate</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="Printer-status-icon" transform="translate(-33.000000, -366.000000)" stroke="#000000">
<g id="icn_buildplate" transform="translate(33.000000, 367.000000)">
<polyline id="Stroke-6823" points="0 5 7 8 14 5"></polyline>
<polyline id="Stroke-6823-Copy" points="0 7 7 10 14 7"></polyline>
<polyline id="Stroke-6823-Copy" points="0 9 7 12 14 9"></polyline>
<polygon id="Stroke-6824" points="0 2.75 7 0 14 2.75 7 5.5"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,5 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon id="Polygon" points="15 8 16 9 9 16 8 15"></polygon>
<circle id="Oval-2" cx="6" cy="18" r="4"></circle>
<circle id="Oval-2-Copy" cx="18" cy="6" r="4"></circle>
</svg>

After

Width:  |  Height:  |  Size: 332 B

View File

@ -0,0 +1,6 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle id="Oval-2" stroke="#FFFFFF" cx="4" cy="20" r="3.5"></circle>
<circle id="Oval-2-Copy" stroke="#FFFFFF" cx="20" cy="4" r="3.5"></circle>
<polygon id="Polygon" fill="#FFFFFF" points="11 12 12 13 9 16 8 15"></polygon>
<polygon id="Polygon-Copy" fill="#FFFFFF" points="15 8 16 9 13 12 12 11"></polygon>
</svg>

After

Width:  |  Height:  |  Size: 475 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="16px" viewBox="0 0 32 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
<title>icn_groupPrinters</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Printer-status-icon" transform="translate(-24.000000, -176.000000)" fill="#000000">
<path d="M48,188 L54,188 L54,190 L48,190 L48,188 L48,188 Z M48,180 L54,180 L54,182 L48,182 L48,180 L48,180 Z M54,189 L56,189 L56,191 L54,191 L54,189 L54,189 Z M54,177 L56,177 L56,189 L54,189 L54,177 L54,177 Z M48,182 L51,182 L51,183 L48,183 L48,182 L48,182 Z M49,183 L50,183 L50,185 L49,185 L49,183 L49,183 Z M48,177 L54,177 L54,179 L48,179 L48,177 L48,177 Z M34,176 L46,176 L48,176 L48,187 L53,187 L53,188 L48,188 L48,190 L48,192 L46,192 L46,191 L34,191 L34,192 L32,192 L32,190 L26,190 L26,191 L24,191 L24,189 L24,177 L26,177 L32,177 L32,176 L34,176 Z M34,178 L34,179 L46,179 L46,178 L34,178 Z M41,182 L41,184 L39,184 L39,182 L38,182 L38,181 L42,181 L42,182 L41,182 Z M46,189 L46,181 L34,181 L34,189 L46,189 Z M32,179 L26,179 L26,180 L32,180 L32,179 Z M32,183 L31,183 L31,185 L30,185 L30,183 L29,183 L29,182 L26,182 L26,188 L27,188 L27,187 L32,187 L32,183 Z M35,188 L45,188 L45,189 L35,189 L35,188 Z" id="icn_groupPrinters"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
<title>icn_singlePrinter</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Printer-status-icon" transform="translate(-217.000000, -176.000000)" fill="#000000">
<g id="icn_singlePrinter" transform="translate(217.000000, 176.000000)">
<path d="M2,13 L14,13 L14,15 L2,15 L2,13 L2,13 Z M2,3 L14,3 L14,5 L2,5 L2,3 L2,3 Z M0,14 L2,14 L2,16 L0,16 L0,14 L0,14 Z M14,14 L16,14 L16,16 L14,16 L14,14 L14,14 Z M0,0 L2,0 L2,14 L0,14 L0,0 L0,0 Z M14,0 L16,0 L16,14 L14,14 L14,0 L14,0 Z M6,5 L10,5 L10,6 L6,6 L6,5 L6,5 Z M7,6 L9,6 L9,8 L7,8 L7,6 L7,6 Z M2,0 L14,0 L14,2 L2,2 L2,0 L2,0 Z M3,12 L13,12 L13,13 L3,13 L3,12 L3,12 Z" id="Rectangle-185-Copy-5-Copy-4"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -14,6 +14,16 @@
"weight": 50,
"family": "Noto Sans"
},
"medium": {
"size": 1.16,
"weight": 50,
"family": "Noto Sans"
},
"medium_bold": {
"size": 1.16,
"weight": 63,
"family": "Noto Sans"
},
"default": {
"size": 1.0,
"weight": 50,
@ -289,7 +299,21 @@
"layerview_move_combing": [0, 0, 255, 255],
"layerview_move_retraction": [128, 128, 255, 255],
"layerview_support_interface": [64, 192, 255, 255],
"layerview_nozzle": [181, 166, 66, 50]
"layerview_nozzle": [181, 166, 66, 50],
"configuration_item": [255, 255, 255, 0],
"configuration_item_active": [12, 169, 227, 32],
"configuration_item_text": [0, 0, 0, 255],
"configuration_item_text_active": [0, 0, 0, 255],
"configuration_item_border": [127, 127, 127, 255],
"configuration_item_border_active": [12, 169, 227, 32],
"configuration_item_border_hover": [12, 169, 227, 255],
"tab_status_connected": [12, 169, 227, 255],
"tab_status_disconnected": [200, 200, 200, 255],
"printer_config_matched": [12, 169, 227, 255],
"printer_config_mismatch": [127, 127, 127, 255]
},
"sizes": {
@ -342,6 +366,9 @@
"small_button": [2, 2],
"small_button_icon": [1.5, 1.5],
"printer_status_icon": [1.8, 1.8],
"printer_sync_icon": [1.2, 1.2],
"topbar_logo_right_margin": [3, 0],
"topbar_button": [8, 4],
"topbar_button_icon": [1.2, 1.2],