mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 23:17:32 -06:00
Merge pull request #7873 from Ultimaker/CURA-7438_Show_cloud_connection_not_available_printer_removed_from_account
CURA-7438 Handle the case when a cloud printer is removed from the account
This commit is contained in:
commit
feeeb972f7
10 changed files with 137 additions and 30 deletions
|
@ -10,7 +10,7 @@ from UM.Message import Message
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from cura.OAuth2.AuthorizationService import AuthorizationService
|
from cura.OAuth2.AuthorizationService import AuthorizationService
|
||||||
from cura.OAuth2.Models import OAuth2Settings
|
from cura.OAuth2.Models import OAuth2Settings
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
@ -69,7 +69,7 @@ class Account(QObject):
|
||||||
self._last_sync_str = "-"
|
self._last_sync_str = "-"
|
||||||
|
|
||||||
self._callback_port = 32118
|
self._callback_port = 32118
|
||||||
self._oauth_root = UltimakerCloudAuthentication.CuraCloudAccountAPIRoot
|
self._oauth_root = UltimakerCloudConstants.CuraCloudAccountAPIRoot
|
||||||
|
|
||||||
self._oauth_settings = OAuth2Settings(
|
self._oauth_settings = OAuth2Settings(
|
||||||
OAUTH_SERVER_URL= self._oauth_root,
|
OAUTH_SERVER_URL= self._oauth_root,
|
||||||
|
|
|
@ -4,14 +4,14 @@ from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty
|
||||||
from PyQt5.QtNetwork import QNetworkReply
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
|
|
||||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
|
|
||||||
|
|
||||||
class ConnectionStatus(QObject):
|
class ConnectionStatus(QObject):
|
||||||
"""Status info for some web services"""
|
"""Status info for some web services"""
|
||||||
|
|
||||||
UPDATE_INTERVAL = 10.0 # seconds
|
UPDATE_INTERVAL = 10.0 # seconds
|
||||||
ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/"
|
ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudConstants.CuraCloudAPIRoot + "/connect/v1/"
|
||||||
|
|
||||||
__instance = None # type: Optional[ConnectionStatus]
|
__instance = None # type: Optional[ConnectionStatus]
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ from cura.UI.RecommendedMode import RecommendedMode
|
||||||
from cura.UI.TextManager import TextManager
|
from cura.UI.TextManager import TextManager
|
||||||
from cura.UI.WelcomePagesModel import WelcomePagesModel
|
from cura.UI.WelcomePagesModel import WelcomePagesModel
|
||||||
from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel
|
from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
from cura.Utils.NetworkingUtil import NetworkingUtil
|
from cura.Utils.NetworkingUtil import NetworkingUtil
|
||||||
from . import BuildVolume
|
from . import BuildVolume
|
||||||
from . import CameraAnimation
|
from . import CameraAnimation
|
||||||
|
@ -255,11 +255,11 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
@pyqtProperty(str, constant=True)
|
@pyqtProperty(str, constant=True)
|
||||||
def ultimakerCloudApiRootUrl(self) -> str:
|
def ultimakerCloudApiRootUrl(self) -> str:
|
||||||
return UltimakerCloudAuthentication.CuraCloudAPIRoot
|
return UltimakerCloudConstants.CuraCloudAPIRoot
|
||||||
|
|
||||||
@pyqtProperty(str, constant = True)
|
@pyqtProperty(str, constant = True)
|
||||||
def ultimakerCloudAccountRootUrl(self) -> str:
|
def ultimakerCloudAccountRootUrl(self) -> str:
|
||||||
return UltimakerCloudAuthentication.CuraCloudAccountAPIRoot
|
return UltimakerCloudConstants.CuraCloudAccountAPIRoot
|
||||||
|
|
||||||
def addCommandLineOptions(self):
|
def addCommandLineOptions(self):
|
||||||
"""Adds command line options to the command line parser.
|
"""Adds command line options to the command line parser.
|
||||||
|
|
|
@ -22,6 +22,7 @@ from UM.Settings.SettingFunction import SettingFunction
|
||||||
from UM.Signal import postponeSignals, CompressTechnique
|
from UM.Signal import postponeSignals, CompressTechnique
|
||||||
|
|
||||||
import cura.CuraApplication # Imported like this to prevent circular references.
|
import cura.CuraApplication # Imported like this to prevent circular references.
|
||||||
|
from UM.Util import parseBool
|
||||||
|
|
||||||
from cura.Machines.ContainerNode import ContainerNode
|
from cura.Machines.ContainerNode import ContainerNode
|
||||||
from cura.Machines.ContainerTree import ContainerTree
|
from cura.Machines.ContainerTree import ContainerTree
|
||||||
|
@ -37,6 +38,7 @@ from cura.Settings.ExtruderStack import ExtruderStack
|
||||||
from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container,
|
from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container,
|
||||||
empty_material_container, empty_quality_container,
|
empty_material_container, empty_quality_container,
|
||||||
empty_quality_changes_container, empty_intent_container)
|
empty_quality_changes_container, empty_intent_container)
|
||||||
|
from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT
|
||||||
|
|
||||||
from .CuraStackBuilder import CuraStackBuilder
|
from .CuraStackBuilder import CuraStackBuilder
|
||||||
|
|
||||||
|
@ -494,6 +496,10 @@ class MachineManager(QObject):
|
||||||
group_size = int(self.activeMachine.getMetaDataEntry("group_size", "-1"))
|
group_size = int(self.activeMachine.getMetaDataEntry("group_size", "-1"))
|
||||||
return group_size > 1
|
return group_size > 1
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||||
|
def activeMachineIsLinkedToCurrentAccount(self) -> bool:
|
||||||
|
return parseBool(self.activeMachine.getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "True"))
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||||
def activeMachineHasNetworkConnection(self) -> bool:
|
def activeMachineHasNetworkConnection(self) -> bool:
|
||||||
# A network connection is only available if any output device is actually a network connected device.
|
# A network connection is only available if any output device is actually a network connected device.
|
||||||
|
|
|
@ -8,6 +8,10 @@ DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str
|
||||||
DEFAULT_CLOUD_API_VERSION = "1" # type: str
|
DEFAULT_CLOUD_API_VERSION = "1" # type: str
|
||||||
DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str
|
DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str
|
||||||
|
|
||||||
|
# Container Metadata keys
|
||||||
|
META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account"
|
||||||
|
"""(bool) Whether a cloud printer is linked to an Ultimaker account"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from cura.CuraVersion import CuraCloudAPIRoot # type: ignore
|
from cura.CuraVersion import CuraCloudAPIRoot # type: ignore
|
||||||
if CuraCloudAPIRoot == "":
|
if CuraCloudAPIRoot == "":
|
|
@ -1,13 +1,13 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 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.
|
||||||
|
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
|
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
# Keeps the plugin settings.
|
# Keeps the plugin settings.
|
||||||
DRIVE_API_VERSION = 1
|
DRIVE_API_VERSION = 1
|
||||||
DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION))
|
DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION))
|
||||||
|
|
||||||
AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled"
|
AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled"
|
||||||
AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date"
|
AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date"
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from cura import ApplicationMetadata
|
from cura import ApplicationMetadata
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
|
|
||||||
|
|
||||||
class CloudApiModel:
|
class CloudApiModel:
|
||||||
sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
|
sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
|
||||||
cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: str
|
cloud_api_version = UltimakerCloudConstants.CuraCloudAPIVersion # type: str
|
||||||
cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str
|
cloud_api_root = UltimakerCloudConstants.CuraCloudAPIRoot # type: str
|
||||||
api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
|
api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
|
||||||
cloud_api_root = cloud_api_root,
|
cloud_api_root = cloud_api_root,
|
||||||
cloud_api_version = cloud_api_version,
|
cloud_api_version = cloud_api_version,
|
||||||
|
|
|
@ -13,7 +13,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||||
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
|
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
|
||||||
from cura.API import Account
|
from cura.API import Account
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
from cura.UltimakerCloud import UltimakerCloudConstants
|
||||||
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
|
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
|
||||||
from .ToolPathUploader import ToolPathUploader
|
from .ToolPathUploader import ToolPathUploader
|
||||||
from ..Models.BaseModel import BaseModel
|
from ..Models.BaseModel import BaseModel
|
||||||
|
@ -35,7 +35,7 @@ class CloudApiClient:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The cloud URL to use for this remote cluster.
|
# The cloud URL to use for this remote cluster.
|
||||||
ROOT_PATH = UltimakerCloudAuthentication.CuraCloudAPIRoot
|
ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot
|
||||||
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
|
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
|
||||||
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
|
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
# 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
|
import os
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional, Set
|
||||||
|
|
||||||
from PyQt5.QtCore import QTimer
|
|
||||||
from PyQt5.QtNetwork import QNetworkReply
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
|
|
||||||
from UM import i18nCatalog
|
from UM import i18nCatalog
|
||||||
|
@ -11,11 +10,13 @@ from UM.Logger import Logger # To log errors talking to the API.
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
from UM.Signal import Signal
|
from UM.Signal import Signal
|
||||||
|
from UM.Util import parseBool
|
||||||
from cura.API import Account
|
from cura.API import Account
|
||||||
from cura.API.Account import SyncState
|
from cura.API.Account import SyncState
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
|
from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT
|
||||||
from .CloudApiClient import CloudApiClient
|
from .CloudApiClient import CloudApiClient
|
||||||
from .CloudOutputDevice import CloudOutputDevice
|
from .CloudOutputDevice import CloudOutputDevice
|
||||||
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||||
|
@ -30,6 +31,7 @@ class CloudOutputDeviceManager:
|
||||||
|
|
||||||
META_CLUSTER_ID = "um_cloud_cluster_id"
|
META_CLUSTER_ID = "um_cloud_cluster_id"
|
||||||
META_NETWORK_KEY = "um_network_key"
|
META_NETWORK_KEY = "um_network_key"
|
||||||
|
|
||||||
SYNC_SERVICE_NAME = "CloudOutputDeviceManager"
|
SYNC_SERVICE_NAME = "CloudOutputDeviceManager"
|
||||||
|
|
||||||
# The translation catalog for this device.
|
# The translation catalog for this device.
|
||||||
|
@ -41,6 +43,10 @@ class CloudOutputDeviceManager:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
# Persistent dict containing the remote clusters for the authenticated user.
|
# Persistent dict containing the remote clusters for the authenticated user.
|
||||||
self._remote_clusters = {} # type: Dict[str, CloudOutputDevice]
|
self._remote_clusters = {} # type: Dict[str, CloudOutputDevice]
|
||||||
|
|
||||||
|
# Dictionary containing all the cloud printers loaded in Cura
|
||||||
|
self._um_cloud_printers = {} # type: Dict[str, GlobalStack]
|
||||||
|
|
||||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||||
self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
|
self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
|
||||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||||
|
@ -98,23 +104,36 @@ class CloudOutputDeviceManager:
|
||||||
def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None:
|
def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None:
|
||||||
"""Callback for when the request for getting the clusters is finished."""
|
"""Callback for when the request for getting the clusters is finished."""
|
||||||
|
|
||||||
|
self._um_cloud_printers = {m.getMetaDataEntry(self.META_CLUSTER_ID): m for m in
|
||||||
|
CuraApplication.getInstance().getContainerRegistry().findContainerStacks(
|
||||||
|
type = "machine") if m.getMetaDataEntry(self.META_CLUSTER_ID, None)}
|
||||||
new_clusters = []
|
new_clusters = []
|
||||||
all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse]
|
all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse]
|
||||||
online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse]
|
online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse]
|
||||||
|
|
||||||
|
# Add the new printers in Cura. If a printer was previously added and is rediscovered, set its metadata to
|
||||||
|
# reflect that and mark the printer not removed from the account
|
||||||
for device_id, cluster_data in all_clusters.items():
|
for device_id, cluster_data in all_clusters.items():
|
||||||
if device_id not in self._remote_clusters:
|
if device_id not in self._remote_clusters:
|
||||||
new_clusters.append(cluster_data)
|
new_clusters.append(cluster_data)
|
||||||
|
if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||||
|
self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||||
self._onDevicesDiscovered(new_clusters)
|
self._onDevicesDiscovered(new_clusters)
|
||||||
|
|
||||||
removed_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys())
|
# Remove the CloudOutput device for offline printers
|
||||||
for device_id in removed_device_keys:
|
offline_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys())
|
||||||
|
for device_id in offline_device_keys:
|
||||||
self._onDiscoveredDeviceRemoved(device_id)
|
self._onDiscoveredDeviceRemoved(device_id)
|
||||||
|
|
||||||
if new_clusters or removed_device_keys:
|
# Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were
|
||||||
self.discoveredDevicesChanged.emit()
|
# removed from the account)
|
||||||
|
removed_device_keys = set(self._um_cloud_printers.keys()) - set(all_clusters.keys())
|
||||||
if removed_device_keys:
|
if removed_device_keys:
|
||||||
|
self._devicesRemovedFromAccount(removed_device_keys)
|
||||||
|
|
||||||
|
if new_clusters or offline_device_keys or removed_device_keys:
|
||||||
|
self.discoveredDevicesChanged.emit()
|
||||||
|
if offline_device_keys:
|
||||||
# If the removed device was active we should connect to the new active device
|
# If the removed device was active we should connect to the new active device
|
||||||
self._connectToActiveMachine()
|
self._connectToActiveMachine()
|
||||||
|
|
||||||
|
@ -144,10 +163,13 @@ class CloudOutputDeviceManager:
|
||||||
if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
|
if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
|
||||||
and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key.
|
and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key.
|
||||||
new_devices.append(device)
|
new_devices.append(device)
|
||||||
|
|
||||||
elif device.getId() not in self._remote_clusters:
|
elif device.getId() not in self._remote_clusters:
|
||||||
self._remote_clusters[device.getId()] = device
|
self._remote_clusters[device.getId()] = device
|
||||||
remote_clusters_added = True
|
remote_clusters_added = True
|
||||||
|
# If a printer that was removed from the account is re-added, change its metadata to mark it not removed
|
||||||
|
# from the account
|
||||||
|
elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||||
|
self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||||
|
|
||||||
# Inform the Cloud printers model about new devices.
|
# Inform the Cloud printers model about new devices.
|
||||||
new_devices_list_of_dicts = [{
|
new_devices_list_of_dicts = [{
|
||||||
|
@ -208,19 +230,86 @@ class CloudOutputDeviceManager:
|
||||||
max_disp_devices = 3
|
max_disp_devices = 3
|
||||||
if len(new_devices) > max_disp_devices:
|
if len(new_devices) > max_disp_devices:
|
||||||
num_hidden = len(new_devices) - max_disp_devices + 1
|
num_hidden = len(new_devices) - max_disp_devices + 1
|
||||||
device_name_list = ["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]]
|
device_name_list = ["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]]
|
||||||
device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "- and {} others", num_hidden))
|
device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "<li>... and {} others</li>", num_hidden))
|
||||||
device_names = "\n".join(device_name_list)
|
device_names = "\n".join(device_name_list)
|
||||||
else:
|
else:
|
||||||
device_names = "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices])
|
device_names = "\n".join(["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices])
|
||||||
|
|
||||||
message_text = self.I18N_CATALOG.i18nc(
|
message_text = self.I18N_CATALOG.i18nc(
|
||||||
"info:status",
|
"info:status",
|
||||||
"Cloud printers added from your account:\n{}",
|
"Cloud printers added from your account:\n<ul>{}</ul>",
|
||||||
device_names
|
device_names
|
||||||
)
|
)
|
||||||
message.setText(message_text)
|
message.setText(message_text)
|
||||||
|
|
||||||
|
def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None:
|
||||||
|
"""
|
||||||
|
Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from
|
||||||
|
account". In addition, it generates a message to inform the user about the printers that are no longer linked to
|
||||||
|
his/her account. The message is not generated if all the printers have been previously reported as not linked
|
||||||
|
to the account.
|
||||||
|
|
||||||
|
:param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Do not report device ids which have been previously marked as non-linked to the account
|
||||||
|
ignored_device_ids = set()
|
||||||
|
for device_id in removed_device_ids:
|
||||||
|
if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||||
|
ignored_device_ids.add(device_id)
|
||||||
|
# Keep the reported_device_ids list in a class variable, so that the message button actions can access it and
|
||||||
|
# take the necessary steps to fulfill their purpose.
|
||||||
|
self.reported_device_ids = removed_device_ids - ignored_device_ids
|
||||||
|
if not self.reported_device_ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Generate message
|
||||||
|
removed_printers_message = Message(
|
||||||
|
title = self.I18N_CATALOG.i18ncp(
|
||||||
|
"info:status",
|
||||||
|
"Cloud connection is not available for a printer",
|
||||||
|
"Cloud connection is not available for some printers",
|
||||||
|
len(self.reported_device_ids)
|
||||||
|
),
|
||||||
|
lifetime = 0
|
||||||
|
)
|
||||||
|
device_names = "\n".join(["<li>{} ({})</li>".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids])
|
||||||
|
message_text = self.I18N_CATALOG.i18ncp(
|
||||||
|
"info:status",
|
||||||
|
"The following cloud printer is not linked to your account:\n",
|
||||||
|
"The following cloud printers are not linked to your account:\n",
|
||||||
|
len(self.reported_device_ids)
|
||||||
|
)
|
||||||
|
message_text += self.I18N_CATALOG.i18nc(
|
||||||
|
"info:status",
|
||||||
|
"<ul>{}</ul>\nTo establish a connection, please visit the "
|
||||||
|
"<a href='https://mycloud.ultimaker.com/'>Ultimaker Digital Factory</a>.",
|
||||||
|
device_names
|
||||||
|
)
|
||||||
|
removed_printers_message.setText(message_text)
|
||||||
|
|
||||||
|
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||||
|
|
||||||
|
# Remove the output device from the printers
|
||||||
|
for device_id in removed_device_ids:
|
||||||
|
device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack]
|
||||||
|
if not device:
|
||||||
|
continue
|
||||||
|
if device_id in output_device_manager.getOutputDeviceIds():
|
||||||
|
output_device_manager.removeOutputDevice(device_id)
|
||||||
|
if device_id in self._remote_clusters:
|
||||||
|
del self._remote_clusters[device_id]
|
||||||
|
|
||||||
|
# Update the printer's metadata to mark it as not linked to the account
|
||||||
|
device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)
|
||||||
|
|
||||||
|
removed_printers_message.show()
|
||||||
|
|
||||||
def _onDiscoveredDeviceRemoved(self, device_id: str) -> None:
|
def _onDiscoveredDeviceRemoved(self, device_id: str) -> None:
|
||||||
device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice]
|
device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice]
|
||||||
if not device:
|
if not device:
|
||||||
|
|
|
@ -35,16 +35,23 @@ Cura.ExpandablePopup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property string connectionStatusMessage: {
|
function getConnectionStatusMessage() {
|
||||||
if (connectionStatus == "printer_cloud_not_available")
|
if (connectionStatus == "printer_cloud_not_available")
|
||||||
{
|
{
|
||||||
if(Cura.API.connectionStatus.isInternetReachable)
|
if(Cura.API.connectionStatus.isInternetReachable)
|
||||||
{
|
{
|
||||||
if (Cura.API.account.isLoggedIn)
|
if (Cura.API.account.isLoggedIn)
|
||||||
|
{
|
||||||
|
if (Cura.MachineManager.activeMachineIsLinkedToCurrentAccount)
|
||||||
{
|
{
|
||||||
return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.")
|
return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@status", "This printer is not linked to your account. Please visit the Ultimaker Digital Factory to establish a connection.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please sign in to connect to the cloud printer.")
|
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please sign in to connect to the cloud printer.")
|
||||||
}
|
}
|
||||||
|
@ -139,12 +146,13 @@ Cura.ExpandablePopup
|
||||||
{
|
{
|
||||||
id: connectionStatusTooltipHoverArea
|
id: connectionStatusTooltipHoverArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: connectionStatusMessage !== ""
|
hoverEnabled: getConnectionStatusMessage() !== ""
|
||||||
acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
|
acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
|
||||||
|
|
||||||
onEntered:
|
onEntered:
|
||||||
{
|
{
|
||||||
machineSelector.mouseArea.entered() // we want both this and the outer area to be entered
|
machineSelector.mouseArea.entered() // we want both this and the outer area to be entered
|
||||||
|
tooltip.tooltipText = getConnectionStatusMessage()
|
||||||
tooltip.show()
|
tooltip.show()
|
||||||
}
|
}
|
||||||
onExited: { tooltip.hide() }
|
onExited: { tooltip.hide() }
|
||||||
|
@ -155,7 +163,7 @@ Cura.ExpandablePopup
|
||||||
id: tooltip
|
id: tooltip
|
||||||
|
|
||||||
width: 250 * screenScaleFactor
|
width: 250 * screenScaleFactor
|
||||||
tooltipText: connectionStatusMessage
|
tooltipText: getConnectionStatusMessage()
|
||||||
arrowSize: UM.Theme.getSize("button_tooltip_arrow").width
|
arrowSize: UM.Theme.getSize("button_tooltip_arrow").width
|
||||||
x: connectionStatusImage.x - UM.Theme.getSize("narrow_margin").width
|
x: connectionStatusImage.x - UM.Theme.getSize("narrow_margin").width
|
||||||
y: connectionStatusImage.y + connectionStatusImage.height + UM.Theme.getSize("narrow_margin").height
|
y: connectionStatusImage.y + connectionStatusImage.height + UM.Theme.getSize("narrow_margin").height
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue