Bring back the printer selection dialog for networked printers

This commit is contained in:
ChrisTerBeke 2019-10-25 12:25:41 +02:00
parent d905b8b8cc
commit 9a3ff527ac
4 changed files with 80 additions and 42 deletions

View file

@ -35,6 +35,7 @@ class PrinterOutputModel(QObject):
self._target_bed_temperature = 0 # type: float self._target_bed_temperature = 0 # type: float
self._name = "" self._name = ""
self._key = "" # Unique identifier self._key = "" # Unique identifier
self._unique_name = "" # Unique name (used in Connect)
self._controller = output_controller self._controller = output_controller
self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged) self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged)
self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)] self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
@ -190,6 +191,15 @@ class PrinterOutputModel(QObject):
self._name = name self._name = name
self.nameChanged.emit() self.nameChanged.emit()
@pyqtProperty(str, notify = nameChanged)
def uniqueName(self) -> str:
return self._unique_name
def updateUniqueName(self, unique_name: str) -> None:
if self._unique_name != unique_name:
self._unique_name = unique_name
self.nameChanged.emit()
## Update the bed temperature. This only changes it locally. ## Update the bed temperature. This only changes it locally.
def updateBedTemperature(self, temperature: float) -> None: def updateBedTemperature(self, temperature: float) -> None:
if self._bed_temperature != temperature: if self._bed_temperature != temperature:

View file

@ -1,52 +1,57 @@
// Copyright (c) 2019 Ultimaker B.V. // Copyright (c) 2019 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import QtQuick.Controls 1.2 import QtQuick.Controls 1.2
import UM 1.1 as UM import UM 1.1 as UM
UM.Dialog { UM.Dialog {
id: base; id: base;
title: catalog.i18nc("@title:window", "Print over network");
width: minimumWidth;
height: minimumHeight; height: minimumHeight;
leftButtons: [
Button {
enabled: true;
onClicked: {
base.visible = false;
printerSelectionCombobox.currentIndex = 0;
OutputDevice.cancelPrintSelection();
}
text: catalog.i18nc("@action:button","Cancel");
}
]
maximumHeight: minimumHeight; maximumHeight: minimumHeight;
maximumWidth: minimumWidth; maximumWidth: minimumWidth;
minimumHeight: 140 * screenScaleFactor; minimumHeight: 140 * screenScaleFactor;
minimumWidth: 500 * screenScaleFactor; minimumWidth: 500 * screenScaleFactor;
modality: Qt.ApplicationModal; modality: Qt.ApplicationModal;
onVisibleChanged: {
if (visible) { Component.onCompleted: {
resetPrintersModel(); populateComboBox()
} else { }
OutputDevice.cancelPrintSelection();
// populates the combo box with the correct printer values
function populateComboBox() {
comboBoxPrintersModel.clear();
comboBoxPrintersModel.append({ name: "Automatic", key: "" }); // Connect will just do it's thing
for (var i in OutputDevice.printers) {
comboBoxPrintersModel.append({
name: OutputDevice.printers[i].name,
key: OutputDevice.printers[i].uniqueName
});
} }
} }
leftButtons: [
Button {
enabled: true;
onClicked: {
base.close();
}
text: catalog.i18nc("@action:button","Cancel");
}
]
rightButtons: [ rightButtons: [
Button { Button {
enabled: true; enabled: true;
onClicked: { onClicked: {
base.visible = false; OutputDevice.selectTargetPrinter(printerComboBox.model.get(printerComboBox.currentIndex).key);
OutputDevice.selectPrinter(printerSelectionCombobox.model.get(printerSelectionCombobox.currentIndex).key); base.close();
// reset to defaults
printerSelectionCombobox.currentIndex = 0;
} }
text: catalog.i18nc("@action:button","Print"); text: catalog.i18nc("@action:button","Print");
} }
] ]
title: catalog.i18nc("@title:window", "Print over network");
visible: true;
width: minimumWidth;
Column { Column {
id: printerSelection; id: printerSelection;
@ -59,10 +64,6 @@ UM.Dialog {
} }
height: 50 * screenScaleFactor; height: 50 * screenScaleFactor;
SystemPalette {
id: palette;
}
UM.I18nCatalog { UM.I18nCatalog {
id: catalog; id: catalog;
name: "cura"; name: "cura";
@ -82,23 +83,14 @@ UM.Dialog {
} }
ComboBox { ComboBox {
id: printerSelectionCombobox; id: printerComboBox;
Behavior on height { NumberAnimation { duration: 100 } } Behavior on height { NumberAnimation { duration: 100 } }
height: 40 * screenScaleFactor; height: 40 * screenScaleFactor;
model: ListModel { model: ListModel {
id: printersModel; id: comboBoxPrintersModel;
} }
textRole: "name"; textRole: "name";
width: parent.width; width: parent.width;
} }
} }
// Utils
function resetPrintersModel() {
printersModel.clear();
printersModel.append({ name: "Automatic", key: ""});
for (var index in OutputDevice.printers) {
printersModel.append({name: OutputDevice.printers[index].name, key: OutputDevice.printers[index].key});
}
}
} }

View file

@ -79,6 +79,7 @@ class ClusterPrinterStatus(BaseModel):
def updateOutputModel(self, model: PrinterOutputModel) -> None: def updateOutputModel(self, model: PrinterOutputModel) -> None:
model.updateKey(self.uuid) model.updateKey(self.uuid)
model.updateName(self.friendly_name) model.updateName(self.friendly_name)
model.updateUniqueName(self.unique_name)
model.updateType(self.machine_variant) model.updateType(self.machine_variant)
model.updateState(self.status if self.enabled else "disabled") model.updateState(self.status if self.enabled else "disabled")
model.updateBuildplate(self.build_plate.type if self.build_plate else "glass") model.updateBuildplate(self.build_plate.type if self.build_plate else "glass")

View file

@ -1,16 +1,17 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import Optional, Dict, List, Callable, Any from typing import Optional, Dict, List, Callable, Any
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
from PyQt5.QtNetwork import QNetworkReply from PyQt5.QtNetwork import QNetworkReply
from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.FileHandler import FileHandler
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Logger import Logger from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
@ -42,6 +43,8 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
) )
self._cluster_api = None # type: Optional[ClusterApiClient] self._cluster_api = None # type: Optional[ClusterApiClient]
self._active_exported_job = None # type: Optional[ExportFileJob]
self._printer_select_dialog = None # type: Optional[QObject]
# We don't have authentication over local networking, so we're always authenticated. # We don't have authentication over local networking, so we're always authenticated.
self.setAuthenticationState(AuthState.Authenticated) self.setAuthenticationState(AuthState.Authenticated)
@ -129,17 +132,49 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
job.finished.connect(self._onPrintJobCreated) job.finished.connect(self._onPrintJobCreated)
job.start() job.start()
## Allows the user to choose a printer to print with from the printer selection dialogue.
# \param unique_name: The unique name of the printer to target.
@pyqtSlot(str, name="selectTargetPrinter")
def selectTargetPrinter(self, unique_name: str = "") -> None:
self._startPrintJobUpload(unique_name if unique_name != "" else None)
## Handler for when the print job was created locally. ## Handler for when the print job was created locally.
# It can now be sent over the network. # It can now be sent over the network.
def _onPrintJobCreated(self, job: ExportFileJob) -> None: def _onPrintJobCreated(self, job: ExportFileJob) -> None:
self._active_exported_job = job
# TODO: add preference to enable/disable this feature?
if self.clusterSize > 1:
self._showPrinterSelectionDialog() # self._startPrintJobUpload will be triggered from this dialog
return
self._startPrintJobUpload()
## Shows a dialog allowing the user to select which printer in a group to send a job to.
def _showPrinterSelectionDialog(self) -> None:
if not self._printer_select_dialog:
path = os.path.join(CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting"),
"resources", "qml", "PrintWindow.qml")
self._printer_select_dialog = CuraApplication.getInstance().createQmlComponent(path, {"OutputDevice": self})
self._printer_select_dialog.show()
## Upload the print job to the group.
def _startPrintJobUpload(self, unique_name: str = None) -> None:
if not self._active_exported_job:
Logger.log("e", "No active exported job to upload!")
return
self._progress.show() self._progress.show()
parts = [ parts = [
self._createFormPart("name=owner", bytes(self._getUserName(), "utf-8"), "text/plain"), self._createFormPart("name=owner", bytes(self._getUserName(), "utf-8"), "text/plain"),
self._createFormPart("name=\"file\"; filename=\"%s\"" % job.getFileName(), job.getOutput()) self._createFormPart("name=\"file\"; filename=\"%s\"" % self._active_exported_job.getFileName(),
self._active_exported_job.getOutput())
] ]
# If a specific printer was selected we include the name in the request.
# FIXME: Connect should allow the printer UUID here instead of the 'unique_name'.
if unique_name is not None:
parts.append(self._createFormPart("name=require_printer_name", bytes(unique_name, "utf-8"), "text/plain"))
# FIXME: move form posting to API client # FIXME: move form posting to API client
self.postFormWithParts("/cluster-api/v1/print_jobs/", parts, on_finished=self._onPrintUploadCompleted, self.postFormWithParts("/cluster-api/v1/print_jobs/", parts, on_finished=self._onPrintUploadCompleted,
on_progress=self._onPrintJobUploadProgress) on_progress=self._onPrintJobUploadProgress)
self._active_exported_job = None
## Handler for print job upload progress. ## Handler for print job upload progress.
def _onPrintJobUploadProgress(self, bytes_sent: int, bytes_total: int) -> None: def _onPrintJobUploadProgress(self, bytes_sent: int, bytes_total: int) -> None: