Show Cura Connect alerts in the monitor tab

CL-897
This commit is contained in:
Simon Edwards 2018-09-18 16:27:22 +02:00
parent 942d20a8d8
commit b7673a7438
4 changed files with 268 additions and 5 deletions

View file

@ -12,6 +12,8 @@ if TYPE_CHECKING:
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ConfigurationChangeModel import ConfigurationChangeModel
class PrintJobOutputModel(QObject): class PrintJobOutputModel(QObject):
stateChanged = pyqtSignal() stateChanged = pyqtSignal()
@ -24,6 +26,7 @@ class PrintJobOutputModel(QObject):
configurationChanged = pyqtSignal() configurationChanged = pyqtSignal()
previewImageChanged = pyqtSignal() previewImageChanged = pyqtSignal()
compatibleMachineFamiliesChanged = pyqtSignal() compatibleMachineFamiliesChanged = pyqtSignal()
configurationChangesChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None) -> None: def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None) -> None:
super().__init__(parent) super().__init__(parent)
@ -41,6 +44,7 @@ class PrintJobOutputModel(QObject):
self._preview_image_id = 0 self._preview_image_id = 0
self._preview_image = None # type: Optional[QImage] self._preview_image = None # type: Optional[QImage]
self._configuration_changes = [] # type: List[ConfigurationChangeModel]
@pyqtProperty("QStringList", notify=compatibleMachineFamiliesChanged) @pyqtProperty("QStringList", notify=compatibleMachineFamiliesChanged)
def compatibleMachineFamilies(self): def compatibleMachineFamilies(self):
@ -147,4 +151,14 @@ class PrintJobOutputModel(QObject):
@pyqtSlot(str) @pyqtSlot(str)
def setState(self, state): def setState(self, state):
self._output_controller.setJobState(self, state) self._output_controller.setJobState(self, state)
@pyqtProperty("QVariantList", notify=configurationChangesChanged)
def configurationChanges(self) -> List[ConfigurationChangeModel]:
return self._configuration_changes
def updateConfigurationChanges(self, changes: List[ConfigurationChangeModel]) -> None:
if len(self._configuration_changes) == 0 and len(changes) == 0:
return
self._configuration_changes = changes
self.configurationChangesChanged.emit()

View file

@ -85,7 +85,6 @@ Component
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").height anchors.rightMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").height anchors.leftMargin: UM.Theme.getSize("default_margin").height
height: 175 * screenScaleFactor
} }
} }
} }

View file

@ -2,6 +2,8 @@ import QtQuick 2.2
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import UM 1.3 as UM import UM 1.3 as UM
@ -9,6 +11,110 @@ import UM 1.3 as UM
Item Item
{ {
id: base id: base
function haveAlert() {
return printJob.configurationChanges.length !== 0;
}
function alertHeight() {
return haveAlert() ? 230 : 0;
}
function alertText() {
if (printJob.configurationChanges.length === 0) {
return "";
}
var topLine;
if (materialsAreKnown(printJob)) {
topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printJob.assignedPrinter.name);
} else {
topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printJob.assignedPrinter.name);
}
var result = "<p>" + topLine +"</p>";
for (var i=0; i<printJob.configurationChanges.length; i++) {
var change = printJob.configurationChanges[i];
var text = "";
if (change.typeOfChange === 'material_change') {
text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
} else if (change.typeOfChange === 'material_insert') {
text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName);
} else if (change.typeOfChange === 'print_core_change') {
text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
} else if (change.typeOfChange === 'buildplate_change') {
var target_name = formatBuildPlateType(change.target_name);
text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(target_name);
}
result += "<p><b>" + text + "</b></p>";
}
return result;
}
function materialsAreKnown(printJob) {
var conf0 = printJob.configuration[0];
if (conf0 && !conf0.material.material) {
return false;
}
var conf1 = printJob.configuration[1];
if (conf1 && !conf1.material.material) {
return false;
}
return true;
}
function formatBuildPlateType(buildPlateType) {
var translationText = "";
switch (buildPlateType) {
case 'glass':
translationText = catalog.i18nc("@label", "Glass");
break;
case 'aluminum':
translationText = catalog.i18nc("@label", "Aluminum");
break;
default:
translationText = null;
}
return translationText;
}
function formatPrintJobName(name) {
var extensionsToRemove = [
'.gz',
'.gcode',
'.ufp'
];
for (var i = 0; i < extensionsToRemove.length; i++) {
var extension = extensionsToRemove[i];
if (name.slice(-extension.length) === extension) {
name = name.substring(0, name.length - extension.length);
}
}
return name;
}
function isPrintJobForcable(printJob) {
var forcable = true;
for (var i = 0; i < printJob.configurationChanges.length; i++) {
var typeOfChange = printJob.configurationChanges[i].typeOfChange;
if (typeOfChange === 'material_insert' || typeOfChange === 'buildplate_change') {
forcable = false;
}
}
return forcable;
}
property var cardHeight: 175
height: (cardHeight + alertHeight()) * screenScaleFactor
property var printJob: null property var printJob: null
property var shadowRadius: 5 * screenScaleFactor property var shadowRadius: 5 * screenScaleFactor
function getPrettyTime(time) function getPrettyTime(time)
@ -27,6 +133,9 @@ Item
Rectangle Rectangle
{ {
id: background id: background
height: (cardHeight + alertHeight()) * screenScaleFactor
anchors anchors
{ {
top: parent.top top: parent.top
@ -35,7 +144,7 @@ Item
leftMargin: base.shadowRadius leftMargin: base.shadowRadius
rightMargin: base.shadowRadius rightMargin: base.shadowRadius
right: parent.right right: parent.right
bottom: parent.bottom //bottom: parent.bottom - alertHeight() * screenScaleFactor
bottomMargin: base.shadowRadius bottomMargin: base.shadowRadius
} }
@ -47,6 +156,18 @@ Item
color: "#3F000000" // 25% shadow color: "#3F000000" // 25% shadow
} }
Rectangle
{
height: cardHeight * screenScaleFactor
anchors
{
top: parent.top
left: parent.left
right: parent.right
// bottom: parent.bottom
}
Item Item
{ {
// Content on the left of the infobox // Content on the left of the infobox
@ -392,15 +513,128 @@ Item
} }
} }
}
Rectangle Rectangle
{ {
height: cardHeight * screenScaleFactor
color: UM.Theme.getColor("viewport_background") color: UM.Theme.getColor("viewport_background")
width: 2 * screenScaleFactor width: 2 * screenScaleFactor
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: UM.Theme.getSize("default_margin").height anchors.margins: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
// Alert / Configuration change box
Rectangle
{
height: alertHeight() * screenScaleFactor
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
color: "#ff00ff"
ColumnLayout
{
anchors.fill: parent
RowLayout
{
Item
{
Layout.fillWidth: true
}
Label
{
font: UM.Theme.getFont("default_bold")
text: "Configuration change"
}
UM.RecolorImage
{
id: collapseIcon
width: 15
height: 15
sourceSize.width: width
sourceSize.height: height
// FIXME
source: base.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
color: "black"
}
Item
{
Layout.fillWidth: true
}
}
Rectangle
{
Layout.fillHeight: true
Layout.fillWidth: true
color: "red"
Rectangle
{
color: "green"
width: childrenRect.width
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
ColumnLayout
{
width: childrenRect.width
anchors.top: parent.top
anchors.bottom: parent.bottom
Text
{
Layout.alignment: Qt.AlignTop
textFormat: Text.StyledText
font: UM.Theme.getFont("default_bold")
text: alertText()
}
Button
{
visible: isPrintJobForcable(printJob)
text: catalog.i18nc("@label", "Override")
onClicked: {
overrideConfirmationDialog.visible = true;
}
}
// Spacer
Item
{
Layout.fillHeight: true
}
}
}
}
}
}
MessageDialog
{
id: overrideConfirmationDialog
title: catalog.i18nc("@window:title", "Override configuration")
icon: StandardIcon.Warning
text: {
var printJobName = formatPrintJobName(printJob.name);
var confirmText = catalog.i18nc("@label", "Starting a print job with an incompatible configuration could damage your 3D printer. Are you sure you want to override the configuration and print %1?").arg(printJobName);
return confirmText;
}
standardButtons: StandardButton.Yes | StandardButton.No
Component.onCompleted: visible = false
onYes: OutputDevice.forceSendJob(printJob.key)
}
} }
} }

View file

@ -17,6 +17,7 @@ from UM.Scene.SceneNode import SceneNode # For typing.
from UM.Version import Version # To check against firmware versions for support. from UM.Version import Version # To check against firmware versions for support.
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.ConfigurationChangeModel import ConfigurationChangeModel
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
@ -406,6 +407,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# is a modification of the cluster queue and not of the actual job. # is a modification of the cluster queue and not of the actual job.
self.delete("print_jobs/{uuid}".format(uuid = print_job_uuid), on_finished=None) self.delete("print_jobs/{uuid}".format(uuid = print_job_uuid), on_finished=None)
@pyqtSlot(str)
def forceSendJob(self, print_job_uuid: str) -> None:
data = "{\"force\": true}"
self.put("print_jobs/{uuid}".format(uuid=print_job_uuid), data, on_finished=None)
def _printJobStateChanged(self) -> None: def _printJobStateChanged(self) -> None:
username = self._getUserName() username = self._getUserName()
@ -574,6 +580,16 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if not status_set_by_impediment: if not status_set_by_impediment:
print_job.updateState(data["status"]) print_job.updateState(data["status"])
print_job.updateConfigurationChanges(self._createConfigurationChanges(data["configuration_changes_required"]))
def _createConfigurationChanges(self, data: List[Dict[str, Any]]) -> List[ConfigurationChangeModel]:
result = []
for change in data:
result.append(ConfigurationChangeModel(type_of_change=change["type_of_change"],
index=change["index"],
target_name=change["target_name"],
origin_name=change["origin_name"]))
return result
def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel: def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel:
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"]) containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"])