mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-07-04 10:45:13 +08:00
Merge pull request #7831 from Ultimaker/CURA-7437_no_internet_cloud_status
CURA-7437_no_internet_cloud_status
This commit is contained in:
commit
56e8827d00
64
cura/API/ConnectionStatus.py
Normal file
64
cura/API/ConnectionStatus.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty
|
||||||
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
|
|
||||||
|
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||||
|
from cura.UltimakerCloud import UltimakerCloudAuthentication
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionStatus(QObject):
|
||||||
|
"""Status info for some web services"""
|
||||||
|
|
||||||
|
UPDATE_INTERVAL = 10.0 # seconds
|
||||||
|
ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/"
|
||||||
|
|
||||||
|
__instance = None # type: Optional[ConnectionStatus]
|
||||||
|
|
||||||
|
internetReachableChanged = pyqtSignal()
|
||||||
|
umCloudReachableChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getInstance(cls, *args, **kwargs) -> "ConnectionStatus":
|
||||||
|
if cls.__instance is None:
|
||||||
|
cls.__instance = cls(*args, **kwargs)
|
||||||
|
return cls.__instance
|
||||||
|
|
||||||
|
def __init__(self, parent: Optional["QObject"] = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._http = HttpRequestManager.getInstance()
|
||||||
|
self._statuses = {
|
||||||
|
self.ULTIMAKER_CLOUD_STATUS_URL: True,
|
||||||
|
"http://example.com": True
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a timer for automatic updates
|
||||||
|
self._update_timer = QTimer()
|
||||||
|
self._update_timer.setInterval(int(self.UPDATE_INTERVAL * 1000))
|
||||||
|
# The timer is restarted automatically
|
||||||
|
self._update_timer.setSingleShot(False)
|
||||||
|
self._update_timer.timeout.connect(self._update)
|
||||||
|
self._update_timer.start()
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=internetReachableChanged)
|
||||||
|
def isInternetReachable(self) -> bool:
|
||||||
|
# Is any of the test urls reachable?
|
||||||
|
return any(self._statuses.values())
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
for url in self._statuses.keys():
|
||||||
|
self._http.get(
|
||||||
|
url = url,
|
||||||
|
callback = self._statusCallback,
|
||||||
|
error_callback = self._statusCallback,
|
||||||
|
timeout = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
def _statusCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError = None):
|
||||||
|
url = reply.request().url().toString()
|
||||||
|
prev_statuses = self._statuses.copy()
|
||||||
|
self._statuses[url] = HttpRequestManager.replyIndicatesSuccess(reply, error)
|
||||||
|
|
||||||
|
if any(self._statuses.values()) != any(prev_statuses.values()):
|
||||||
|
self.internetReachableChanged.emit()
|
@ -5,6 +5,7 @@ from typing import Optional, TYPE_CHECKING
|
|||||||
from PyQt5.QtCore import QObject, pyqtProperty
|
from PyQt5.QtCore import QObject, pyqtProperty
|
||||||
|
|
||||||
from cura.API.Backups import Backups
|
from cura.API.Backups import Backups
|
||||||
|
from cura.API.ConnectionStatus import ConnectionStatus
|
||||||
from cura.API.Interface import Interface
|
from cura.API.Interface import Interface
|
||||||
from cura.API.Account import Account
|
from cura.API.Account import Account
|
||||||
|
|
||||||
@ -45,6 +46,8 @@ class CuraAPI(QObject):
|
|||||||
# Backups API
|
# Backups API
|
||||||
self._backups = Backups(self._application)
|
self._backups = Backups(self._application)
|
||||||
|
|
||||||
|
self._connectionStatus = ConnectionStatus()
|
||||||
|
|
||||||
# Interface API
|
# Interface API
|
||||||
self._interface = Interface(self._application)
|
self._interface = Interface(self._application)
|
||||||
|
|
||||||
@ -55,6 +58,10 @@ class CuraAPI(QObject):
|
|||||||
def account(self) -> "Account":
|
def account(self) -> "Account":
|
||||||
return self._account
|
return self._account
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, constant = True)
|
||||||
|
def connectionStatus(self) -> "ConnectionStatus":
|
||||||
|
return self._connectionStatus
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def backups(self) -> "Backups":
|
def backups(self) -> "Backups":
|
||||||
return self._backups
|
return self._backups
|
||||||
|
@ -491,6 +491,10 @@ class MachineManager(QObject):
|
|||||||
# A cloud connection is only available if any output device actually is a cloud connected device.
|
# A cloud connection is only available if any output device actually is a cloud connected device.
|
||||||
return any(d.connectionType == ConnectionType.CloudConnection for d in self._printer_output_devices)
|
return any(d.connectionType == ConnectionType.CloudConnection for d in self._printer_output_devices)
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||||
|
def activeMachineHasCloudRegistration(self) -> bool:
|
||||||
|
return self.activeMachine is not None and ConnectionType.CloudConnection in self.activeMachine.configuredConnectionTypes
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||||
def activeMachineIsUsingCloudConnection(self) -> bool:
|
def activeMachineIsUsingCloudConnection(self) -> bool:
|
||||||
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
|
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
|
||||||
|
@ -265,7 +265,7 @@ class LocalClusterOutputDeviceManager:
|
|||||||
## Nudge the user to start using Ultimaker Cloud.
|
## Nudge the user to start using Ultimaker Cloud.
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _showCloudFlowMessage(device: LocalClusterOutputDevice) -> None:
|
def _showCloudFlowMessage(device: LocalClusterOutputDevice) -> None:
|
||||||
if CuraApplication.getInstance().getMachineManager().activeMachineIsUsingCloudConnection:
|
if CuraApplication.getInstance().getMachineManager().activeMachineHasCloudRegistration:
|
||||||
# This printer is already cloud connected, so we do not bother the user anymore.
|
# This printer is already cloud connected, so we do not bother the user anymore.
|
||||||
return
|
return
|
||||||
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
|
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
|
||||||
|
@ -35,7 +35,8 @@ Item
|
|||||||
property color headerActiveColor: UM.Theme.getColor("secondary")
|
property color headerActiveColor: UM.Theme.getColor("secondary")
|
||||||
property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
|
property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
|
||||||
|
|
||||||
property alias enabled: mouseArea.enabled
|
property alias mouseArea: headerMouseArea
|
||||||
|
property alias enabled: headerMouseArea.enabled
|
||||||
|
|
||||||
// Text to show when this component is disabled
|
// Text to show when this component is disabled
|
||||||
property alias disabledText: disabledLabel.text
|
property alias disabledText: disabledLabel.text
|
||||||
@ -139,6 +140,16 @@ Item
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: base.enabled
|
visible: base.enabled
|
||||||
|
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
id: headerMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: toggleContent()
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: background.color = headerHoverColor
|
||||||
|
onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
|
||||||
|
}
|
||||||
|
|
||||||
Loader
|
Loader
|
||||||
{
|
{
|
||||||
id: headerItemLoader
|
id: headerItemLoader
|
||||||
@ -180,15 +191,6 @@ Item
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea
|
|
||||||
{
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: toggleContent()
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: background.color = headerHoverColor
|
|
||||||
onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow
|
DropShadow
|
||||||
|
@ -5,16 +5,49 @@ import QtQuick 2.7
|
|||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.1 as Cura
|
||||||
|
|
||||||
Cura.ExpandablePopup
|
Cura.ExpandablePopup
|
||||||
{
|
{
|
||||||
id: machineSelector
|
id: machineSelector
|
||||||
|
|
||||||
property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasNetworkConnection
|
property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasNetworkConnection
|
||||||
property bool isCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection
|
property bool isConnectedCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection
|
||||||
|
property bool isCloudRegistered: Cura.MachineManager.activeMachineHasCloudRegistration
|
||||||
property bool isGroup: Cura.MachineManager.activeMachineIsGroup
|
property bool isGroup: Cura.MachineManager.activeMachineIsGroup
|
||||||
|
|
||||||
|
readonly property string connectionStatus: {
|
||||||
|
if (isNetworkPrinter)
|
||||||
|
{
|
||||||
|
return "printer_connected"
|
||||||
|
}
|
||||||
|
else if (isConnectedCloudPrinter && Cura.API.connectionStatus.isInternetReachable)
|
||||||
|
{
|
||||||
|
return "printer_cloud_connected"
|
||||||
|
}
|
||||||
|
else if (isCloudRegistered)
|
||||||
|
{
|
||||||
|
return "printer_cloud_not_available"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string connectionStatusMessage: {
|
||||||
|
if (connectionStatus == "printer_cloud_not_available")
|
||||||
|
{
|
||||||
|
if(Cura.API.connectionStatus.isInternetReachable){
|
||||||
|
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection and sign in to connect to the cloud printer.")
|
||||||
|
} else {
|
||||||
|
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentPadding: UM.Theme.getSize("default_lining").width
|
contentPadding: UM.Theme.getSize("default_lining").width
|
||||||
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
|
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
|
||||||
|
|
||||||
@ -44,7 +77,7 @@ Cura.ExpandablePopup
|
|||||||
{
|
{
|
||||||
return UM.Theme.getIcon("printer_group")
|
return UM.Theme.getIcon("printer_group")
|
||||||
}
|
}
|
||||||
else if (isNetworkPrinter || isCloudPrinter)
|
else if (isNetworkPrinter || isCloudRegistered)
|
||||||
{
|
{
|
||||||
return UM.Theme.getIcon("printer_single")
|
return UM.Theme.getIcon("printer_single")
|
||||||
}
|
}
|
||||||
@ -59,6 +92,7 @@ Cura.ExpandablePopup
|
|||||||
|
|
||||||
UM.RecolorImage
|
UM.RecolorImage
|
||||||
{
|
{
|
||||||
|
id: connectionStatusImage
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
@ -66,27 +100,14 @@ Cura.ExpandablePopup
|
|||||||
leftMargin: UM.Theme.getSize("thick_margin").width
|
leftMargin: UM.Theme.getSize("thick_margin").width
|
||||||
}
|
}
|
||||||
|
|
||||||
source:
|
source: UM.Theme.getIcon(connectionStatus)
|
||||||
{
|
|
||||||
if (isNetworkPrinter)
|
|
||||||
{
|
|
||||||
return UM.Theme.getIcon("printer_connected")
|
|
||||||
}
|
|
||||||
else if (isCloudPrinter)
|
|
||||||
{
|
|
||||||
return UM.Theme.getIcon("printer_cloud_connected")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: UM.Theme.getColor("primary")
|
color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary")
|
||||||
visible: isNetworkPrinter || isCloudPrinter
|
|
||||||
|
visible: isNetworkPrinter || isCloudRegistered
|
||||||
|
|
||||||
// Make a themable circle in the background so we can change it in other themes
|
// Make a themable circle in the background so we can change it in other themes
|
||||||
Rectangle
|
Rectangle
|
||||||
@ -100,6 +121,38 @@ Cura.ExpandablePopup
|
|||||||
color: UM.Theme.getColor("main_background")
|
color: UM.Theme.getColor("main_background")
|
||||||
z: parent.z - 1
|
z: parent.z - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea // Connection status tooltip hover area
|
||||||
|
{
|
||||||
|
id: connectionStatusTooltipHoverArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: connectionStatusMessage !== ""
|
||||||
|
acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
|
||||||
|
|
||||||
|
onEntered:
|
||||||
|
{
|
||||||
|
machineSelector.mouseArea.entered() // we want both this and the outer area to be entered
|
||||||
|
tooltip.show()
|
||||||
|
}
|
||||||
|
onExited: { tooltip.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Cura.ToolTip
|
||||||
|
{
|
||||||
|
id: tooltip
|
||||||
|
|
||||||
|
width: 250 * screenScaleFactor
|
||||||
|
tooltipText: connectionStatusMessage
|
||||||
|
arrowSize: UM.Theme.getSize("button_tooltip_arrow").width
|
||||||
|
x: connectionStatusImage.x - UM.Theme.getSize("narrow_margin").width
|
||||||
|
y: connectionStatusImage.y + connectionStatusImage.height + UM.Theme.getSize("narrow_margin").height
|
||||||
|
z: popup.z + 1
|
||||||
|
targetPoint: Qt.point(
|
||||||
|
connectionStatusImage.x + Math.round(connectionStatusImage.width / 2),
|
||||||
|
connectionStatusImage.y
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,20 @@ ToolTip
|
|||||||
property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight
|
property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight
|
||||||
|
|
||||||
property alias tooltipText: tooltip.text
|
property alias tooltipText: tooltip.text
|
||||||
|
property alias arrowSize: backgroundRect.arrowSize
|
||||||
property var targetPoint: Qt.point(parent.x, y + Math.round(height/2))
|
property var targetPoint: Qt.point(parent.x, y + Math.round(height/2))
|
||||||
|
|
||||||
id: tooltip
|
id: tooltip
|
||||||
text: ""
|
text: ""
|
||||||
delay: 500
|
delay: 500
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
|
visible: opacity != 0.0
|
||||||
|
opacity: 0.0 // initially hidden
|
||||||
|
|
||||||
|
Behavior on opacity
|
||||||
|
{
|
||||||
|
NumberAnimation { duration: 100; }
|
||||||
|
}
|
||||||
|
|
||||||
// If the text is not set, just set the height to 0 to prevent it from showing
|
// If the text is not set, just set the height to 0 to prevent it from showing
|
||||||
height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0
|
height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0
|
||||||
@ -60,4 +68,12 @@ ToolTip
|
|||||||
color: UM.Theme.getColor("tooltip_text")
|
color: UM.Theme.getColor("tooltip_text")
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
opacity = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
opacity = 0
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
|
||||||
|
<title>Artboard Copy 4</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<path d="M6,0 C9.3137085,0 12,2.6862915 12,6 C12,9.3137085 9.3137085,12 6,12 C2.6862915,12 0,9.3137085 0,6 C0,2.6862915 2.6862915,0 6,0 Z M5.2,3 C4.08,3 3.2,3.91666667 3.2,5.08333333 L3.2,5.125 L3.2,5.125 C2.52,5.20833333 2,5.83333333 2,6.54166667 C2,7.33333333 2.64,8 3.4,8 L8.6,8 L8.6,8 C9.36,8 10,7.33333333 10,6.54166667 C10,5.79166667 9.48,5.20833333 8.8,5.08333333 C8.72,4.375 8.12,3.83333333 7.4,3.83333333 C7.2,3.83333333 7.04,3.875 6.88,3.95833333 C6.52,3.375 5.88,3 5.2,3 Z" id="path-1"></path>
|
||||||
|
</defs>
|
||||||
|
<g id="Artboard-Copy-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<rect id="Rectangle" fill="#999999" transform="translate(7.187184, 7.187184) scale(-1, 1) rotate(45.000000) translate(-7.187184, -7.187184) " x="6.43718434" y="-0.812815665" width="1.5" height="16" rx="0.75"></rect>
|
||||||
|
<g id="Path" transform="translate(1.000000, 1.000000)">
|
||||||
|
<mask id="mask-2" fill="white">
|
||||||
|
<use xlink:href="#path-1"></use>
|
||||||
|
</mask>
|
||||||
|
<use id="Combined-Shape" fill="#999999" xlink:href="#path-1"></use>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -438,7 +438,9 @@
|
|||||||
"monitor_shadow": [200, 200, 200, 255],
|
"monitor_shadow": [200, 200, 200, 255],
|
||||||
|
|
||||||
"monitor_carousel_dot": [216, 216, 216, 255],
|
"monitor_carousel_dot": [216, 216, 216, 255],
|
||||||
"monitor_carousel_dot_current": [119, 119, 119, 255]
|
"monitor_carousel_dot_current": [119, 119, 119, 255],
|
||||||
|
|
||||||
|
"cloud_unavailable": [153, 153, 153, 255]
|
||||||
},
|
},
|
||||||
|
|
||||||
"sizes": {
|
"sizes": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user