mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge pull request #20680 from Ultimaker/CURA-12557_handle-deactivated-things
CURA-12557 handle deactivated things
This commit is contained in:
commit
4e04d06db0
16 changed files with 207 additions and 48 deletions
|
@ -33,8 +33,8 @@ class AuthState(IntEnum):
|
||||||
class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||||
authenticationStateChanged = pyqtSignal()
|
authenticationStateChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.NetworkConnection, parent: QObject = None) -> None:
|
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.NetworkConnection, parent: QObject = None, active: bool = True) -> None:
|
||||||
super().__init__(device_id = device_id, connection_type = connection_type, parent = parent)
|
super().__init__(device_id = device_id, connection_type = connection_type, parent = parent, active = active)
|
||||||
self._manager = None # type: Optional[QNetworkAccessManager]
|
self._manager = None # type: Optional[QNetworkAccessManager]
|
||||||
self._timeout_time = 10 # After how many seconds of no response should a timeout occur?
|
self._timeout_time = 10 # After how many seconds of no response should a timeout occur?
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,10 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||||
# Signal to indicate that the configuration of one of the printers has changed.
|
# Signal to indicate that the configuration of one of the printers has changed.
|
||||||
uniqueConfigurationsChanged = pyqtSignal()
|
uniqueConfigurationsChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.NotConnected, parent: QObject = None) -> None:
|
# Signal to indicate that the printer has become active or inactive
|
||||||
|
activeChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.NotConnected, parent: QObject = None, active: bool = True) -> None:
|
||||||
super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance
|
super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance
|
||||||
|
|
||||||
self._printers = [] # type: List[PrinterOutputModel]
|
self._printers = [] # type: List[PrinterOutputModel]
|
||||||
|
@ -88,6 +91,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||||
|
|
||||||
self._accepts_commands = False # type: bool
|
self._accepts_commands = False # type: bool
|
||||||
|
|
||||||
|
self._active: bool = active
|
||||||
|
|
||||||
self._update_timer = QTimer() # type: QTimer
|
self._update_timer = QTimer() # type: QTimer
|
||||||
self._update_timer.setInterval(2000) # TODO; Add preference for update interval
|
self._update_timer.setInterval(2000) # TODO; Add preference for update interval
|
||||||
self._update_timer.setSingleShot(False)
|
self._update_timer.setSingleShot(False)
|
||||||
|
@ -295,3 +300,17 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._firmware_updater.updateFirmware(firmware_file)
|
self._firmware_updater.updateFirmware(firmware_file)
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = activeChanged)
|
||||||
|
def active(self) -> bool:
|
||||||
|
"""
|
||||||
|
Indicates whether the printer is active, which is not the same as "being the active printer". In this case,
|
||||||
|
active means that the printer can be used. An example of an inactive printer is one that cannot be used because
|
||||||
|
the user doesn't have enough seats on Digital Factory.
|
||||||
|
"""
|
||||||
|
return self._active
|
||||||
|
|
||||||
|
def _setActive(self, active: bool) -> None:
|
||||||
|
if active != self._active:
|
||||||
|
self._active = active
|
||||||
|
self.activeChanged.emit()
|
||||||
|
|
|
@ -183,10 +183,14 @@ class MachineManager(QObject):
|
||||||
self.setActiveMachine(active_machine_id)
|
self.setActiveMachine(active_machine_id)
|
||||||
|
|
||||||
def _onOutputDevicesChanged(self) -> None:
|
def _onOutputDevicesChanged(self) -> None:
|
||||||
|
for printer_output_device in self._printer_output_devices:
|
||||||
|
printer_output_device.activeChanged.disconnect(self.printerConnectedStatusChanged)
|
||||||
|
|
||||||
self._printer_output_devices = []
|
self._printer_output_devices = []
|
||||||
for printer_output_device in self._application.getOutputDeviceManager().getOutputDevices():
|
for printer_output_device in self._application.getOutputDeviceManager().getOutputDevices():
|
||||||
if isinstance(printer_output_device, PrinterOutputDevice):
|
if isinstance(printer_output_device, PrinterOutputDevice):
|
||||||
self._printer_output_devices.append(printer_output_device)
|
self._printer_output_devices.append(printer_output_device)
|
||||||
|
printer_output_device.activeChanged.connect(self.printerConnectedStatusChanged)
|
||||||
|
|
||||||
self.outputDevicesChanged.emit()
|
self.outputDevicesChanged.emit()
|
||||||
|
|
||||||
|
@ -569,6 +573,13 @@ class MachineManager(QObject):
|
||||||
def activeMachineIsUsingCloudConnection(self) -> bool:
|
def activeMachineIsUsingCloudConnection(self) -> bool:
|
||||||
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
|
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||||
|
def activeMachineIsActive(self) -> bool:
|
||||||
|
if not self._printer_output_devices:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return self._printer_output_devices[0].active
|
||||||
|
|
||||||
def activeMachineNetworkKey(self) -> str:
|
def activeMachineNetworkKey(self) -> str:
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
return self._global_container_stack.getMetaDataEntry("um_network_key", "")
|
return self._global_container_stack.getMetaDataEntry("um_network_key", "")
|
||||||
|
|
|
@ -11,10 +11,10 @@ Cura.RoundedRectangle
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: projectImage.height + 2 * UM.Theme.getSize("default_margin").width
|
height: projectImage.height + 2 * UM.Theme.getSize("default_margin").width
|
||||||
cornerSide: Cura.RoundedRectangle.Direction.All
|
cornerSide: Cura.RoundedRectangle.Direction.All
|
||||||
border.color: UM.Theme.getColor("lining")
|
border.color: enabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("action_button_disabled_border")
|
||||||
border.width: UM.Theme.getSize("default_lining").width
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
radius: UM.Theme.getSize("default_radius").width
|
radius: UM.Theme.getSize("default_radius").width
|
||||||
color: UM.Theme.getColor("main_background")
|
color: getBackgroundColor()
|
||||||
signal clicked()
|
signal clicked()
|
||||||
property alias imageSource: projectImage.source
|
property alias imageSource: projectImage.source
|
||||||
property alias projectNameText: displayNameLabel.text
|
property alias projectNameText: displayNameLabel.text
|
||||||
|
@ -22,17 +22,18 @@ Cura.RoundedRectangle
|
||||||
property alias projectLastUpdatedText: lastUpdatedLabel.text
|
property alias projectLastUpdatedText: lastUpdatedLabel.text
|
||||||
property alias cardMouseAreaEnabled: cardMouseArea.enabled
|
property alias cardMouseAreaEnabled: cardMouseArea.enabled
|
||||||
|
|
||||||
onVisibleChanged: color = UM.Theme.getColor("main_background")
|
onVisibleChanged: color = getBackgroundColor()
|
||||||
|
|
||||||
MouseArea
|
MouseArea
|
||||||
{
|
{
|
||||||
id: cardMouseArea
|
id: cardMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: base.enabled
|
||||||
onEntered: base.color = UM.Theme.getColor("action_button_hovered")
|
onEntered: color = getBackgroundColor()
|
||||||
onExited: base.color = UM.Theme.getColor("main_background")
|
onExited: color = getBackgroundColor()
|
||||||
onClicked: base.clicked()
|
onClicked: base.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
Row
|
Row
|
||||||
{
|
{
|
||||||
id: projectInformationRow
|
id: projectInformationRow
|
||||||
|
@ -73,7 +74,7 @@ Cura.RoundedRectangle
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Math.round(parent.height / 3)
|
height: Math.round(parent.height / 3)
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: UM.Theme.getColor("small_button_text")
|
color: base.enabled ? UM.Theme.getColor("small_button_text") : UM.Theme.getColor("text_disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
UM.Label
|
UM.Label
|
||||||
|
@ -82,8 +83,27 @@ Cura.RoundedRectangle
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Math.round(parent.height / 3)
|
height: Math.round(parent.height / 3)
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: UM.Theme.getColor("small_button_text")
|
color: base.enabled ? UM.Theme.getColor("small_button_text") : UM.Theme.getColor("text_disabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function getBackgroundColor()
|
||||||
|
{
|
||||||
|
if(enabled)
|
||||||
|
{
|
||||||
|
if(cardMouseArea.containsMouse)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("action_button_hovered")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("main_background")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("action_button_disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -159,17 +159,30 @@ Item
|
||||||
Repeater
|
Repeater
|
||||||
{
|
{
|
||||||
model: manager.digitalFactoryProjectModel
|
model: manager.digitalFactoryProjectModel
|
||||||
delegate: ProjectSummaryCard
|
delegate: Item
|
||||||
{
|
{
|
||||||
id: projectSummaryCard
|
width: parent.width
|
||||||
imageSource: model.thumbnailUrl || "../images/placeholder.svg"
|
height: projectSummaryCard.height
|
||||||
projectNameText: model.displayName
|
|
||||||
projectUsernameText: model.username
|
|
||||||
projectLastUpdatedText: "Last updated: " + model.lastUpdated
|
|
||||||
|
|
||||||
onClicked:
|
UM.TooltipArea
|
||||||
{
|
{
|
||||||
manager.selectedProjectIndex = index
|
anchors.fill: parent
|
||||||
|
text: "This project is inactive and cannot be used."
|
||||||
|
enabled: !model.active
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectSummaryCard
|
||||||
|
{
|
||||||
|
id: projectSummaryCard
|
||||||
|
imageSource: model.thumbnailUrl || "../images/placeholder.svg"
|
||||||
|
projectNameText: model.displayName
|
||||||
|
projectUsernameText: model.username
|
||||||
|
projectLastUpdatedText: "Last updated: " + model.lastUpdated
|
||||||
|
enabled: model.active
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
manager.selectedProjectIndex = index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ class DigitalFactoryProjectModel(ListModel):
|
||||||
ThumbnailUrlRole = Qt.ItemDataRole.UserRole + 5
|
ThumbnailUrlRole = Qt.ItemDataRole.UserRole + 5
|
||||||
UsernameRole = Qt.ItemDataRole.UserRole + 6
|
UsernameRole = Qt.ItemDataRole.UserRole + 6
|
||||||
LastUpdatedRole = Qt.ItemDataRole.UserRole + 7
|
LastUpdatedRole = Qt.ItemDataRole.UserRole + 7
|
||||||
|
ActiveRole = Qt.ItemDataRole.UserRole + 8
|
||||||
|
|
||||||
dfProjectModelChanged = pyqtSignal()
|
dfProjectModelChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ class DigitalFactoryProjectModel(ListModel):
|
||||||
self.addRoleName(self.ThumbnailUrlRole, "thumbnailUrl")
|
self.addRoleName(self.ThumbnailUrlRole, "thumbnailUrl")
|
||||||
self.addRoleName(self.UsernameRole, "username")
|
self.addRoleName(self.UsernameRole, "username")
|
||||||
self.addRoleName(self.LastUpdatedRole, "lastUpdated")
|
self.addRoleName(self.LastUpdatedRole, "lastUpdated")
|
||||||
|
self.addRoleName(self.ActiveRole, "active")
|
||||||
self._projects = [] # type: List[DigitalFactoryProjectResponse]
|
self._projects = [] # type: List[DigitalFactoryProjectResponse]
|
||||||
|
|
||||||
def setProjects(self, df_projects: List[DigitalFactoryProjectResponse]) -> None:
|
def setProjects(self, df_projects: List[DigitalFactoryProjectResponse]) -> None:
|
||||||
|
@ -59,5 +61,6 @@ class DigitalFactoryProjectModel(ListModel):
|
||||||
"thumbnailUrl": project.thumbnail_url,
|
"thumbnailUrl": project.thumbnail_url,
|
||||||
"username": project.username,
|
"username": project.username,
|
||||||
"lastUpdated": project.last_updated.strftime(PROJECT_UPDATED_AT_DATETIME_FORMAT) if project.last_updated else "",
|
"lastUpdated": project.last_updated.strftime(PROJECT_UPDATED_AT_DATETIME_FORMAT) if project.last_updated else "",
|
||||||
|
"active": project.active,
|
||||||
})
|
})
|
||||||
self.dfProjectModelChanged.emit()
|
self.dfProjectModelChanged.emit()
|
||||||
|
|
|
@ -28,6 +28,7 @@ class DigitalFactoryProjectResponse(BaseModel):
|
||||||
team_ids: Optional[List[str]] = None,
|
team_ids: Optional[List[str]] = None,
|
||||||
status: Optional[str] = None,
|
status: Optional[str] = None,
|
||||||
technical_requirements: Optional[Dict[str, Any]] = None,
|
technical_requirements: Optional[Dict[str, Any]] = None,
|
||||||
|
is_inactive: bool = False,
|
||||||
**kwargs) -> None:
|
**kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a new digital factory project response object
|
Creates a new digital factory project response object
|
||||||
|
@ -56,6 +57,7 @@ class DigitalFactoryProjectResponse(BaseModel):
|
||||||
self.last_updated = datetime.strptime(last_updated, DIGITAL_FACTORY_RESPONSE_DATETIME_FORMAT) if last_updated else None
|
self.last_updated = datetime.strptime(last_updated, DIGITAL_FACTORY_RESPONSE_DATETIME_FORMAT) if last_updated else None
|
||||||
self.status = status
|
self.status = status
|
||||||
self.technical_requirements = technical_requirements
|
self.technical_requirements = technical_requirements
|
||||||
|
self.active = not is_inactive
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
|
|
@ -163,7 +163,7 @@ class CloudApiClient:
|
||||||
scope=self._scope,
|
scope=self._scope,
|
||||||
data=b"",
|
data=b"",
|
||||||
callback=self._parseCallback(on_finished, CloudPrintResponse),
|
callback=self._parseCallback(on_finished, CloudPrintResponse),
|
||||||
error_callback=on_error,
|
error_callback=self._parseError(on_error),
|
||||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str,
|
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str,
|
||||||
|
@ -256,7 +256,6 @@ class CloudApiClient:
|
||||||
"""Creates a callback function so that it includes the parsing of the response into the correct model.
|
"""Creates a callback function so that it includes the parsing of the response into the correct model.
|
||||||
|
|
||||||
The callback is added to the 'finished' signal of the reply.
|
The callback is added to the 'finished' signal of the reply.
|
||||||
:param reply: The reply that should be listened to.
|
|
||||||
:param on_finished: The callback in case the response is successful. Depending on the endpoint it will be either
|
:param on_finished: The callback in case the response is successful. Depending on the endpoint it will be either
|
||||||
a list or a single item.
|
a list or a single item.
|
||||||
:param model: The type of the model to convert the response to.
|
:param model: The type of the model to convert the response to.
|
||||||
|
@ -281,6 +280,25 @@ class CloudApiClient:
|
||||||
self._anti_gc_callbacks.append(parse)
|
self._anti_gc_callbacks.append(parse)
|
||||||
return parse
|
return parse
|
||||||
|
|
||||||
|
def _parseError(self,
|
||||||
|
on_error: Callable[[CloudError, "QNetworkReply.NetworkError", int], None]) -> Callable[[QNetworkReply, "QNetworkReply.NetworkError"], None]:
|
||||||
|
|
||||||
|
"""Creates a callback function so that it includes the parsing of an explicit error response into the correct model.
|
||||||
|
|
||||||
|
:param on_error: The callback in case the response gives an explicit error
|
||||||
|
"""
|
||||||
|
|
||||||
|
def parse(reply: QNetworkReply, error: "QNetworkReply.NetworkError") -> None:
|
||||||
|
|
||||||
|
self._anti_gc_callbacks.remove(parse)
|
||||||
|
|
||||||
|
http_code, response = self._parseReply(reply)
|
||||||
|
result = CloudError(**response["errors"][0])
|
||||||
|
on_error(result, error, http_code)
|
||||||
|
|
||||||
|
self._anti_gc_callbacks.append(parse)
|
||||||
|
return parse
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getMachineIDMap(cls) -> Dict[str, str]:
|
def getMachineIDMap(cls) -> Dict[str, str]:
|
||||||
if cls._machine_id_to_name is None:
|
if cls._machine_id_to_name is None:
|
||||||
|
|
|
@ -27,9 +27,11 @@ from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOut
|
||||||
from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage
|
from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage
|
||||||
from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage
|
from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage
|
||||||
from ..Messages.PrintJobUploadQueueFullMessage import PrintJobUploadQueueFullMessage
|
from ..Messages.PrintJobUploadQueueFullMessage import PrintJobUploadQueueFullMessage
|
||||||
|
from ..Messages.PrintJobUploadPrinterInactiveMessage import PrintJobUploadPrinterInactiveMessage
|
||||||
from ..Messages.PrintJobUploadSuccessMessage import PrintJobUploadSuccessMessage
|
from ..Messages.PrintJobUploadSuccessMessage import PrintJobUploadSuccessMessage
|
||||||
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||||
from ..Models.Http.CloudClusterStatus import CloudClusterStatus
|
from ..Models.Http.CloudClusterStatus import CloudClusterStatus
|
||||||
|
from ..Models.Http.CloudError import CloudError
|
||||||
from ..Models.Http.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest
|
from ..Models.Http.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest
|
||||||
from ..Models.Http.CloudPrintResponse import CloudPrintResponse
|
from ..Models.Http.CloudPrintResponse import CloudPrintResponse
|
||||||
from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse
|
from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse
|
||||||
|
@ -87,7 +89,8 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||||
address="",
|
address="",
|
||||||
connection_type=ConnectionType.CloudConnection,
|
connection_type=ConnectionType.CloudConnection,
|
||||||
properties=properties,
|
properties=properties,
|
||||||
parent=parent
|
parent=parent,
|
||||||
|
active=cluster.display_status != "inactive"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._api = api_client
|
self._api = api_client
|
||||||
|
@ -190,6 +193,8 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||||
self._received_print_jobs = status.print_jobs
|
self._received_print_jobs = status.print_jobs
|
||||||
self._updatePrintJobs(status.print_jobs)
|
self._updatePrintJobs(status.print_jobs)
|
||||||
|
|
||||||
|
self._setActive(status.active)
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
file_handler: Optional[FileHandler] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
file_handler: Optional[FileHandler] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
|
|
||||||
|
@ -291,19 +296,21 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||||
|
|
||||||
self.writeFinished.emit()
|
self.writeFinished.emit()
|
||||||
|
|
||||||
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
|
def _onPrintUploadSpecificError(self, error: CloudError, _: "QNetworkReply.NetworkError", http_error: int):
|
||||||
"""
|
"""
|
||||||
Displays a message when an error occurs specific to uploading print job (i.e. queue is full).
|
Displays a message when an error occurs specific to uploading print job (i.e. queue is full).
|
||||||
"""
|
"""
|
||||||
error_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute)
|
if http_error == 409:
|
||||||
if error_code == 409:
|
if error.code == "printerInactive":
|
||||||
PrintJobUploadQueueFullMessage().show()
|
PrintJobUploadPrinterInactiveMessage().show()
|
||||||
|
else:
|
||||||
|
PrintJobUploadQueueFullMessage().show()
|
||||||
else:
|
else:
|
||||||
PrintJobUploadErrorMessage(I18N_CATALOG.i18nc("@error:send",
|
PrintJobUploadErrorMessage(I18N_CATALOG.i18nc("@error:send",
|
||||||
"Unknown error code when uploading print job: {0}",
|
"Unknown error code when uploading print job: {0}",
|
||||||
error_code)).show()
|
http_error)).show()
|
||||||
|
|
||||||
Logger.log("w", "Upload of print job failed specifically with error code {}".format(error_code))
|
Logger.log("w", "Upload of print job failed specifically with error code {}".format(http_error))
|
||||||
|
|
||||||
self._progress.hide()
|
self._progress.hide()
|
||||||
self._pre_upload_print_job = None
|
self._pre_upload_print_job = None
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright (c) 2020 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM import i18nCatalog
|
||||||
|
from UM.Message import Message
|
||||||
|
|
||||||
|
|
||||||
|
I18N_CATALOG = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
class PrintJobUploadPrinterInactiveMessage(Message):
|
||||||
|
"""Message shown when uploading a print job to a cluster and the printer is inactive."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(
|
||||||
|
text = I18N_CATALOG.i18nc("@info:status", "The printer is inactive and cannot accept a new print job."),
|
||||||
|
title = I18N_CATALOG.i18nc("@info:title", "Printer inactive"),
|
||||||
|
lifetime = 10,
|
||||||
|
message_type=Message.MessageType.ERROR
|
||||||
|
)
|
|
@ -10,7 +10,7 @@ class CloudClusterResponse(BaseModel):
|
||||||
"""Class representing a cloud connected cluster."""
|
"""Class representing a cloud connected cluster."""
|
||||||
|
|
||||||
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
||||||
host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
display_status: str, host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
||||||
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", printer_count: int = 1,
|
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", printer_count: int = 1,
|
||||||
capabilities: Optional[List[str]] = None, **kwargs) -> None:
|
capabilities: Optional[List[str]] = None, **kwargs) -> None:
|
||||||
"""Creates a new cluster response object.
|
"""Creates a new cluster response object.
|
||||||
|
@ -20,6 +20,7 @@ class CloudClusterResponse(BaseModel):
|
||||||
:param host_name: The name of the printer as configured during the Wi-Fi setup. Used as identifier for end users.
|
:param host_name: The name of the printer as configured during the Wi-Fi setup. Used as identifier for end users.
|
||||||
:param is_online: Whether this cluster is currently connected to the cloud.
|
:param is_online: Whether this cluster is currently connected to the cloud.
|
||||||
:param status: The status of the cluster authentication (active or inactive).
|
:param status: The status of the cluster authentication (active or inactive).
|
||||||
|
:param display_status: The display status of the cluster.
|
||||||
:param host_version: The firmware version of the cluster host. This is where the Stardust client is running on.
|
:param host_version: The firmware version of the cluster host. This is where the Stardust client is running on.
|
||||||
:param host_internal_ip: The internal IP address of the host printer.
|
:param host_internal_ip: The internal IP address of the host printer.
|
||||||
:param friendly_name: The human readable name of the host printer.
|
:param friendly_name: The human readable name of the host printer.
|
||||||
|
@ -31,6 +32,7 @@ class CloudClusterResponse(BaseModel):
|
||||||
self.host_guid = host_guid
|
self.host_guid = host_guid
|
||||||
self.host_name = host_name
|
self.host_name = host_name
|
||||||
self.status = status
|
self.status = status
|
||||||
|
self.display_status = display_status
|
||||||
self.is_online = is_online
|
self.is_online = is_online
|
||||||
self.host_version = host_version
|
self.host_version = host_version
|
||||||
self.host_internal_ip = host_internal_ip
|
self.host_internal_ip = host_internal_ip
|
||||||
|
@ -51,5 +53,5 @@ class CloudClusterResponse(BaseModel):
|
||||||
Convenience function for printing when debugging.
|
Convenience function for printing when debugging.
|
||||||
:return: A human-readable representation of the data in this object.
|
:return: A human-readable representation of the data in this object.
|
||||||
"""
|
"""
|
||||||
return str({k: v for k, v in self.__dict__.items() if k in {"cluster_id", "host_guid", "host_name", "status", "is_online", "host_version", "host_internal_ip", "friendly_name", "printer_type", "printer_count", "capabilities"}})
|
return str({k: v for k, v in self.__dict__.items() if k in {"cluster_id", "host_guid", "host_name", "status", "display_status", "is_online", "host_version", "host_internal_ip", "friendly_name", "printer_type", "printer_count", "capabilities"}})
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class CloudClusterStatus(BaseModel):
|
||||||
def __init__(self, printers: List[Union[ClusterPrinterStatus, Dict[str, Any]]],
|
def __init__(self, printers: List[Union[ClusterPrinterStatus, Dict[str, Any]]],
|
||||||
print_jobs: List[Union[ClusterPrintJobStatus, Dict[str, Any]]],
|
print_jobs: List[Union[ClusterPrintJobStatus, Dict[str, Any]]],
|
||||||
generated_time: Union[str, datetime],
|
generated_time: Union[str, datetime],
|
||||||
|
unavailable: bool = False,
|
||||||
**kwargs) -> None:
|
**kwargs) -> None:
|
||||||
"""Creates a new cluster status model object.
|
"""Creates a new cluster status model object.
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ class CloudClusterStatus(BaseModel):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.generated_time = self.parseDate(generated_time)
|
self.generated_time = self.parseDate(generated_time)
|
||||||
|
self.active = not unavailable
|
||||||
self.printers = self.parseModels(ClusterPrinterStatus, printers)
|
self.printers = self.parseModels(ClusterPrinterStatus, printers)
|
||||||
self.print_jobs = self.parseModels(ClusterPrintJobStatus, print_jobs)
|
self.print_jobs = self.parseModels(ClusterPrintJobStatus, print_jobs)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
|
@ -20,13 +20,23 @@ from ..BaseModel import BaseModel
|
||||||
class ClusterPrinterStatus(BaseModel):
|
class ClusterPrinterStatus(BaseModel):
|
||||||
"""Class representing a cluster printer"""
|
"""Class representing a cluster printer"""
|
||||||
|
|
||||||
def __init__(self, enabled: bool, firmware_version: str, friendly_name: str, ip_address: str, machine_variant: str,
|
def __init__(self,
|
||||||
status: str, unique_name: str, uuid: str,
|
enabled: Optional[bool] = True,
|
||||||
configuration: List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]],
|
friendly_name: Optional[str] = "",
|
||||||
reserved_by: Optional[str] = None, maintenance_required: Optional[bool] = None,
|
machine_variant: Optional[str] = "",
|
||||||
firmware_update_status: Optional[str] = None, latest_available_firmware: Optional[str] = None,
|
status: Optional[str] = "unknown",
|
||||||
build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None,
|
unique_name: Optional[str] = "",
|
||||||
material_station: Union[Dict[str, Any], ClusterPrinterMaterialStation] = None, **kwargs) -> None:
|
uuid: Optional[str] = "",
|
||||||
|
configuration: Optional[List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]]] = None,
|
||||||
|
firmware_version: Optional[str] = None,
|
||||||
|
ip_address: Optional[str] = None,
|
||||||
|
reserved_by: Optional[str] = "",
|
||||||
|
maintenance_required: Optional[bool] = False,
|
||||||
|
firmware_update_status: Optional[str] = "",
|
||||||
|
latest_available_firmware: Optional[str] = "",
|
||||||
|
build_plate: Optional[Union[Dict[str, Any], ClusterBuildPlate]] = None,
|
||||||
|
material_station: Optional[Union[Dict[str, Any], ClusterPrinterMaterialStation]] = None,
|
||||||
|
**kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a new cluster printer status
|
Creates a new cluster printer status
|
||||||
:param enabled: A printer can be disabled if it should not receive new jobs. By default, every printer is enabled.
|
:param enabled: A printer can be disabled if it should not receive new jobs. By default, every printer is enabled.
|
||||||
|
@ -47,7 +57,7 @@ class ClusterPrinterStatus(BaseModel):
|
||||||
:param material_station: The material station that is on the printer.
|
:param material_station: The material station that is on the printer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration)
|
self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration) if configuration else []
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.firmware_version = firmware_version
|
self.firmware_version = firmware_version
|
||||||
self.friendly_name = friendly_name
|
self.friendly_name = friendly_name
|
||||||
|
@ -70,7 +80,7 @@ class ClusterPrinterStatus(BaseModel):
|
||||||
|
|
||||||
:param controller: - The controller of the model.
|
:param controller: - The controller of the model.
|
||||||
"""
|
"""
|
||||||
model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version)
|
model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version or "")
|
||||||
self.updateOutputModel(model)
|
self.updateOutputModel(model)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
@ -86,7 +96,8 @@ class ClusterPrinterStatus(BaseModel):
|
||||||
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")
|
||||||
model.setCameraUrl(QUrl("http://{}:8080/?action=stream".format(self.ip_address)))
|
if self.ip_address:
|
||||||
|
model.setCameraUrl(QUrl("http://{}:8080/?action=stream".format(self.ip_address)))
|
||||||
|
|
||||||
if not model.printerConfiguration:
|
if not model.printerConfiguration:
|
||||||
# Prevent accessing printer configuration when not available.
|
# Prevent accessing printer configuration when not available.
|
||||||
|
|
|
@ -46,10 +46,10 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice):
|
||||||
QUEUED_PRINT_JOBS_STATES = {"queued", "error"}
|
QUEUED_PRINT_JOBS_STATES = {"queued", "error"}
|
||||||
|
|
||||||
def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType,
|
def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType,
|
||||||
parent=None) -> None:
|
parent=None, active: bool = True) -> None:
|
||||||
|
|
||||||
super().__init__(device_id=device_id, address=address, properties=properties, connection_type=connection_type,
|
super().__init__(device_id=device_id, address=address, properties=properties, connection_type=connection_type,
|
||||||
parent=parent)
|
parent=parent, active=active)
|
||||||
# Trigger the printersChanged signal when the private signal is triggered.
|
# Trigger the printersChanged signal when the private signal is triggered.
|
||||||
self.printersChanged.connect(self._clusterPrintersChanged)
|
self.printersChanged.connect(self._clusterPrintersChanged)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ Cura.ExpandablePopup
|
||||||
property bool isConnectedCloudPrinter: machineManager.activeMachineHasCloudConnection
|
property bool isConnectedCloudPrinter: machineManager.activeMachineHasCloudConnection
|
||||||
property bool isCloudRegistered: machineManager.activeMachineHasCloudRegistration
|
property bool isCloudRegistered: machineManager.activeMachineHasCloudRegistration
|
||||||
property bool isGroup: machineManager.activeMachineIsGroup
|
property bool isGroup: machineManager.activeMachineIsGroup
|
||||||
|
property bool isActive: machineManager.activeMachineIsActive
|
||||||
property string machineName: {
|
property string machineName: {
|
||||||
if (isNetworkPrinter && machineManager.activeMachineNetworkGroupName != "")
|
if (isNetworkPrinter && machineManager.activeMachineNetworkGroupName != "")
|
||||||
{
|
{
|
||||||
|
@ -40,7 +41,14 @@ Cura.ExpandablePopup
|
||||||
}
|
}
|
||||||
else if (isConnectedCloudPrinter && Cura.API.connectionStatus.isInternetReachable)
|
else if (isConnectedCloudPrinter && Cura.API.connectionStatus.isInternetReachable)
|
||||||
{
|
{
|
||||||
return "printer_cloud_connected"
|
if (isActive)
|
||||||
|
{
|
||||||
|
return "printer_cloud_connected"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "printer_cloud_inactive"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (isCloudRegistered)
|
else if (isCloudRegistered)
|
||||||
{
|
{
|
||||||
|
@ -53,7 +61,7 @@ Cura.ExpandablePopup
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConnectionStatusMessage() {
|
function getConnectionStatusMessage() {
|
||||||
if (connectionStatus == "printer_cloud_not_available")
|
if (connectionStatus === "printer_cloud_not_available")
|
||||||
{
|
{
|
||||||
if(Cura.API.connectionStatus.isInternetReachable)
|
if(Cura.API.connectionStatus.isInternetReachable)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +86,10 @@ Cura.ExpandablePopup
|
||||||
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection.")
|
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(connectionStatus === "printer_cloud_inactive")
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@status", "This printer is deactivated and can not accept commands or jobs.")
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ""
|
return ""
|
||||||
|
@ -130,14 +142,18 @@ Cura.ExpandablePopup
|
||||||
|
|
||||||
source:
|
source:
|
||||||
{
|
{
|
||||||
if (connectionStatus == "printer_connected")
|
if (connectionStatus === "printer_connected")
|
||||||
{
|
{
|
||||||
return UM.Theme.getIcon("CheckBlueBG", "low")
|
return UM.Theme.getIcon("CheckBlueBG", "low")
|
||||||
}
|
}
|
||||||
else if (connectionStatus == "printer_cloud_connected" || connectionStatus == "printer_cloud_not_available")
|
else if (connectionStatus === "printer_cloud_connected" || connectionStatus === "printer_cloud_not_available")
|
||||||
{
|
{
|
||||||
return UM.Theme.getIcon("CloudBadge", "low")
|
return UM.Theme.getIcon("CloudBadge", "low")
|
||||||
}
|
}
|
||||||
|
else if (connectionStatus === "printer_cloud_inactive")
|
||||||
|
{
|
||||||
|
return UM.Theme.getIcon("WarningBadge", "low")
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ""
|
return ""
|
||||||
|
@ -147,7 +163,21 @@ Cura.ExpandablePopup
|
||||||
width: UM.Theme.getSize("printer_status_icon").width
|
width: UM.Theme.getSize("printer_status_icon").width
|
||||||
height: UM.Theme.getSize("printer_status_icon").height
|
height: UM.Theme.getSize("printer_status_icon").height
|
||||||
|
|
||||||
color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary")
|
color:
|
||||||
|
{
|
||||||
|
if (connectionStatus === "printer_cloud_not_available")
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("cloud_unavailable")
|
||||||
|
}
|
||||||
|
else if(connectionStatus === "printer_cloud_inactive")
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("cloud_inactive")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
visible: (isNetworkPrinter || isCloudRegistered) && source != ""
|
visible: (isNetworkPrinter || isCloudRegistered) && source != ""
|
||||||
|
|
||||||
|
|
|
@ -498,6 +498,7 @@
|
||||||
"monitor_carousel_dot_current": [119, 119, 119, 255],
|
"monitor_carousel_dot_current": [119, 119, 119, 255],
|
||||||
|
|
||||||
"cloud_unavailable": [153, 153, 153, 255],
|
"cloud_unavailable": [153, 153, 153, 255],
|
||||||
|
"cloud_inactive": [253, 209, 58, 255],
|
||||||
"connection_badge_background": [255, 255, 255, 255],
|
"connection_badge_background": [255, 255, 255, 255],
|
||||||
"warning_badge_background": [0, 0, 0, 255],
|
"warning_badge_background": [0, 0, 0, 255],
|
||||||
"error_badge_background": [255, 255, 255, 255],
|
"error_badge_background": [255, 255, 255, 255],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue