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
and user experience. More information") - textFormat: Text.RichText - font: UM.Theme.getFont("medium") - renderType: Text.NativeRendering + spacing: UM.Theme.getSize("welcome_pages_default_margin").height + + Image + { + id: curaImage + anchors.horizontalCenter: parent.horizontalCenter + source: UM.Theme.getImage("first_run_share_data") + } + + Label + { + id: textLabel + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: catalog.i18nc("@text", "Ultimaker Cura collects anonymous data to improve print quality
and user experience. More information") + textFormat: Text.RichText + font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering + } } } @@ -60,7 +69,7 @@ Item id: getStartedButton 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", "Next") width: 140 fixedWidthMode: true diff --git a/resources/qml/WelcomePages/DropDownWidget.qml b/resources/qml/WelcomePages/DropDownWidget.qml index cff9cf8ac1..9f413769e0 100644 --- a/resources/qml/WelcomePages/DropDownWidget.qml +++ b/resources/qml/WelcomePages/DropDownWidget.qml @@ -21,14 +21,14 @@ Item id: base - implicitWidth: 200 - height: header.contentShown ? (header.height + contentRectangle.height + 30) : header.height + implicitWidth: 200 * screenScaleFactor + height: header.contentShown ? (header.height + contentRectangle.height) : header.height property var contentComponent: null property alias contentItem: contentLoader.item property alias title: header.title - property bool contentShown: false + property bool contentShown: false // indicates if this dropdown widget is expanded to show its content signal clicked() @@ -59,7 +59,7 @@ Item anchors.top: header.bottom anchors.left: header.left anchors.right: header.right - height: contentLoader.height + 2 + height: contentLoader.height border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") @@ -74,7 +74,9 @@ Item anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - anchors.margins: 1 + // Keep a small margin with the Rectangle container so its content will not overlap with the Rectangle + // border. + anchors.margins: UM.Theme.getSize("default_lining").width sourceComponent: base.contentComponent != null ? base.contentComponent : emptyComponent } diff --git a/resources/qml/WelcomePages/StepPanel.qml b/resources/qml/WelcomePages/StepPanel.qml index bc99320e02..551a01687a 100644 --- a/resources/qml/WelcomePages/StepPanel.qml +++ b/resources/qml/WelcomePages/StepPanel.qml @@ -33,7 +33,7 @@ Item signal showNextPage() signal showPreviousPage() signal passLastPage() // Emitted when there is no more page to show - signal gotoPage(string page_id) // Go to a specific page by the given page_id. + signal goToPage(string page_id) // Go to a specific page by the given page_id. onShowNextPage: { @@ -41,7 +41,8 @@ Item { currentStep++ } - else { + else + { passLastPage() } } @@ -54,7 +55,7 @@ Item } } - onGotoPage: + onGoToPage: { // find the page index var page_index = -1 @@ -71,10 +72,6 @@ Item { currentStep = page_index } - else - { - console.log("Error: cannot find page with page_id = [", page_id, "]") - } } onVisibleChanged: @@ -110,7 +107,6 @@ Item source: parent horizontalOffset: base.shadowOffset verticalOffset: base.shadowOffset - visible: true color: UM.Theme.getColor("monitor_shadow") transparentBorder: true // Should always be drawn behind the background. diff --git a/resources/qml/WelcomePages/UserAgreementContent.qml b/resources/qml/WelcomePages/UserAgreementContent.qml index 82b16ba2ee..dfec088ad6 100644 --- a/resources/qml/WelcomePages/UserAgreementContent.qml +++ b/resources/qml/WelcomePages/UserAgreementContent.qml @@ -58,7 +58,7 @@ Item id: agreeButton 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", "Agree") width: 140 fixedWidthMode: true @@ -75,7 +75,7 @@ Item id: declineButton anchors.left: parent.left anchors.bottom: parent.bottom - anchors.margins: 40 + anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Decline and close") width: 140 fixedWidthMode: true diff --git a/resources/qml/WelcomePages/WhatsNewContent.qml b/resources/qml/WelcomePages/WhatsNewContent.qml index b083c99e32..d9d3648d6a 100644 --- a/resources/qml/WelcomePages/WhatsNewContent.qml +++ b/resources/qml/WelcomePages/WhatsNewContent.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", "What's new in Ultimaker Cura") @@ -32,8 +32,8 @@ Item { anchors.top: titleLabel.bottom anchors.bottom: getStartedButton.top - anchors.topMargin: 40 - anchors.bottomMargin: 40 + anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height + anchors.bottomMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 3 / 4 @@ -76,7 +76,7 @@ Item id: getStartedButton 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", "Next") width: 140 fixedWidthMode: true diff --git a/resources/themes/cura-light/images/first_run_Ultimaker_cloud.svg b/resources/themes/cura-light/images/first_run_ultimaker_cloud.svg similarity index 100% rename from resources/themes/cura-light/images/first_run_Ultimaker_cloud.svg rename to resources/themes/cura-light/images/first_run_ultimaker_cloud.svg diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 83edda6486..2363ed28fe 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -193,8 +193,6 @@ "window_disabled_background": [0, 0, 0, 255], - "text_light_blue": [50, 130, 255, 255], - "text": [25, 25, 25, 255], "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], @@ -509,6 +507,8 @@ "button_icon": [2.5, 2.5], "button_lining": [0, 0], + "welcome_pages_default_margin": [2.5, 2.5], + "action_button": [15.0, 2.5], "action_button_icon": [1.0, 1.0], "action_button_radius": [0.15, 0.15],