Fix merge conflicts with 4.0

This commit is contained in:
ChrisTerBeke 2019-01-08 11:49:36 +01:00
commit a6114d39e4
78 changed files with 595 additions and 474 deletions

View file

@ -51,7 +51,7 @@ from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
from cura.Arranging.ShapeArray import ShapeArray
from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.PrintersModel import PrintersModel
from cura.GlobalStacksModel import GlobalStacksModel
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
from cura.Operations.SetParentOperation import SetParentOperation
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
@ -502,7 +502,8 @@ class CuraApplication(QtApplication):
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
preferences.addPreference("cura/choice_on_open_project", "always_ask")
preferences.addPreference("cura/use_multi_build_plate", False)
preferences.addPreference("view/settings_list_height", 400)
preferences.addPreference("view/settings_visible", False)
preferences.addPreference("cura/currency", "")
preferences.addPreference("cura/material_settings", "{}")
@ -974,7 +975,7 @@ class CuraApplication(QtApplication):
qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel")
qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer")
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
qmlRegisterType(PrintersModel, "Cura", 1, 0, "PrintersModel")
qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel")
qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")
qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")

View file

@ -12,7 +12,8 @@ from cura.PrinterOutputDevice import ConnectionType
from cura.Settings.GlobalStack import GlobalStack
class PrintersModel(ListModel):
class GlobalStacksModel(ListModel):
NameRole = Qt.UserRole + 1
IdRole = Qt.UserRole + 2
HasRemoteConnectionRole = Qt.UserRole + 3
@ -41,20 +42,14 @@ class PrintersModel(ListModel):
if isinstance(container, GlobalStack):
self._update()
## Handler for container name change events.
def _onContainerNameChanged(self):
self._update()
def _update(self) -> None:
items = []
for container in self._container_stacks:
container.nameChanged.disconnect(self._onContainerNameChanged)
container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
for container_stack in container_stacks:
connection_type = container_stack.getMetaDataEntry("connection_type", 0)
has_remote_connection = int(connection_type) in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]
connection_type = int(container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value))
has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]
if container_stack.getMetaDataEntry("hidden", False) in ["True", True]:
continue

View file

@ -302,6 +302,10 @@ class MaterialManager(QObject):
def getMaterialGroupListByGUID(self, guid: str) -> Optional[List[MaterialGroup]]:
return self._guid_material_groups_map.get(guid)
# Returns a dict of all material groups organized by root_material_id.
def getAllMaterialGroups(self) -> Dict[str, "MaterialGroup"]:
return self._material_group_map
#
# Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup.
#

View file

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QTimer
from UM.Application import Application
from UM.Logger import Logger
@ -39,15 +39,23 @@ class QualityProfilesDropDownMenuModel(ListModel):
self._machine_manager = self._application.getMachineManager()
self._quality_manager = Application.getInstance().getQualityManager()
self._application.globalContainerStackChanged.connect(self._update)
self._machine_manager.activeQualityGroupChanged.connect(self._update)
self._machine_manager.extruderChanged.connect(self._update)
self._quality_manager.qualitiesUpdated.connect(self._update)
self._application.globalContainerStackChanged.connect(self._onChange)
self._machine_manager.activeQualityGroupChanged.connect(self._onChange)
self._machine_manager.extruderChanged.connect(self._onChange)
self._quality_manager.qualitiesUpdated.connect(self._onChange)
self._layer_height_unit = "" # This is cached
self._update_timer = QTimer() # type: QTimer
self._update_timer.setInterval(100)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._update)
self._update()
def _onChange(self) -> None:
self._update_timer.start()
def _update(self):
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))

View file

@ -35,7 +35,7 @@ class ConnectionState(IntEnum):
class ConnectionType(IntEnum):
Unknown = 0
NotConnected = 0
UsbConnection = 1
NetworkConnection = 2
CloudConnection = 3
@ -69,7 +69,7 @@ class PrinterOutputDevice(QObject, OutputDevice):
# Signal to indicate that the configuration of one of the printers has changed.
uniqueConfigurationsChanged = pyqtSignal()
def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.Unknown, parent: QObject = None) -> None:
def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.NotConnected, parent: QObject = None) -> None:
super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance
self._printers = [] # type: List[PrinterOutputModel]

View file

@ -527,7 +527,10 @@ class MachineManager(QObject):
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
def activeMachineHasRemoteConnection(self) -> bool:
return self.activeMachineHasActiveNetworkConnection or self.activeMachineHasActiveCloudConnection
if self._global_container_stack:
connection_type = int(self._global_container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value))
return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]
return False
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
def activeMachineIsGroup(self) -> bool:

View file

@ -1,4 +1,5 @@
// Copyright (c) 2017 Ultimaker B.V.
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Controls 1.4
@ -7,8 +8,6 @@ import UM 1.3 as UM
import Cura 1.0 as Cura
Item
{
// We show a nice overlay on the 3D viewer when the current output device has no monitor view
Rectangle
{
@ -30,8 +29,6 @@ Item
{
anchors.fill: parent
}
}
Loader
{
id: monitorViewComponent

View file

@ -61,7 +61,7 @@ UM.Dialog
anchors.leftMargin: base.textMargin
anchors.right: parent.right
anchors.rightMargin: base.textMargin
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
elide: Text.ElideRight
}
ListView
@ -289,7 +289,7 @@ UM.Dialog
elide: Text.ElideRight
height: 20 * screenScaleFactor
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
}
@ -484,7 +484,7 @@ UM.Dialog
onClicked: dialog.accept()
}
Button
Cura.SecondaryButton
{
objectName: "postProcessingSaveAreaButton"
visible: activeScriptsList.count > 0
@ -492,41 +492,7 @@ UM.Dialog
width: height
tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts")
onClicked: dialog.show()
style: ButtonStyle
{
background: Rectangle
{
id: deviceSelectionIcon
border.width: UM.Theme.getSize("default_lining").width
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
control.pressed ? UM.Theme.getColor("action_button_active_border") :
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
color: !control.enabled ? UM.Theme.getColor("action_button_disabled") :
control.pressed ? UM.Theme.getColor("action_button_active") :
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
Behavior on color { ColorAnimation { duration: 50; } }
anchors.left: parent.left
anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2)
width: parent.height
height: parent.height
UM.RecolorImage
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(parent.width / 2)
height: Math.round(parent.height / 2)
sourceSize.height: height
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
control.pressed ? UM.Theme.getColor("action_button_active_text") :
control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text")
source: "postprocessing.svg"
}
}
label: Label { }
}
iconSource: "postprocessing.svg"
fixedWidthMode: true
}
}

View file

@ -163,9 +163,9 @@ Item
id: rangleHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
x: parent.x + parent.width + UM.Theme.getSize("default_margin").width
x: parent.x - width - UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.verticalCenter
target: Qt.point(sliderRoot.width + width, y + height / 2)
target: Qt.point(sliderRoot.width, y + height / 2)
visible: sliderRoot.activeHandle == parent
// custom properties

View file

@ -43,7 +43,7 @@ Cura.ExpandableComponent
verticalAlignment: Text.AlignVCenter
height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
}
@ -60,7 +60,7 @@ Cura.ExpandableComponent
}
height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
}

View file

@ -30,7 +30,7 @@ Row
width: contentWidth
anchors.verticalCenter: starIcon.verticalCenter
color: starIcon.color
font: UM.Theme.getFont("small")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
}

View file

@ -38,7 +38,7 @@ Window
{
id: mainView
width: parent.width
z: -1
z: parent.z - 1
anchors
{
top: header.bottom

View file

@ -55,7 +55,7 @@ Item
bottomMargin: UM.Theme.getSize("default_margin").height
}
text: details.name || ""
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
wrapMode: Text.WordWrap
width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height

View file

@ -61,8 +61,13 @@ Item
id: labelStyle
text: control.text
color: control.enabled ? (control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
font: UM.Theme.getFont("default_bold")
horizontalAlignment: Text.AlignRight
font: UM.Theme.getFont("medium_bold")
horizontalAlignment: Text.AlignLeft
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("default_margin").width
}
width: control.width
renderType: Text.NativeRendering
}

View file

@ -59,7 +59,7 @@ Item
leftMargin: UM.Theme.getSize("default_margin").width
}
text: details === null ? "" : (details.name || "")
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
width: contentWidth
height: contentHeight

View file

@ -22,7 +22,7 @@ Column
text: gridArea.heading
width: parent.width
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium")
font: UM.Theme.getFont("large")
renderType: Text.NativeRendering
}
Grid

View file

@ -112,7 +112,7 @@ Item
elide: Text.ElideRight
width: parent.width
wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
anchors.top: name.bottom
anchors.bottom: rating.top

View file

@ -23,7 +23,7 @@ Rectangle
text: catalog.i18nc("@label", "Featured")
width: parent.width
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium")
font: UM.Theme.getFont("large")
renderType: Text.NativeRendering
}
Grid

View file

@ -37,7 +37,7 @@ ScrollView
width: page.width
text: catalog.i18nc("@title:tab", "Plugins")
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium")
font: UM.Theme.getFont("large")
renderType: Text.NativeRendering
}
Rectangle

View file

@ -49,13 +49,14 @@ Item
width: parent.width
height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
wrapMode: Text.WordWrap
font: UM.Theme.getFont("default_bold")
font: UM.Theme.getFont("large_bold")
color: pluginInfo.color
renderType: Text.NativeRendering
}
Label
{
text: model.description
font: UM.Theme.getFont("default")
maximumLineCount: 3
elide: Text.ElideRight
width: parent.width
@ -82,6 +83,7 @@ Item
return model.author_name
}
}
font: UM.Theme.getFont("medium")
width: parent.width
height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
wrapMode: Text.WordWrap
@ -96,6 +98,7 @@ Item
Label
{
text: model.version
font: UM.Theme.getFont("default")
width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height
color: UM.Theme.getColor("text")

View file

@ -223,7 +223,7 @@ Cura.MachineAction
width: parent.width
wrapMode: Text.WordWrap
text: base.selectedDevice ? base.selectedDevice.name : ""
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
elide: Text.ElideRight
renderType: Text.NativeRendering
}

View file

@ -81,7 +81,7 @@ Item
text: printer && printer.name ? printer.name : ""
color: "#414054" // TODO: Theme!
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
font: UM.Theme.getFont("large_bold") // 16pt, bold
width: parent.width
// FIXED-LINE-HEIGHT:
@ -185,7 +185,7 @@ Item
verticalCenter: parent.verticalCenter
}
color: "#414054" // TODO: Theme!
font: UM.Theme.getFont("large") // 16pt, bold
font: UM.Theme.getFont("large_bold") // 16pt, bold
text: {
if (printer && printer.state == "disabled")
{
@ -236,7 +236,7 @@ Item
id: printerJobNameLabel
color: printer.activePrintJob && printer.activePrintJob.isActive ? "#414054" : "#babac1" // TODO: Theme!
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
font: UM.Theme.getFont("large_bold") // 16pt, bold
text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N
width: parent.width

View file

@ -23,7 +23,7 @@ Item
top: parent.top
}
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("large_nonbold")
font: UM.Theme.getFont("large")
text: catalog.i18nc("@label", "Queued")
}

View file

@ -90,67 +90,4 @@ Item {
source: "DiscoverUM3Action.qml";
}
}
Column {
anchors.fill: parent;
objectName: "networkPrinterConnectionInfo";
spacing: UM.Theme.getSize("default_margin").width;
visible: isUM3;
Button {
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication();
text: catalog.i18nc("@action:button", "Request Access");
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer");
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested;
}
Row {
anchors {
left: parent.left;
right: parent.right;
}
height: childrenRect.height;
spacing: UM.Theme.getSize("default_margin").width;
visible: printerConnected;
Column {
Repeater {
model: CuraApplication.getExtrudersModel()
Label {
text: model.name;
}
}
}
Column {
Repeater {
id: nozzleColumn;
model: hotendIds
Label {
text: nozzleColumn.model[index];
}
}
}
Column {
Repeater {
id: materialColumn;
model: materialNames
Label {
text: materialColumn.model[index];
}
}
}
}
Button {
onClicked: manager.loadConfigurationFromPrinter();
text: catalog.i18nc("@action:button", "Activate Configuration");
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura");
visible: false; // printerConnected && !isClusterPrinter()
}
}
}

View file

@ -561,8 +561,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if material_group_list is None:
material_name = i18n_catalog.i18nc("@label:material", "Empty") if len(material_data.get("guid", "")) == 0 \
else i18n_catalog.i18nc("@label:material", "Unknown")
return MaterialOutputModel(guid = material_data.get("guid", ""),
type = material_data.get("type", ""),
type = material_data.get("material", ""),
color = material_data.get("color", ""),
brand = material_data.get("brand", ""),
name = material_data.get("name", material_name)

View file

@ -200,4 +200,3 @@ class DiscoverUM3Action(MachineAction):
# Create extra components
CuraApplication.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
CuraApplication.getInstance().addAdditionalComponent("machinesDetailPane", self.__additional_components_view.findChild(QObject, "networkPrinterConnectionInfo"))

View file

@ -2,7 +2,6 @@
# Cura is released under the terms of the LGPLv3 or higher.
import json
import os
import urllib.parse
from typing import Dict, TYPE_CHECKING, Set, Optional
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
@ -10,9 +9,7 @@ from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from UM.Application import Application
from UM.Job import Job
from UM.Logger import Logger
from UM.MimeTypeDatabase import MimeTypeDatabase
from UM.Resources import Resources
from cura.CuraApplication import CuraApplication
# Absolute imports don't work in plugins
from .Models import ClusterMaterial, LocalMaterial
@ -37,7 +34,6 @@ class SendMaterialJob(Job):
#
# \param reply The reply from the printer, a json file.
def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None:
# Got an error from the HTTP request. If we did not receive a 200 something happened.
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
Logger.log("e", "Error fetching materials from printer: %s", reply.errorString())
@ -52,7 +48,6 @@ class SendMaterialJob(Job):
#
# \param remote_materials_by_guid The remote materials by GUID.
def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None:
# Collect local materials
local_materials_by_guid = self._getLocalMaterials()
if len(local_materials_by_guid) == 0:
@ -91,22 +86,22 @@ class SendMaterialJob(Job):
#
# \param materials_to_send A set with id's of materials that must be sent.
def _sendMaterials(self, materials_to_send: Set[str]) -> None:
file_paths = Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer)
container_registry = Application.getInstance().getContainerRegistry()
material_manager = Application.getInstance().getMaterialManager()
material_group_dict = material_manager.getAllMaterialGroups()
# Find all local material files and send them if needed.
for file_path in file_paths:
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path)
except MimeTypeDatabase.MimeTypeNotFoundError:
continue
file_name = os.path.basename(file_path)
material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name))
if material_id not in materials_to_send:
for root_material_id in material_group_dict:
if root_material_id not in materials_to_send:
# If the material does not have to be sent we skip it.
continue
self._sendMaterialFile(file_path, file_name, material_id)
file_path = container_registry.getContainerFilePathById(root_material_id)
if not file_path:
Logger.log("w", "Cannot get file path for material container [%s]", root_material_id)
continue
file_name = os.path.basename(file_path)
self._sendMaterialFile(file_path, file_name, root_material_id)
## Send a single material file to the printer.
#
@ -116,7 +111,6 @@ class SendMaterialJob(Job):
# \param file_name The name of the material file.
# \param material_id The ID of the material in the file.
def _sendMaterialFile(self, file_path: str, file_name: str, material_id: str) -> None:
parts = []
# Add the material file.
@ -172,17 +166,21 @@ class SendMaterialJob(Job):
# \return a dictionary of LocalMaterial objects by GUID
def _getLocalMaterials(self) -> Dict[str, LocalMaterial]:
result = {} # type: Dict[str, LocalMaterial]
container_registry = Application.getInstance().getContainerRegistry()
material_containers = container_registry.findContainersMetadata(type = "material")
material_manager = Application.getInstance().getMaterialManager()
material_group_dict = material_manager.getAllMaterialGroups()
# Find the latest version of all material containers in the registry.
for material in material_containers:
for root_material_id, material_group in material_group_dict.items():
material_metadata = material_group.root_material_node.getMetadata()
try:
# material version must be an int
material["version"] = int(material["version"])
material_metadata["version"] = int(material_metadata["version"])
# Create a new local material
local_material = LocalMaterial(**material)
local_material = LocalMaterial(**material_metadata)
local_material.id = root_material_id
if local_material.GUID not in result or \
local_material.GUID not in result or \
@ -190,10 +188,10 @@ class SendMaterialJob(Job):
result[local_material.GUID] = local_material
except KeyError:
Logger.logException("w", "Local material {} has missing values.".format(material["id"]))
Logger.logException("w", "Local material {} has missing values.".format(material_metadata["id"]))
except ValueError:
Logger.logException("w", "Local material {} has invalid values.".format(material["id"]))
Logger.logException("w", "Local material {} has invalid values.".format(material_metadata["id"]))
except TypeError:
Logger.logException("w", "Local material {} has invalid values.".format(material["id"]))
Logger.logException("w", "Local material {} has invalid values.".format(material_metadata["id"]))
return result

View file

@ -118,6 +118,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
if key == um_network_key:
if not self._discovered_devices[key].isConnected():
Logger.log("d", "Attempting to connect with [%s]" % key)
active_machine.setMetaDataEntry("connection_type", self._discovered_devices[key].getConnectionType().value)
self._discovered_devices[key].connect()
self._discovered_devices[key].connectionStateChanged.connect(self._onDeviceConnectionStateChanged)
else:

View file

@ -1,26 +1,29 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy
import io
import json
from unittest import TestCase, mock
from unittest.mock import patch, call
from unittest.mock import patch, call, MagicMock
from PyQt5.QtCore import QByteArray
from UM.MimeTypeDatabase import MimeType
from UM.Application import Application
from cura.Machines.MaterialGroup import MaterialGroup
from cura.Machines.MaterialNode import MaterialNode
from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob
_FILES_MAP = {"generic_pla_white": "/materials/generic_pla_white.xml.fdm_material",
"generic_pla_black": "/materials/generic_pla_black.xml.fdm_material",
}
@patch("builtins.open", lambda _, __: io.StringIO("<xml></xml>"))
@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile",
lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile",
suffixes = ["xml.fdm_material"]))
@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"])
@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice")
@patch("PyQt5.QtNetwork.QNetworkReply")
class TestSendMaterialJob(TestCase):
# version 1
_LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White",
@ -29,6 +32,37 @@ class TestSendMaterialJob(TestCase):
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
# version 2
_LOCAL_MATERIAL_WHITE_NEWER = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White",
"GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "2",
"color_code": "#ffffff",
"description": "Test PLA White", "adhesion_info": "Use glue.",
"approximate_diameter": "3",
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
# invalid version: "one"
_LOCAL_MATERIAL_WHITE_INVALID_VERSION = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White",
"GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "one",
"color_code": "#ffffff",
"description": "Test PLA White", "adhesion_info": "Use glue.",
"approximate_diameter": "3",
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
_LOCAL_MATERIAL_WHITE_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE))}
_LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE_NEWER))}
_LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE_INVALID_VERSION))}
_LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black",
"base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE",
"brand": "Ultimaker", "material": "CPE", "color_name": "Black",
@ -37,6 +71,9 @@ class TestSendMaterialJob(TestCase):
"properties": {"density": "1.01", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
_LOCAL_MATERIAL_BLACK_ALL_RESULT = {"generic_pla_black": MaterialGroup("generic_pla_black",
MaterialNode(_LOCAL_MATERIAL_BLACK))}
_REMOTE_MATERIAL_WHITE = {
"guid": "badb0ee7-87c8-4f3f-9398-938587b67dce",
"material": "PLA",
@ -55,14 +92,17 @@ class TestSendMaterialJob(TestCase):
"density": 1.00
}
def test_run(self, device_mock, reply_mock):
def test_run(self):
device_mock = MagicMock()
job = SendMaterialJob(device_mock)
job.run()
# We expect the materials endpoint to be called when the job runs.
device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials)
def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock):
def test__onGetRemoteMaterials_withFailedRequest(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 404
job = SendMaterialJob(device_mock)
job._onGetRemoteMaterials(reply_mock)
@ -70,7 +110,9 @@ class TestSendMaterialJob(TestCase):
# We expect the device not to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock):
def test__onGetRemoteMaterials_withWrongEncoding(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500"))
job = SendMaterialJob(device_mock)
@ -79,7 +121,9 @@ class TestSendMaterialJob(TestCase):
# Given that the parsing fails we do no expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock):
def test__onGetRemoteMaterials_withBadJsonAnswer(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.")
job = SendMaterialJob(device_mock)
@ -88,7 +132,9 @@ class TestSendMaterialJob(TestCase):
# Given that the parsing fails we do no expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock):
def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200
remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy()
del remote_material_without_guid["guid"]
@ -99,18 +145,20 @@ class TestSendMaterialJob(TestCase):
# Given that parsing fails we do not expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count)
@patch("cura.Machines.MaterialManager.MaterialManager")
@patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application")
def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock,
reply_mock, device_mock):
material_manager_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
application_mock.getContainerRegistry.return_value = container_registry_mock
application_mock.getMaterialManager.return_value = material_manager_mock
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy()
localMaterialWhiteWithInvalidVersion["version"] = "one"
container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion]
application_mock.getContainerRegistry.return_value = container_registry_mock
material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT.copy()
with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
job = SendMaterialJob(device_mock)
@ -118,15 +166,16 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(0, device_mock.createFormPart.call_count)
@patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application")
def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock,
device_mock):
application_mock.getContainerRegistry.return_value = container_registry_mock
@patch("UM.Application.Application.getInstance")
def test__onGetRemoteMaterials_withNoUpdate(self, application_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
container_registry_mock = application_mock.getContainerRegistry.return_value
material_manager_mock = application_mock.getMaterialManager.return_value
device_mock.createFormPart.return_value = "_xXx_"
container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE]
material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy()
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
@ -138,22 +187,23 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(0, device_mock.createFormPart.call_count)
self.assertEqual(0, device_mock.postFormWithParts.call_count)
@patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application")
def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock,
device_mock):
application_mock.getContainerRegistry.return_value = container_registry_mock
@patch("UM.Application.Application.getInstance")
def test__onGetRemoteMaterials_withUpdatedMaterial(self, get_instance_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
application_mock = get_instance_mock.return_value
container_registry_mock = application_mock.getContainerRegistry.return_value
material_manager_mock = application_mock.getMaterialManager.return_value
container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x)
device_mock.createFormPart.return_value = "_xXx_"
localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy()
localMaterialWhiteWithHigherVersion["version"] = "2"
container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion]
material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT.copy()
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
job = SendMaterialJob(device_mock)
job._onGetRemoteMaterials(reply_mock)
@ -164,16 +214,21 @@ class TestSendMaterialJob(TestCase):
call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
device_mock.method_calls)
@patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application")
def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock,
device_mock):
application_mock.getContainerRegistry.return_value = container_registry_mock
@patch("UM.Application.Application.getInstance")
def test__onGetRemoteMaterials_withNewMaterial(self, application_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
container_registry_mock = application_mock.getContainerRegistry.return_value
material_manager_mock = application_mock.getMaterialManager.return_value
container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x)
device_mock.createFormPart.return_value = "_xXx_"
container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE,
self._LOCAL_MATERIAL_BLACK]
all_results = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy()
for key, value in self._LOCAL_MATERIAL_BLACK_ALL_RESULT.items():
all_results[key] = value
material_manager_mock.getAllMaterialGroups.return_value = all_results
reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii"))

View file

@ -44,7 +44,7 @@ Column
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please log in or create an account to\nenjoy all features of Ultimaker Cura.")
font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default")
font: loggedIn ? UM.Theme.getFont("large_bold") : UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
}

View file

@ -23,6 +23,7 @@ Rectangle
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
radius: UM.Theme.getSize("default_radius").width
z: 10
property bool outputAvailable: UM.Backend.state == UM.Backend.Done || UM.Backend.state == UM.Backend.Disabled

View file

@ -93,7 +93,6 @@ Item
onClicked:
{
UM.OutputDeviceManager.setActiveDevice(model.id)
widget.requestWriteToDevice()
popup.close()
}
}

View file

@ -40,8 +40,7 @@ Column
anchors
{
left: parent.left
right: printInformationPanel.left
rightMargin: printInformationPanel.visible ? UM.Theme.getSize("thin_margin").width : 0
right: parent.right
}
Cura.IconWithText
@ -51,7 +50,15 @@ Column
text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
source: UM.Theme.getIcon("clock")
font: UM.Theme.getFont("default_bold")
font: UM.Theme.getFont("large_bold")
PrintInformationWidget
{
id: printInformationPanel
visible: !preSlicedData
anchors.left: parent.left
anchors.leftMargin: parent.contentWidth + UM.Theme.getSize("default_margin").width
}
}
Cura.IconWithText
@ -84,20 +91,43 @@ Column
return totalWeights + "g · " + totalLengths.toFixed(2) + "m"
}
source: UM.Theme.getIcon("spool")
Item
{
id: additionalComponents
width: childrenRect.width
anchors.right: parent.right
height: parent.height
Row
{
id: additionalComponentsRow
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing: UM.Theme.getSize("default_margin").width
}
}
Component.onCompleted: addAdditionalComponents("saveButton")
Connections
{
target: CuraApplication
onAdditionalComponentsChanged: addAdditionalComponents("saveButton")
}
function addAdditionalComponents (areaId)
{
if(areaId == "saveButton")
{
for (var component in CuraApplication.additionalComponents["saveButton"])
{
CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
}
}
}
}
}
PrintInformationWidget
{
id: printInformationPanel
visible: !preSlicedData
anchors
{
right: parent.right
verticalCenter: timeAndCostsInformation.verticalCenter
}
}
}
Item
@ -127,9 +157,6 @@ Column
onClicked: UM.Controller.setActiveStage("PreviewStage")
visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage"
shadowEnabled: true
shadowColor: UM.Theme.getColor("action_button_disabled_shadow")
}
Cura.OutputDevicesActionButton

View file

@ -44,9 +44,9 @@ Column
{
id: autoSlicingLabel
width: parent.width
visible: prepareButtons.autoSlice && (widget.backendState == UM.Backend.Processing || widget.backendState == UM.Backend.NotStarted)
visible: progressBar.visible
text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...")
text: catalog.i18nc("@label:PrintjobStatus", "Slicing...")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
@ -107,7 +107,13 @@ Column
{
id: sliceButton
fixedWidthMode: true
anchors.fill: parent
height: parent.height
anchors.right: additionalComponents.left
anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0
anchors.left: parent.left
text: catalog.i18nc("@button", "Slice")
tooltip: catalog.i18nc("@label", "Start the slicing process")
enabled: widget.backendState != UM.Backend.Error
@ -119,12 +125,48 @@ Column
{
id: cancelButton
fixedWidthMode: true
anchors.fill: parent
height: parent.height
anchors.left: parent.left
anchors.right: additionalComponents.left
anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@button", "Cancel")
enabled: sliceButton.enabled
visible: !sliceButton.visible
onClicked: sliceOrStopSlicing()
}
Item
{
id: additionalComponents
width: childrenRect.width
anchors.right: parent.right
height: parent.height
Row
{
id: additionalComponentsRow
anchors.verticalCenter: parent.verticalCenter
spacing: UM.Theme.getSize("default_margin").width
}
}
Component.onCompleted: prepareButtons.addAdditionalComponents("saveButton")
Connections
{
target: CuraApplication
onAdditionalComponentsChanged: prepareButtons.addAdditionalComponents("saveButton")
}
function addAdditionalComponents (areaId)
{
if(areaId == "saveButton")
{
for (var component in CuraApplication.additionalComponents["saveButton"])
{
CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
}
}
}
}

View file

@ -252,7 +252,21 @@ UM.MainWindow
anchors.bottom: parent.bottom
anchors.rightMargin: UM.Theme.getSize("thick_margin").width
anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
visible: CuraApplication.platformActivity
/*
Show this panel only if there is something on the build plate, and there is NOT an opaque item in front of the build plate.
This cannot be solved by Z indexing! If you want to try solving this, please increase this counter when you're done:
Number of people having tried to fix this by z-indexing: 2
The problem arises from the following render order requirements:
- The stage menu must be rendered above the stage main.
- The stage main must be rendered above the action panel (because the monitor page must be rendered above the action panel).
- The action panel must be rendered above the expandable components drop-down.
However since the expandable components drop-downs are child elements of the stage menu,
they can't be rendered lower than elements that are lower than the stage menu.
Therefore we opted to forego the second requirement and hide the action panel instead when something obscures it (except the expandable components).
We assume that QQuickRectangles are always opaque and any other item is not.
*/
visible: CuraApplication.platformActivity && (main.item == null || !qmlTypeOf(main.item, "QQuickRectangle"))
}
Loader
@ -818,4 +832,21 @@ UM.MainWindow
}
}
}
/**
* Function to check whether a QML object has a certain type.
* Taken from StackOverflow: https://stackoverflow.com/a/28384228 and
* adapted to our code style.
* Licensed under CC BY-SA 3.0.
* \param obj The QtObject to get the name of.
* \param class_name (str) The name of the class to check against. Has to be
* the QtObject class name, not the QML entity name.
*/
function qmlTypeOf(obj, class_name)
{
//className plus "(" is the class instance without modification.
//className plus "_QML" is the class instance with user-defined properties.
var str = obj.toString();
return str.indexOf(class_name + "(") == 0 || str.indexOf(class_name + "_QML") == 0;
}
}

View file

@ -51,7 +51,7 @@ UM.Dialog
id: version
text: catalog.i18nc("@label","version: %1").arg(UM.Application.version)
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
anchors.right : logo.right
anchors.top: logo.bottom

View file

@ -64,7 +64,7 @@ Item
property alias iconSize: collapseButton.height
// Is the "drawer" open?
readonly property alias expanded: contentContainer.visible
property alias expanded: contentContainer.visible
// What should the radius of the header be. This is also influenced by the headerCornerSide
property alias headerRadius: background.radius

View file

@ -25,7 +25,7 @@ Cura.RoundedRectangle
{
id: headerLabel
text: ""
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("small_button_text")

View file

@ -48,7 +48,7 @@ Item
id: extruderNumberText
anchors.centerIn: parent
text: index + 1
font: UM.Theme.getFont("very_small")
font: UM.Theme.getFont("small")
color: UM.Theme.getColor("text")
width: contentWidth
height: contentHeight
@ -66,7 +66,7 @@ Item
sourceSize.height: width
source: UM.Theme.getIcon("cross1")
visible: !extruderEnabled
color: "black"
color: UM.Theme.getColor("text")
}
}
}

View file

@ -97,7 +97,7 @@ Item
style: TextFieldStyle
{
textColor: UM.Theme.getColor("text_scene")
font: UM.Theme.getFont("default_bold")
font: UM.Theme.getFont("default")
background: Rectangle
{
opacity: 0
@ -115,7 +115,7 @@ Item
height: UM.Theme.getSize("jobspecs_line").height
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default_bold")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_scene")
text: CuraApplication.getSceneBoundingBoxString
}

View file

@ -83,14 +83,6 @@ Item
}
}
Menu
{
id: plugin_menu
title: catalog.i18nc("@title:menu menubar:toplevel", "&Marketplace")
MenuItem { action: Cura.Actions.browsePackages }
}
Menu
{
id: preferencesMenu

View file

@ -102,6 +102,7 @@ Item
{
id: label
text: marketplaceButton.text
font: UM.Theme.getFont("default")
color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text")
width: contentWidth
verticalAlignment: Text.AlignVCenter

View file

@ -16,7 +16,7 @@ Item
{
id: header
text: catalog.i18nc("@header", "Configurations")
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("small_button_text")
height: contentHeight
renderType: Text.NativeRendering

View file

@ -12,7 +12,23 @@ Button
id: configurationItem
property var configuration: null
hoverEnabled: true
hoverEnabled: isValidMaterial
property bool isValidMaterial:
{
var extruderConfigurations = configuration.extruderConfigurations
for (var index in extruderConfigurations)
{
var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : ""
if (name == "" || name == "Unknown")
{
return false
}
}
return true
}
background: Rectangle
{
@ -40,17 +56,104 @@ Button
right: parent.right
rightMargin: UM.Theme.getSize("wide_margin").width
}
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
Repeater
{
id: repeater
model: configuration.extruderConfigurations
delegate: PrintCoreConfiguration
{
width: Math.round(parent.width / 2)
printCoreConfiguration: modelData
visible: configurationItem.isValidMaterial
}
}
// Unknown material
Item
{
id: unknownMaterial
height: unknownMaterialMessage.height + UM.Theme.getSize("thin_margin").width / 2
width: parent.width
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("thin_margin").width / 2
visible: !configurationItem.isValidMaterial
UM.RecolorImage
{
id: icon
anchors.verticalCenter: unknownMaterialMessage.verticalCenter
source: UM.Theme.getIcon("warning")
color: UM.Theme.getColor("warning")
width: UM.Theme.getSize("section_icon").width
height: width
}
Label
{
id: unknownMaterialMessage
text:
{
var extruderConfigurations = configuration.extruderConfigurations
var unknownMaterials = []
for (var index in extruderConfigurations)
{
var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : ""
if (name == "" || name == "Unknown")
{
var materialType = extruderConfigurations[index].material.type
if (extruderConfigurations[index].material.type == "")
{
materialType = "Unknown"
}
var brand = extruderConfigurations[index].material.brand
if (brand == "")
{
brand = "Unknown"
}
name = materialType + " (" + brand + ")"
unknownMaterials.push(name)
}
}
unknownMaterials = "<b>" + unknownMaterials + "</b>"
var draftResult = catalog.i18nc("@label", "This configuration is not available because %1 is not recognized. Please visit %2 to download the correct material profile.");
var result = draftResult.arg(unknownMaterials).arg("<a href=' '>" + catalog.i18nc("@label","Marketplace") + "</a> ")
return result
}
width: extruderRow.width
anchors.left: icon.right
anchors.right: unknownMaterial.right
anchors.leftMargin: UM.Theme.getSize("wide_margin").height
anchors.top: unknownMaterial.top
wrapMode: Text.WordWrap
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
linkColor: UM.Theme.getColor("text_link")
onLinkActivated:
{
Cura.Actions.browsePackages.trigger()
}
}
MouseArea
{
anchors.fill: parent
cursorShape: unknownMaterialMessage.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
acceptedButtons: Qt.NoButton
}
}
}
@ -84,27 +187,15 @@ Button
rightMargin: UM.Theme.getSize("wide_margin").width
}
height: childrenRect.height
visible: configuration.buildplateConfiguration != ""
visible: configuration.buildplateConfiguration != "" && false //Buildplate is disabled as long as we have no printers that properly support buildplate swapping (so we can't test).
UM.RecolorImage
{
id: buildplateIcon
anchors.left: parent.left
width: UM.Theme.getSize("main_window_header_button_icon").width
height: UM.Theme.getSize("main_window_header_button_icon").height
source: UM.Theme.getIcon("buildplate")
color: UM.Theme.getColor("text")
}
Label
// Show the type of buildplate. The first letter is capitalized
Cura.IconWithText
{
id: buildplateLabel
anchors.left: buildplateIcon.right
anchors.verticalCenter: buildplateIcon.verticalCenter
anchors.leftMargin: UM.Theme.getSize("narrow_margin").height
text: configuration.buildplateConfiguration
renderType: Text.NativeRendering
color: UM.Theme.getColor("text")
source: UM.Theme.getIcon("buildplate")
text: configuration.buildplateConfiguration.charAt(0).toUpperCase() + configuration.buildplateConfiguration.substr(1)
anchors.left: parent.left
}
}
}
@ -125,6 +216,9 @@ Button
onClicked:
{
Cura.MachineManager.applyRemoteConfiguration(configuration)
if(isValidMaterial)
{
Cura.MachineManager.applyRemoteConfiguration(configuration);
}
}
}

View file

@ -51,7 +51,7 @@ Item
anchors.left: icon.right
anchors.right: parent.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@label", "The configurations are not available because the printer is disconnected.")
text: catalog.i18nc("@label", "Downloading the configurations from the remote printer")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering

View file

@ -86,7 +86,7 @@ Cura.ExpandablePopup
{
text: model.material
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
@ -107,7 +107,7 @@ Cura.ExpandablePopup
{
text: catalog.i18nc("@label", "Select configuration")
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering

View file

@ -23,7 +23,7 @@ Item
{
id: header
text: catalog.i18nc("@header", "Custom")
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("small_button_text")
height: contentHeight
renderType: Text.NativeRendering

View file

@ -39,7 +39,7 @@ Row
text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct.
renderType: Text.NativeRendering
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
}
Label

View file

@ -9,7 +9,7 @@ import Cura 1.0 as Cura
Instantiator
{
model: Cura.PrintersModel {}
model: Cura.GlobalStacksModel {}
MenuItem
{

View file

@ -9,7 +9,7 @@ import Cura 1.0 as Cura
Instantiator
{
model: Cura.PrintersModel {}
model: Cura.GlobalStacksModel {}
MenuItem
{
text: model.metadata["connect_group_name"]

View file

@ -168,7 +168,7 @@ Item
anchors.leftMargin: UM.Theme.getSize("thick_margin").width
color: base.statusColor
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
text: statusText
}
@ -179,7 +179,7 @@ Item
anchors.right: progressBar.right
color: base.statusColor
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
text: Math.round(progress) + "%"
visible: showProgress
}

View file

@ -70,7 +70,7 @@ UM.ManagementPage
{
id: machineName
text: base.currentItem && base.currentItem.name ? base.currentItem.name : ""
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
width: parent.width
elide: Text.ElideRight
}
@ -126,132 +126,15 @@ UM.ManagementPage
}
}
Grid
{
id: machineInfo
anchors.top: machineActions.visible ? machineActions.bottom : machineActions.anchors.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.right: parent.right
spacing: UM.Theme.getSize("default_margin").height
rowSpacing: UM.Theme.getSize("default_lining").height
columns: 2
visible: base.currentItem
property bool printerConnected: Cura.MachineManager.printerConnected
property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property var printJob: connectedPrinter != null ? connectedPrinter.activePrintJob: null
Label
{
text: catalog.i18nc("@label", "Printer type:")
visible: base.currentItem && "definition_name" in base.currentItem.metadata
}
Label
{
text: (base.currentItem && "definition_name" in base.currentItem.metadata) ? base.currentItem.metadata.definition_name : ""
}
Label
{
text: catalog.i18nc("@label", "Connection:")
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId
}
Label
{
width: (parent.width * 0.7) | 0
text: machineInfo.printerConnected ? machineInfo.connectedPrinter.connectionText : catalog.i18nc("@info:status", "The printer is not connected.")
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId
wrapMode: Text.WordWrap
}
Label
{
text: catalog.i18nc("@label", "State:")
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands
}
Label {
width: (parent.width * 0.7) | 0
text:
{
if(!machineInfo.printerConnected || !machineInfo.printerAcceptsCommands) {
return "";
}
if (machineInfo.printJob == null)
{
return catalog.i18nc("@label:MonitorStatus", "Waiting for a printjob");
}
switch(machineInfo.printJob.state)
{
case "printing":
return catalog.i18nc("@label:MonitorStatus", "Printing...");
case "paused":
return catalog.i18nc("@label:MonitorStatus", "Paused");
case "pre_print":
return catalog.i18nc("@label:MonitorStatus", "Preparing...");
case "wait_cleanup":
return catalog.i18nc("@label:MonitorStatus", "Waiting for someone to clear the build plate");
case "error":
return printerOutputDevice.errorText;
case "maintenance":
return catalog.i18nc("@label:MonitorStatus", "In maintenance. Please check the printer");
case "abort": // note sure if this jobState actually occurs in the wild
return catalog.i18nc("@label:MonitorStatus", "Aborting print...");
}
return ""
}
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands
wrapMode: Text.WordWrap
}
}
Column {
id: additionalComponentsColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: machineInfo.visible ? machineInfo.bottom : machineInfo.anchors.top
anchors.topMargin: UM.Theme.getSize("default_margin").width
spacing: UM.Theme.getSize("default_margin").width
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId
Component.onCompleted:
{
for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) {
CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn
}
}
}
Component.onCompleted: {
addAdditionalComponents("machinesDetailPane")
}
Connections {
target: CuraApplication
onAdditionalComponentsChanged: addAdditionalComponents
}
function addAdditionalComponents (areaId) {
if(areaId == "machinesDetailPane") {
for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) {
CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn
}
}
}
UM.I18nCatalog { id: catalog; name: "cura"; }
UM.ConfirmRemoveDialog
{
id: confirmDialog;
object: base.currentItem && base.currentItem.name ? base.currentItem.name : "";
id: confirmDialog
object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""
onYes:
{
Cura.MachineManager.removeMachine(base.currentItem.id);
Cura.MachineManager.removeMachine(base.currentItem.id)
if(!base.currentItem)
{
objectList.currentIndex = activeMachineIndex()

View file

@ -10,7 +10,7 @@ import QtQuick.Dialogs 1.2
import UM 1.2 as UM
import Cura 1.0 as Cura
Rectangle
Item
{
id: brand_section

View file

@ -65,7 +65,7 @@ Item
Label {
text: materialProperties.name
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
}
}

View file

@ -10,7 +10,7 @@ import QtQuick.Dialogs 1.2
import UM 1.2 as UM
import Cura 1.0 as Cura
Rectangle
Item
{
id: material_type_section
property var materialType

View file

@ -376,6 +376,7 @@ Item
width: true ? (parent.width * 0.4) | 0 : parent.width
frameVisible: true
clip: true
ListView
{
@ -408,7 +409,7 @@ Item
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width
text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles")
text: section == "true" ? catalog.i18nc("@label", "Default profiles") : catalog.i18nc("@label", "Custom profiles")
font.bold: true
}
}
@ -471,7 +472,7 @@ Item
Label {
text: base.currentItemName
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
}
}

View file

@ -11,7 +11,6 @@ import Cura 1.0 as Cura
Item
{
id: customPrintSetup
height: childrenRect.height + padding
property real padding: UM.Theme.getSize("default_margin").width
property bool multipleExtruders: extrudersModel.count > 1
@ -98,15 +97,15 @@ Item
Rectangle
{
height: UM.Theme.getSize("print_setup_widget").height
anchors
{
top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom
topMargin: -UM.Theme.getSize("default_lining").width
left: parent.left
leftMargin: parent.padding
right: parent.right
rightMargin: parent.padding
topMargin: -UM.Theme.getSize("default_lining").width
bottom: parent.bottom
}
z: tabBar.z - 1
// Don't show the border when only one extruder

View file

@ -25,7 +25,7 @@ Item
right: globalProfileSelection.left
}
text: catalog.i18nc("@label", "Profile")
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
}

View file

@ -29,4 +29,7 @@ Cura.ExpandableComponent
property var extrudersModel: CuraApplication.getExtrudersModel()
contentItem: PrintSetupSelectorContents {}
onExpandedChanged: UM.Preferences.setValue("view/settings_visible", expanded)
Component.onCompleted: expanded = UM.Preferences.getValue("view/settings_visible")
}

View file

@ -15,7 +15,7 @@ Item
id: content
width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width
height: childrenRect.height
height: contents.height + buttonRow.height
enum Mode
{
@ -71,6 +71,15 @@ Item
right: parent.right
top: parent.top
}
height: UM.Preferences.getValue("view/settings_list_height") - UM.Theme.getSize("default_margin").height
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
customPrintSetup.height = UM.Preferences.getValue("view/settings_list_height");
}
}
visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom
}
}
@ -94,13 +103,14 @@ Item
anchors
{
top: buttonsSeparator.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
Cura.SecondaryButton
{
id: recommendedButton
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: parent.padding
@ -125,5 +135,58 @@ Item
visible: currentModeIndex == PrintSetupSelectorContents.Mode.Recommended
onClicked: currentModeIndex = PrintSetupSelectorContents.Mode.Custom
}
//Invisible area at the bottom with which you can resize the panel.
MouseArea
{
anchors
{
left: parent.left
right: parent.right
bottom: parent.bottom
top: recommendedButton.bottom
topMargin: UM.Theme.getSize("default_lining").height
}
cursorShape: Qt.SplitVCursor
visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom
drag
{
target: parent
axis: Drag.YAxis
}
onMouseYChanged:
{
if(drag.active)
{
// position of mouse relative to dropdown align vertical centre of mouse area to cursor
// v------------------------------v v------------v
var h = mouseY + buttonRow.y + content.y - height / 2 | 0;
if(h < 200 * screenScaleFactor) //Enforce a minimum size.
{
h = 200 * screenScaleFactor;
}
//Absolute mouse Y position in the window, to prevent it from going outside the window.
var mouse_absolute_y = mapToGlobal(mouseX, mouseY).y - UM.Preferences.getValue("general/window_top");
if(mouse_absolute_y > base.height)
{
h -= mouse_absolute_y - base.height;
}
UM.Preferences.setValue("view/settings_list_height", h);
}
}
UM.RecolorImage
{
width: parent.width * 0.05
height: parent.height * 0.3
anchors.centerIn: parent
source: UM.Theme.getIcon("grip_lines")
color: UM.Theme.getColor("lining")
}
}
}
}

View file

@ -29,6 +29,7 @@ RowLayout
}
return ""
}
font: UM.Theme.getFont("medium")
UM.SettingPropertyProvider
{
@ -43,6 +44,7 @@ RowLayout
{
source: UM.Theme.getIcon("category_infill")
text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%"
font: UM.Theme.getFont("medium")
UM.SettingPropertyProvider
{
@ -57,6 +59,7 @@ RowLayout
{
source: UM.Theme.getIcon("category_support")
text: supportEnabled.properties.value == "True" ? enabledText : disabledText
font: UM.Theme.getFont("medium")
UM.SettingPropertyProvider
{
@ -71,6 +74,7 @@ RowLayout
{
source: UM.Theme.getIcon("category_adhesion")
text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText
font: UM.Theme.getFont("medium")
UM.SettingPropertyProvider
{

View file

@ -26,6 +26,7 @@ Item
anchors.left: parent.left
source: UM.Theme.getIcon("category_adhesion")
text: catalog.i18nc("@label", "Adhesion")
font: UM.Theme.getFont("medium")
width: labelColumnWidth
}

View file

@ -63,6 +63,7 @@ Item
anchors.left: parent.left
source: UM.Theme.getIcon("category_infill")
text: catalog.i18nc("@label", "Infill") + " (%)"
font: UM.Theme.getFont("medium")
width: labelColumnWidth
}
@ -140,6 +141,7 @@ Item
Label
{
text: index
font: UM.Theme.getFont("default")
visible: (index % 20) == 0 // Only show steps of 20%
anchors.horizontalCenter: parent.horizontalCenter
y: UM.Theme.getSize("thin_margin").height

View file

@ -173,6 +173,7 @@ Item
id: qualityRowTitle
source: UM.Theme.getIcon("category_layer_height")
text: catalog.i18nc("@label", "Layer Height")
font: UM.Theme.getFont("medium")
anchors.left: parent.left
anchors.right: customisedSettings.left
}
@ -271,6 +272,7 @@ Item
return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2))
}
}
font: UM.Theme.getFont("default")
}
}
}

View file

@ -27,6 +27,7 @@ Item
visible: enableSupportCheckBox.visible
source: UM.Theme.getIcon("category_support")
text: catalog.i18nc("@label", "Support")
font: UM.Theme.getFont("medium")
width: labelColumnWidth
}

View file

@ -41,7 +41,8 @@ UM.PointingRectangle {
base.opacity = 0;
}
Label {
Label
{
id: label;
anchors {
top: parent.top;

View file

@ -80,7 +80,7 @@ Item
id: extruderCurrentTemperature
text: Math.round(extruderModel.hotendTemperature) + "°C"
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
anchors.right: extruderTargetTemperature.left
anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width

View file

@ -67,7 +67,7 @@ Item
{
id: bedCurrentTemperature
text: printerModel != null ? printerModel.bedTemperature + "°C" : ""
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
anchors.right: bedTargetTemperature.left
anchors.top: parent.top

View file

@ -31,7 +31,7 @@ Item
Label
{
id: outputDeviceNameLabel
font: UM.Theme.getFont("large")
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("text")
anchors.left: parent.left
anchors.top: parent.top

View file

@ -101,14 +101,16 @@ Cura.ExpandablePopup
width: scroll.width - scroll.leftPadding - scroll.rightPadding
property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height
onHeightChanged:
// We use an extra property here, since we only want to to be informed about the content size changes.
onContentHeightChanged:
{
scroll.height = Math.min(height, maximumHeight)
scroll.height = Math.min(contentHeight, maximumHeight)
popup.height = scroll.height + buttonRow.height
}
Component.onCompleted:
{
scroll.height = Math.min(height, maximumHeight)
scroll.height = Math.min(contentHeight, maximumHeight)
popup.height = scroll.height + buttonRow.height
}

View file

@ -10,15 +10,15 @@ import Cura 1.0 as Cura
ListView
{
id: listView
height: childrenRect.height
model: Cura.PrintersModel {}
model: Cura.GlobalStacksModel {}
section.property: "hasRemoteConnection"
property real contentHeight: childrenRect.height
section.delegate: Label
{
text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Preset printers")
width: parent.width
height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0
height: UM.Theme.getSize("action_button").height
leftPadding: UM.Theme.getSize("default_margin").width
renderType: Text.NativeRendering
font: UM.Theme.getFont("medium")

View file

@ -73,7 +73,7 @@ Button
text: definition.label
textFormat: Text.PlainText
renderType: Text.NativeRendering
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium_bold")
color:
{
if (!base.enabled)

View file

@ -51,7 +51,7 @@ Cura.ExpandablePopup
verticalAlignment: Text.AlignVCenter
height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
}
@ -68,7 +68,7 @@ Cura.ExpandablePopup
}
height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
font: UM.Theme.getFont("medium")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
}

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 6">
<rect width="30" height="2" rx="1" ry="1" />
<rect width="30" height="2" rx="1" ry="1" y="4" />
</svg>

After

Width:  |  Height:  |  Size: 170 B

View file

@ -207,8 +207,8 @@ QtObject
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: control.text;
font: Theme.getFont("button_tooltip")
text: control.text
font: Theme.getFont("default")
color: Theme.getColor("tooltip_text")
}
}
@ -256,7 +256,7 @@ QtObject
source: control.iconSource
width: Theme.getSize("button_icon").width
height: Theme.getSize("button_icon").height
color: Theme.getColor("toolbar_button_text")
color: Theme.getColor("icon")
sourceSize: Theme.getSize("button_icon")
}
@ -585,6 +585,7 @@ QtObject
text: control.unit ? control.unit : ""
color: Theme.getColor("setting_unit");
font: Theme.getFont("default");
renderType: Text.NativeRendering
}
}
}

View file

@ -6,17 +6,17 @@
"fonts": {
"large": {
"size": 1.35,
"weight": 63,
"weight": 40,
"family": "Noto Sans"
},
"large_nonbold": {
"large_bold": {
"size": 1.35,
"weight": 50,
"weight": 63,
"family": "Noto Sans"
},
"medium": {
"size": 1.16,
"weight": 50,
"weight": 40,
"family": "Noto Sans"
},
"medium_bold": {
@ -25,29 +25,24 @@
"family": "Noto Sans"
},
"default": {
"size": 1.0,
"weight": 50,
"size": 0.95,
"weight": 40,
"family": "Noto Sans"
},
"default_bold": {
"size": 1.0,
"size": 0.95,
"weight": 63,
"family": "Noto Sans"
},
"default_italic": {
"size": 1.0,
"weight": 50,
"size": 0.95,
"weight": 40,
"italic": true,
"family": "Noto Sans"
},
"small": {
"size": 0.85,
"weight": 50,
"family": "Noto Sans"
},
"very_small": {
"size": 0.7,
"weight": 50,
"weight": 40,
"family": "Noto Sans"
}
},
@ -118,7 +113,6 @@
"warning": [245, 166, 35, 255],
"disabled": [229, 229, 229, 255],
"toolbar_button_text": [8, 7, 63, 255],
"toolbar_button_hover": [232, 242, 252, 255],
"toolbar_button_active": [232, 242, 252, 255],
"toolbar_button_active_hover": [232, 242, 252, 255],
@ -303,7 +297,7 @@
"printer_config_mismatch": [127, 127, 127, 255],
"toolbox_header_button_text_active": [0, 0, 0, 255],
"toolbox_header_button_text_inactive": [128, 128, 128, 255],
"toolbox_header_button_text_inactive": [0, 0, 0, 255],
"toolbox_header_button_text_hovered": [0, 0, 0, 255],
"favorites_header_bar": [245, 245, 245, 255],
@ -492,7 +486,7 @@
"toolbox_detail_header": [1.0, 14.0],
"toolbox_detail_tile": [1.0, 8.0],
"toolbox_back_column": [6.0, 1.0],
"toolbox_back_button": [4.0, 2.0],
"toolbox_back_button": [6.0, 2.0],
"toolbox_installed_tile": [1.0, 8.0],
"toolbox_property_label": [1.0, 2.0],
"toolbox_heading_label": [1.0, 3.8],