diff --git a/cura/UI/MachineModels/DiscoveredPrintersModel.py b/cura/UI/MachineModels/DiscoveredPrintersModel.py index 15d002298c..e127ba48af 100644 --- a/cura/UI/MachineModels/DiscoveredPrintersModel.py +++ b/cura/UI/MachineModels/DiscoveredPrintersModel.py @@ -88,7 +88,7 @@ class DiscoveredPrintersModel(QObject): discoveredPrintersChanged = pyqtSignal() @pyqtProperty(list, notify = discoveredPrintersChanged) - def discovered_printers(self) -> "List[DiscoveredPrinter]": + def discovered_printers(self) -> List["DiscoveredPrinter"]: item_list = list(x for x in self._discovered_printer_by_ip_dict.values()) item_list.sort(key = lambda x: x.name) return item_list @@ -125,6 +125,8 @@ class DiscoveredPrintersModel(QObject): del self._discovered_printer_by_ip_dict[ip_address] self.discoveredPrintersChanged.emit() + # A convenience function for QML to create a machine (GlobalStack) out of the given discovered printer. + # This function invokes the given discovered printer's "create_callback" to do this. @pyqtSlot("QVariant") def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None: discovered_printer.create_callback(discovered_printer.getKey()) diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index d72071fd7f..2b58218cd6 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -49,8 +49,8 @@ class WelcomePagesModel(ListModel): self._pages.append({"id": "data_collections", "page_url": self._getBuiltinWelcomePagePath("DataCollectionsContent.qml"), }) - self._pages.append({"id": "add_printer_by_selection", - "page_url": self._getBuiltinWelcomePagePath("AddPrinterBySelectionContent.qml"), + self._pages.append({"id": "add_network_or_local_printer", + "page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"), }) self._pages.append({"id": "add_printer_by_ip", "page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"), diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 9f1684f624..3cfa20c1f9 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -1,25 +1,25 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json +import os from queue import Queue from threading import Event, Thread from time import time -import os +from typing import Optional, TYPE_CHECKING, Dict from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo + from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager from PyQt5.QtCore import QUrl from PyQt5.QtGui import QDesktopServices from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputDevice import ConnectionType -from cura.Settings.GlobalStack import GlobalStack # typing -from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin -from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.Message import Message +from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal, signalemitter @@ -28,9 +28,9 @@ from UM.Version import Version from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager -from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: + from PyQt5.QtNetwork import QNetworkReply from cura.Settings.GlobalStack import GlobalStack @@ -255,7 +255,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): name_request = QNetworkRequest(url) self._network_manager.get(name_request) - def _onNetworkRequestFinished(self, reply): + def _onNetworkRequestFinished(self, reply: "QNetworkReply") -> None: reply_url = reply.url().toString() address = reply.url().host() @@ -325,7 +325,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.getOutputDeviceManager().addOutputDevice(device) self.addManualDeviceSignal.emit(self.getPluginId(), device.getId(), address, properties) - def _onRemoveDevice(self, device_id): + def _onRemoveDevice(self, device_id: str) -> None: device = self._discovered_devices.pop(device_id, None) if device: if device.isConnected(): diff --git a/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml index 7ad0f5c96c..f8db8b297e 100644 --- a/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml +++ b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml @@ -16,10 +16,13 @@ ScrollView { id: base + // The currently selected machine item in the local machine list. property var currentItem: (machineList.currentIndex >= 0) ? machineList.model.getItem(machineList.currentIndex) : null + // The currently active (expanded) section/category, where section/category is the grouping of local machine items. property string currentSection: preferredCategory + // By default (when this list shows up) we always expand the "Ultimaker" section. property string preferredCategory: "Ultimaker" ScrollBar.horizontal.policy: ScrollBar.AlwaysOff diff --git a/resources/qml/WelcomePages/AddPrinterBySelectionContent.qml b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml similarity index 94% rename from resources/qml/WelcomePages/AddPrinterBySelectionContent.qml rename to resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml index 3282b219c9..3004a563f4 100644 --- a/resources/qml/WelcomePages/AddPrinterBySelectionContent.qml +++ b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml @@ -19,7 +19,7 @@ Item { id: titleLabel anchors.top: parent.top - anchors.topMargin: 40 + anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@label", "Add a printer") @@ -67,7 +67,7 @@ Item onAddByIpButtonClicked: { - base.gotoPage("add_printer_by_ip") + base.goToPage("add_printer_by_ip") } } } @@ -101,8 +101,6 @@ Item AddLocalPrinterScrollView { id: localPrinterView - - maxItemCountAtOnce: 10 // show at max 10 items at once, otherwise you need to scroll. } } } @@ -112,7 +110,7 @@ Item id: nextButton anchors.right: parent.right anchors.bottom: parent.bottom - anchors.margins: 40 + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width enabled: { // If the network printer dropdown is expanded, make sure that there is a selected item @@ -148,7 +146,7 @@ Item // TODO: implement machine actions // If we have created a machine, go to the last page, which is the "cloud" page. - base.gotoPage("cloud") + base.goToPage("cloud") } } } diff --git a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml index 575ec9c126..0d37b2092b 100644 --- a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml +++ b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml @@ -73,10 +73,9 @@ Item Component.onCompleted: { - // select the first one that's not "unknown" by default. + // Select the first one that's not "unknown" by default. for (var i = 0; i < count; i++) { - if (!model[i].is_unknown_machine_type) { currentIndex = i diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 034ee74e76..1ea0c1f19d 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -18,15 +18,18 @@ Item id: addPrinterByIpScreen - property bool hasSentRequest: false // True when a request has been sent to the device at the typed address. - property bool haveConnection: false // True when there is a connection with a machine, it can then be added. - property bool deviceUnresponsive: false // True when a request comes back, but the device hasn't responded. + // Whether an IP address is currently being resolved. + property bool hasSentRequest: false + // Whether the IP address user entered can be resolved as a recognizable printer. + property bool haveConnection: false + // True when a request comes back, but the device hasn't responded. + property bool deviceUnresponsive: false Label { id: titleLabel anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@label", "Add printer by IP address") @@ -240,13 +243,11 @@ Item id: backButton anchors.left: parent.left anchors.bottom: parent.bottom - anchors.margins: UM.Theme.getSize("default_margin").width + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Cancel") width: UM.Theme.getSize("action_button").width fixedWidthMode: true - onClicked: base.gotoPage("add_printer_by_selection") - - enabled: true + onClicked: base.goToPage("add_printer_by_selection") } Cura.PrimaryButton @@ -254,7 +255,7 @@ Item id: connectButton anchors.right: parent.right anchors.bottom: parent.bottom - anchors.margins: UM.Theme.getSize("default_margin").width + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Connect") width: UM.Theme.getSize("action_button").width fixedWidthMode: true diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index 09e52c17dd..84b8232a3f 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -19,7 +19,7 @@ Item { id: titleLabel anchors.top: parent.top - anchors.topMargin: 40 + anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@label", "Ultimaker Cloud") @@ -28,48 +28,65 @@ Item renderType: Text.NativeRendering } - Column + // Area where the cloud contents can be put. Pictures, texts and such. + Item { + id: cloudContentsArea anchors.top: titleLabel.bottom - anchors.topMargin: 80 - anchors.horizontalCenter: parent.horizontalCenter - - spacing: 60 - - Image - { - id: cloudImage - anchors.horizontalCenter: parent.horizontalCenter - source: UM.Theme.getImage("first_run_ultimaker_cloud") - } + anchors.bottom: finishButton.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: UM.Theme.getSize("default_margin").width + // Pictures and texts are arranged using Columns with spacing. The whole picture and text area is centered in + // the cloud contents area. Column { - anchors.horizontalCenter: parent.horizontalCenter + anchors.centerIn: parent + width: childrenRect.width + height: childrenRect.height - spacing: 30 + spacing: 20 * screenScaleFactor - Label + Image // Cloud image + { + id: cloudImage + anchors.horizontalCenter: parent.horizontalCenter + source: UM.Theme.getImage("first_run_ultimaker_cloud") + } + + Label // A title-ish text { id: highlightTextLabel anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@text", "The next generation 3D printing workflow") textFormat: Text.RichText - color: UM.Theme.getColor("text_light_blue") + color: UM.Theme.getColor("primary") font: UM.Theme.getFont("medium") renderType: Text.NativeRendering } - Label + Label // A number of text items { id: textLabel anchors.horizontalCenter: parent.horizontalCenter - text: { - var t = "
- Send print jobs to Ultimaker printers outside your local network
" - t += "
- Store your Ultimaker Cura settings in the cloud for use anywhere
" - t += "- Get exclusive access to material profiles from leading brands
" - catalog.i18nc("@text", t) + text: + { + // There are 3 text items, each of which is translated separately as a single piece of text. + var full_text = "" + var t = "" + + t = catalog.i18nc("@text", "- Send print jobs to Ultimaker printers outside your local network") + full_text += "" + t + "
" + + t = catalog.i18nc("@text", "- Store your Ultimaker Cura settings in the cloud for use anywhere") + full_text += "" + t + "
" + + t = catalog.i18nc("@text", "- Get exclusive access to material profiles from leading brands") + full_text += "" + t + "
" + + return full_text } textFormat: Text.RichText font: UM.Theme.getFont("medium") @@ -78,12 +95,13 @@ Item } } + // Bottom buttons go here Cura.PrimaryButton { id: finishButton anchors.right: parent.right anchors.bottom: parent.bottom - anchors.margins: 40 + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Finish") width: 140 fixedWidthMode: true @@ -95,25 +113,31 @@ Item id: createAccountButton anchors.left: parent.left anchors.verticalCenter: finishButton.verticalCenter - anchors.margins: 40 + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Create an account") width: 140 fixedWidthMode: true onClicked: Qt.openUrlExternally(CuraApplication.ultimakerCloudAccountRootUrl + "/app/create") } - Cura.SecondaryButton + Label { id: signInButton anchors.left: createAccountButton.right anchors.verticalCenter: finishButton.verticalCenter + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Sign in") - width: 80 - shadowEnabled: false - color: "transparent" - hoverColor: "transparent" - textHoverColor: UM.Theme.getColor("text_light_blue") - fixedWidthMode: true - onClicked: Cura.API.account.login() + color: UM.Theme.getColor("secondary_button_text") + font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering + + MouseArea + { + anchors.fill: parent + hoverEnabled: true + onClicked: Cura.API.account.login() + onEntered: parent.font.underline = true + onExited: parent.font.underline = false + } } } diff --git a/resources/qml/WelcomePages/DataCollectionsContent.qml b/resources/qml/WelcomePages/DataCollectionsContent.qml index 93426d2c2c..b17c24300a 100644 --- a/resources/qml/WelcomePages/DataCollectionsContent.qml +++ b/resources/qml/WelcomePages/DataCollectionsContent.qml @@ -19,7 +19,7 @@ Item { id: titleLabel anchors.top: parent.top - anchors.topMargin: 40 + anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@label", "Help us to improve Ultimaker Cura") @@ -28,30 +28,39 @@ Item renderType: Text.NativeRendering } - Column + // Area where the cloud contents can be put. Pictures, texts and such. + Item { + id: cloudContentsArea anchors.top: titleLabel.bottom - anchors.topMargin: 80 - anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: getStartedButton.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: UM.Theme.getSize("default_margin").width - spacing: 60 - - Image + Column { - id: curaImage - anchors.horizontalCenter: parent.horizontalCenter - source: UM.Theme.getImage("first_run_share_data") - } + anchors.centerIn: parent - Label - { - id: textLabel - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - text: catalog.i18nc("@text", "Ultimaker Cura collects anonymous data to improve print quality