Merge pull request #7831 from Ultimaker/CURA-7437_no_internet_cloud_status

CURA-7437_no_internet_cloud_status
This commit is contained in:
Konstantinos Karmas 2020-05-28 10:02:15 +02:00 committed by GitHub
commit 56e8827d00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 198 additions and 32 deletions

View 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()

View File

@ -5,6 +5,7 @@ from typing import Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtProperty
from cura.API.Backups import Backups
from cura.API.ConnectionStatus import ConnectionStatus
from cura.API.Interface import Interface
from cura.API.Account import Account
@ -45,6 +46,8 @@ class CuraAPI(QObject):
# Backups API
self._backups = Backups(self._application)
self._connectionStatus = ConnectionStatus()
# Interface API
self._interface = Interface(self._application)
@ -55,6 +58,10 @@ class CuraAPI(QObject):
def account(self) -> "Account":
return self._account
@pyqtProperty(QObject, constant = True)
def connectionStatus(self) -> "ConnectionStatus":
return self._connectionStatus
@property
def backups(self) -> "Backups":
return self._backups

View File

@ -490,6 +490,10 @@ class MachineManager(QObject):
def activeMachineHasCloudConnection(self) -> bool:
# 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)
@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)
def activeMachineIsUsingCloudConnection(self) -> bool:

View File

@ -265,7 +265,7 @@ class LocalClusterOutputDeviceManager:
## Nudge the user to start using Ultimaker Cloud.
@staticmethod
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.
return
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:

View File

@ -35,7 +35,8 @@ Item
property color headerActiveColor: UM.Theme.getColor("secondary")
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
property alias disabledText: disabledLabel.text
@ -139,6 +140,16 @@ Item
anchors.fill: parent
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
{
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

View File

@ -5,16 +5,49 @@ import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
import Cura 1.1 as Cura
Cura.ExpandablePopup
{
id: machineSelector
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
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
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
@ -44,7 +77,7 @@ Cura.ExpandablePopup
{
return UM.Theme.getIcon("printer_group")
}
else if (isNetworkPrinter || isCloudPrinter)
else if (isNetworkPrinter || isCloudRegistered)
{
return UM.Theme.getIcon("printer_single")
}
@ -59,6 +92,7 @@ Cura.ExpandablePopup
UM.RecolorImage
{
id: connectionStatusImage
anchors
{
bottom: parent.bottom
@ -66,27 +100,14 @@ Cura.ExpandablePopup
leftMargin: UM.Theme.getSize("thick_margin").width
}
source:
{
if (isNetworkPrinter)
{
return UM.Theme.getIcon("printer_connected")
}
else if (isCloudPrinter)
{
return UM.Theme.getIcon("printer_cloud_connected")
}
else
{
return ""
}
}
source: UM.Theme.getIcon(connectionStatus)
width: UM.Theme.getSize("printer_status_icon").width
height: UM.Theme.getSize("printer_status_icon").height
color: UM.Theme.getColor("primary")
visible: isNetworkPrinter || isCloudPrinter
color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary")
visible: isNetworkPrinter || isCloudRegistered
// Make a themable circle in the background so we can change it in other themes
Rectangle
@ -100,6 +121,38 @@ Cura.ExpandablePopup
color: UM.Theme.getColor("main_background")
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
)
}
}

View File

@ -19,12 +19,20 @@ ToolTip
property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight
property alias tooltipText: tooltip.text
property alias arrowSize: backgroundRect.arrowSize
property var targetPoint: Qt.point(parent.x, y + Math.round(height/2))
id: tooltip
text: ""
delay: 500
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
height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0
@ -60,4 +68,12 @@ ToolTip
color: UM.Theme.getColor("tooltip_text")
renderType: Text.NativeRendering
}
function show() {
opacity = 1
}
function hide() {
opacity = 0
}
}

View File

@ -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

View File

@ -438,7 +438,9 @@
"monitor_shadow": [200, 200, 200, 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": {