Merge branch 'master' of github.com:Ultimaker/Cura

This commit is contained in:
Jaime van Kessel 2019-05-03 13:17:27 +02:00
commit 08b79a93be
28 changed files with 352 additions and 97 deletions

View file

@ -117,6 +117,8 @@ from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
from cura.UI.WelcomePagesModel import WelcomePagesModel
from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel
from cura.Utils.NetworkingUtil import NetworkingUtil
from .SingleInstance import SingleInstance
from .AutoSave import AutoSave
from . import PlatformPhysics
@ -1028,6 +1030,8 @@ class CuraApplication(QtApplication):
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel")
qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel")
qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel")

View file

@ -192,7 +192,7 @@ class DiscoveredPrintersModel(QObject):
def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]:
return self._discovered_printer_by_ip_dict
@pyqtProperty(list, notify = discoveredPrintersChanged)
@pyqtProperty("QVariantList", notify = discoveredPrintersChanged)
def discoveredPrinters(self) -> List["DiscoveredPrinter"]:
item_list = list(
x for x in self._discovered_printer_by_ip_dict.values() if not parseBool(x.device.getProperty("temporary")))

View file

@ -35,6 +35,8 @@ class FirstStartMachineActionsModel(ListModel):
self._application = application
self._application.initializationFinished.connect(self._initialize)
self._previous_global_stack = None
def _initialize(self) -> None:
self._application.getMachineManager().globalContainerChanged.connect(self._update)
self._update()
@ -86,6 +88,12 @@ class FirstStartMachineActionsModel(ListModel):
self.setItems([])
return
# Do not update if the machine has not been switched. This can cause the SettingProviders on the Machine
# Setting page to do a force update, but they can use potential outdated cached values.
if self._previous_global_stack is not None and global_stack.getId() == self._previous_global_stack.getId():
return
self._previous_global_stack = global_stack
definition_id = global_stack.definition.getId()
first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id)

View file

@ -5,6 +5,7 @@ from PyQt5.QtCore import Qt, QTimer
from UM.Qt.ListModel import ListModel
from UM.i18n import i18nCatalog
from UM.Util import parseBool
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
@ -54,7 +55,6 @@ class GlobalStacksModel(ListModel):
items = []
container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
for container_stack in container_stacks:
has_remote_connection = False
@ -62,7 +62,7 @@ class GlobalStacksModel(ListModel):
has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value,
ConnectionType.CloudConnection.value]
if container_stack.getMetaDataEntry("hidden", False) in ["True", True]:
if parseBool(container_stack.getMetaDataEntry("hidden", False)):
continue
section_name = "Network enabled printers" if has_remote_connection else "Local printers"

View file

@ -4,6 +4,8 @@
from collections import defaultdict
import threading
from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List
import uuid
from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
from UM.Decorators import override
@ -34,6 +36,12 @@ class GlobalStack(CuraContainerStack):
self.setMetaDataEntry("type", "machine") # For backward compatibility
# TL;DR: If Cura is looking for printers that belong to the same group, it should use "group_id".
# Each GlobalStack by default belongs to a group which is identified via "group_id". This group_id is used to
# figure out which GlobalStacks are in the printer cluster for example without knowing the implementation
# details such as the um_network_key or some other identifier that's used by the underlying device plugin.
self.setMetaDataEntry("group_id", str(uuid.uuid4())) # Assign a new GlobalStack to a unique group by default
self._extruders = {} # type: Dict[str, "ExtruderStack"]
# This property is used to track which settings we are calculating the "resolve" for

View file

@ -797,7 +797,6 @@ class MachineManager(QObject):
self.setActiveMachine(other_machine_stacks[0]["id"])
metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
network_key = metadata.get("um_network_key", None)
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
for container in containers:
@ -805,8 +804,9 @@ class MachineManager(QObject):
CuraContainerRegistry.getInstance().removeContainer(machine_id)
# If the printer that is being removed is a network printer, the hidden printers have to be also removed
if network_key:
metadata_filter = {"um_network_key": network_key}
group_id = metadata.get("group_id", None)
if group_id:
metadata_filter = {"group_id": group_id}
hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
if hidden_containers:
# This reuses the method and remove all printers recursively
@ -1360,21 +1360,24 @@ class MachineManager(QObject):
# Get the definition id corresponding to this machine name
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
# Try to find a machine with the same network key
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey()})
metadata_filter = {"group_id": self._global_container_stack.getMetaDataEntry("group_id"),
"um_network_key": self.activeMachineNetworkKey(),
}
new_machine = self.getMachine(machine_definition_id, metadata_filter = metadata_filter)
# If there is no machine, then create a new one and set it to the non-hidden instance
if not new_machine:
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
if not new_machine:
return
new_machine.setMetaDataEntry("group_id", self._global_container_stack.getMetaDataEntry("group_id"))
new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey())
new_machine.setMetaDataEntry("group_name", self.activeMachineNetworkGroupName)
new_machine.setMetaDataEntry("hidden", False)
new_machine.setMetaDataEntry("connection_type", self._global_container_stack.getMetaDataEntry("connection_type"))
else:
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey())
new_machine.setMetaDataEntry("hidden", False)
# Set the current printer instance to hidden (the metadata entry must exist)
new_machine.setMetaDataEntry("hidden", False)
self._global_container_stack.setMetaDataEntry("hidden", True)
self.setActiveMachine(new_machine.getId())
@ -1654,3 +1657,7 @@ class MachineManager(QObject):
if results:
machine_type_name = results[0]["name"]
return machine_type_name
# Gets all machines that belong to the given group_id.
def getMachinesInGroup(self, group_id: str) -> List["GlobalStack"]:
return self._container_registry.findContainerStacks(type = "machine", group_id = group_id)

View file

@ -15,6 +15,7 @@ class AddPrinterPagesModel(WelcomePagesModel):
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
"next_page_id": "machine_actions",
"next_page_button_text": self._catalog.i18nc("@action:button", "Add"),
"previous_page_button_text": self._catalog.i18nc("@action:button", "Cancel"),
})
self._pages.append({"id": "add_printer_by_ip",
"page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"),

View file

@ -39,6 +39,7 @@ class WelcomePagesModel(ListModel):
PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file
NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to
NextPageButtonTextRole = Qt.UserRole + 4 # The text for the next page button
PreviousPageButtonTextRole = Qt.UserRole + 5 # The text for the previous page button
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
super().__init__(parent)
@ -47,6 +48,7 @@ class WelcomePagesModel(ListModel):
self.addRoleName(self.PageUrlRole, "page_url")
self.addRoleName(self.NextPageIdRole, "next_page_id")
self.addRoleName(self.NextPageButtonTextRole, "next_page_button_text")
self.addRoleName(self.PreviousPageButtonTextRole, "previous_page_button_text")
self._application = application
self._catalog = i18nCatalog("cura")

View file

@ -0,0 +1,44 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import socket
from typing import Optional
from PyQt5.QtCore import QObject, pyqtSlot
#
# This is a QObject because some of the functions can be used (and are useful) in QML.
#
class NetworkingUtil(QObject):
def __init__(self, parent: Optional["QObject"] = None) -> None:
super().__init__(parent = parent)
# Checks if the given string is a valid IPv4 address.
@pyqtSlot(str, result = bool)
def isIPv4(self, address: str) -> bool:
try:
socket.inet_pton(socket.AF_INET, address)
result = True
except:
result = False
return result
# Checks if the given string is a valid IPv6 address.
@pyqtSlot(str, result = bool)
def isIPv6(self, address: str) -> bool:
try:
socket.inet_pton(socket.AF_INET6, address)
result = True
except:
result = False
return result
# Checks if the given string is a valid IPv4 or IPv6 address.
@pyqtSlot(str, result = bool)
def isValidIP(self, address: str) -> bool:
return self.isIPv4(address) or self.isIPv6(address)
__all__ = ["NetworkingUtil"]

View file

@ -285,18 +285,30 @@ Item
optionModel: ListModel
{
id: extruderCountModel
Component.onCompleted:
{
extruderCountModel.clear()
update()
}
function update()
{
clear()
for (var i = 1; i <= Cura.MachineManager.activeMachine.maxExtruderCount; i++)
{
// Use String as value. JavaScript only has Number. PropertyProvider.setPropertyValue()
// takes a QVariant as value, and Number gets translated into a float. This will cause problem
// for integer settings such as "Number of Extruders".
extruderCountModel.append({ text: String(i), value: String(i) })
append({ text: String(i), value: String(i) })
}
}
}
Connections
{
target: Cura.MachineManager
onGlobalContainerChanged: extruderCountModel.update()
}
}
}
}

View file

@ -71,7 +71,7 @@ Window
left: parent.left
right: parent.right
}
text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is sent:")
text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is shared:")
wrapMode: Text.WordWrap
renderType: Text.NativeRendering
}
@ -106,7 +106,7 @@ Window
Cura.RadioButton
{
id: dontSendButton
text: catalog.i18nc("@text:window", "I don't want to send this data")
text: catalog.i18nc("@text:window", "I don't want to send anonymous data")
onClicked:
{
baseDialog.allowSendData = !checked
@ -115,7 +115,7 @@ Window
Cura.RadioButton
{
id: allowSendButton
text: catalog.i18nc("@text:window", "Allow sending this data to Ultimaker and help us improve Cura")
text: catalog.i18nc("@text:window", "Allow sending anonymous data")
onClicked:
{
baseDialog.allowSendData = checked

View file

@ -1,8 +1,8 @@
// Copyright (c) 2018 Ultimaker B.V.
// Copyright (c) 2019 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import UM 1.2 as UM
import Cura 1.0 as Cura
import Cura 1.5 as Cura
import QtQuick 2.2
import QtQuick.Controls 1.1
@ -14,9 +14,13 @@ Cura.MachineAction
{
id: base
anchors.fill: parent;
property alias currentItemIndex: listview.currentIndex
property var selectedDevice: null
property bool completeProperties: true
// For validating IP addresses
property var networkingUtil: Cura.NetworkingUtil {}
function connectToPrinter()
{
if(base.selectedDevice && base.completeProperties)
@ -342,6 +346,17 @@ Cura.MachineAction
}
}
MessageDialog
{
id: invalidIPAddressMessageDialog
x: (parent.x + (parent.width) / 2) | 0
y: (parent.y + (parent.height) / 2) | 0
title: catalog.i18nc("@title:window", "Invalid IP address")
text: catalog.i18nc("@text", "Please enter a valid IP address.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
UM.Dialog
{
id: manualPrinterDialog
@ -404,6 +419,26 @@ Cura.MachineAction
text: catalog.i18nc("@action:button", "OK")
onClicked:
{
// Validate the input first
if (!networkingUtil.isValidIP(manualPrinterDialog.addressText))
{
invalidIPAddressMessageDialog.open()
return
}
// if the entered IP address has already been discovered, switch the current item to that item
// and do nothing else.
for (var i = 0; i < manager.foundDevices.length; i++)
{
var device = manager.foundDevices[i]
if (device.address == manualPrinterDialog.addressText)
{
currentItemIndex = i
manualPrinterDialog.hide()
return
}
}
manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
manualPrinterDialog.hide()
}

View file

@ -110,14 +110,12 @@ class DiscoverUM3Action(MachineAction):
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "group_name" in meta_data:
previous_connect_group_name = meta_data["group_name"]
global_container_stack.setMetaDataEntry("group_name", group_name)
# Find all the places where there is the same group name and change it accordingly
self._replaceContainersMetadata(key = "group_name", value = previous_connect_group_name, new_value = group_name)
else:
global_container_stack.setMetaDataEntry("group_name", group_name)
# Update a GlobalStacks in the same group with the new group name.
group_id = global_container_stack.getMetaDataEntry("group_id")
machine_manager = CuraApplication.getInstance().getMachineManager()
for machine in machine_manager.getMachinesInGroup(group_id):
machine.setMetaDataEntry("group_name", group_name)
# Set the default value for "hidden", which is used when you have a group with multiple types of printers
global_container_stack.setMetaDataEntry("hidden", False)
@ -125,13 +123,6 @@ class DiscoverUM3Action(MachineAction):
# Ensure that the connection states are refreshed.
self._network_plugin.refreshConnections()
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
def _replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
machines = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
for machine in machines:
if machine.getMetaDataEntry(key) == value:
machine.setMetaDataEntry(key, new_value)
# Associates the currently active machine with the given printer device. The network connection information will be
# stored into the metadata of the currently active machine.
@pyqtSlot(QObject)

View file

@ -283,34 +283,24 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key)
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
machine_manager = CuraApplication.getInstance().getMachineManager()
global_container_stack = machine_manager.activeMachine
if not global_container_stack:
return
meta_data = global_container_stack.getMetaData()
for machine in machine_manager.getMachinesInGroup(global_container_stack.getMetaDataEntry("group_id")):
machine.setMetaDataEntry("um_network_key", printer_device.key)
machine.setMetaDataEntry("group_name", printer_device.name)
if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed.
old_network_key = meta_data["um_network_key"]
# Since we might have a bunch of hidden stacks, we also need to change it there.
metadata_filter = {"um_network_key": old_network_key}
containers = self._application.getContainerRegistry().findContainerStacks(type = "machine", **metadata_filter)
# Delete old authentication data.
Logger.log("d", "Removing old authentication id %s for device %s",
global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key)
for container in containers:
container.setMetaDataEntry("um_network_key", printer_device.key)
machine.removeMetaDataEntry("network_authentication_id")
machine.removeMetaDataEntry("network_authentication_key")
# Delete old authentication data.
Logger.log("d", "Removing old authentication id %s for device %s",
global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key)
container.removeMetaDataEntry("network_authentication_id")
container.removeMetaDataEntry("network_authentication_key")
# Ensure that these containers do know that they are configured for network connection
container.addConfiguredConnectionType(printer_device.connectionType.value)
else: # Global stack didn't have a connection yet, configure it.
global_container_stack.setMetaDataEntry("um_network_key", printer_device.key)
global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value)
# Ensure that these containers do know that they are configured for network connection
machine.addConfiguredConnectionType(printer_device.connectionType.value)
self.refreshConnections()

View file

@ -3,6 +3,7 @@
import configparser
import io
import uuid
from typing import Dict, List, Tuple
from UM.VersionUpgrade import VersionUpgrade
@ -18,6 +19,7 @@ _renamed_quality_profiles = {
"gmax15plus_pla_very_thick": "gmax15plus_global_very_thick"
} # type: Dict[str, str]
## Upgrades configurations from the state they were in at version 4.0 to the
# state they should be in at version 4.1.
class VersionUpgrade40to41(VersionUpgrade):
@ -95,6 +97,13 @@ class VersionUpgrade40to41(VersionUpgrade):
if parser["containers"]["4"] in _renamed_quality_profiles:
parser["containers"]["4"] = _renamed_quality_profiles[parser["containers"]["4"]]
# Assign a GlobalStack to a unique group_id. If the GlobalStack has a UM network connection, use the UM network
# key as the group_id.
if "um_network_key" in parser["metadata"]:
parser["metadata"]["group_id"] = parser["metadata"]["um_network_key"]
elif "group_id" not in parser["metadata"]:
parser["metadata"]["group_id"] = str(uuid.uuid4())
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View file

@ -0,0 +1,45 @@
{
"version": 2,
"name": "Anet A6",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Mark",
"manufacturer": "Anet",
"file_formats": "text/x-gcode",
"platform": "aneta6_platform.stl",
"platform_offset": [0, -3.4, 0],
"machine_extruder_trains":
{
"0": "anet_a6_extruder_0"
}
},
"overrides": {
"machine_name": { "default_value": "Anet A6" },
"machine_heated_bed": {
"default_value": true
},
"machine_width": {
"default_value": 220
},
"machine_height": {
"default_value": 250
},
"machine_depth": {
"default_value": 220
},
"machine_center_is_zero": {
"default_value": false
},
"gantry_height": {
"default_value": 55
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM84 ;steppers off\nM0 S12 ;wait 12 seconds\nM17 ;turn steppers on\nG1 Z10.0 F300 ;move the platform down 10mm\nG92 E0 ;zero the extruded length\nG1 F200 E8 ;extrude 8mm of feed stock\nG92 E0 ;zero the extruded length again\nM0 S5 ;wait 5 seconds\nG1 F9000\nM117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+4 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y210 F9000 ;move out to get part off\nM84 ;steppers off\nG90 ;absolute positioning"
}
}
}

View file

@ -5248,7 +5248,7 @@
"type": "bool",
"enabled": "extruders_enabled_count > 1",
"default_value": false,
"resolve": "any(extruderValues('prime_tower_enable'))",
"resolve": "any(extruderValues('prime_tower_enable')) or (adhesion_type in ('none', 'skirt'))",
"settable_per_mesh": false,
"settable_per_extruder": false
},

View file

@ -0,0 +1,16 @@
{
"id": "anet_a6_extruder_0",
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "anet_a6",
"position": "0"
},
"overrides": {
"extruder_nr": { "default_value": 0 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

Binary file not shown.

View file

@ -795,7 +795,6 @@ UM.MainWindow
title: catalog.i18nc("@title:window", "Add Printer")
model: CuraApplication.getAddPrinterPagesModel()
progressBarVisible: false
hasCancelButton: true
}
Cura.WizardDialog
@ -804,7 +803,6 @@ UM.MainWindow
title: catalog.i18nc("@title:window", "What's New")
model: CuraApplication.getWhatsNewPagesModel()
progressBarVisible: false
hasCancelButton: false
}
Connections

View file

@ -3,7 +3,26 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtGraphicalEffects 1.0 // For the dropshadow
import UM 1.2 as UM
// Empty placeholder
Item { }
Rectangle
{
color: UM.Theme.getColor("disabled")
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: parent
source: parent
verticalOffset: 2
visible: true
color: UM.Theme.getColor("action_button_shadow")
// Should always be drawn behind the background.
z: parent.z - 1
}
}

View file

@ -76,6 +76,4 @@ NumericTextFieldWithUnit
forceUpdateOnChangeFunction()
}
}
// TODO: add forceUpdateOnChangeFunction:
}

View file

@ -32,16 +32,7 @@ ListView
width: listView.width
outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
checked:
{
// If the machine has a remote connection
var result = Cura.MachineManager.activeMachineId == model.id
if (Cura.MachineManager.activeMachineHasRemoteConnection)
{
result |= Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["group_name"]
}
return result
}
checked: Cura.MachineManager.activeMachineId == model.id
onClicked:
{

View file

@ -98,6 +98,20 @@ Item
}
}
// This "Back" button only shows in the "Add Machine" dialog, which has "previous_page_button_text" set to "Cancel"
Cura.SecondaryButton
{
id: backButton
anchors.left: parent.left
anchors.bottom: parent.bottom
visible: base.currentItem.previous_page_button_text ? true : false
text: base.currentItem.previous_page_button_text ? base.currentItem.previous_page_button_text : ""
onClicked:
{
base.endWizard()
}
}
Cura.PrimaryButton
{
id: nextButton

View file

@ -76,15 +76,28 @@ Item
Component.onCompleted:
{
// Select the first one that's not "unknown" by default.
var toSelectIndex = -1
// Select the first one that's not "unknown" and is the host a group by default.
for (var i = 0; i < count; i++)
{
if (!model[i].isUnknownMachineType)
if (!model[i].isUnknownMachineType && model[i].isHostOfGroup)
{
currentIndex = i
toSelectIndex = i
break
}
}
currentIndex = toSelectIndex
}
// CURA-6483 For some reason currentIndex can be reset to 0. This check is here to prevent automatically
// selecting an unknown or non-host printer.
onCurrentIndexChanged:
{
var item = model[currentIndex]
if (!item || item.isUnknownMachineType || !item.isHostOfGroup)
{
currentIndex = -1
}
}
Component

View file

@ -6,7 +6,7 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import UM 1.3 as UM
import Cura 1.1 as Cura
import Cura 1.5 as Cura
//
@ -22,9 +22,35 @@ Item
property bool hasRequestInProgress: CuraApplication.getDiscoveredPrintersModel().hasManualDeviceRequestInProgress
// Indicates if a request has finished.
property bool hasRequestFinished: false
property string currentRequestAddress: ""
property var discoveredPrinter: null
property var isPrinterDiscovered: discoveredPrinter != null
property bool isPrinterDiscovered: discoveredPrinter != null
// A printer can only be added if it doesn't have an unknown type and it's the host of a group.
property bool canAddPrinter: isPrinterDiscovered && !discoveredPrinter.isUnknownMachineType && discoveredPrinter.isHostOfGroup
// For validating IP address
property var networkingUtil: Cura.NetworkingUtil {}
// CURA-6483
// For a manually added UM printer, the UM3OutputDevicePlugin will first create a LegacyUM device for it. Later,
// when it gets more info from the printer, it will first REMOVE the LegacyUM device and then add a ClusterUM device.
// The Add-by-IP page needs to make sure that the user do not add an unknown printer or a printer that's not the
// host of a group. Because of the device list change, this page needs to react upon DiscoveredPrintersChanged so
// it has the correct information.
Connections
{
target: CuraApplication.getDiscoveredPrintersModel()
onDiscoveredPrintersChanged:
{
if (hasRequestFinished && currentRequestAddress)
{
var printer = CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[currentRequestAddress]
printer = printer ? printer : null
discoveredPrinter = printer
}
}
}
// Make sure to cancel the current request when this page closes.
onVisibleChanged:
@ -93,17 +119,36 @@ Item
anchors.verticalCenter: addPrinterButton.verticalCenter
anchors.left: parent.left
signal invalidInputDetected()
onInvalidInputDetected: invalidInputLabel.visible = true
validator: RegExpValidator
{
regExp: /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))?/
regExp: /([a-fA-F0-9.:]+)?/
}
onTextEdited: invalidInputLabel.visible = false
placeholderText: catalog.i18nc("@text", "Place enter your printer's IP address.")
enabled: { ! (addPrinterByIpScreen.hasRequestInProgress || addPrinterByIpScreen.isPrinterDiscovered) }
onAccepted: addPrinterButton.clicked()
}
Label
{
id: invalidInputLabel
anchors.top: hostnameField.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
visible: false
text: catalog.i18nc("@text", "Please enter a valid IP address.")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
}
Cura.SecondaryButton
{
id: addPrinterButton
@ -115,14 +160,21 @@ Item
onClicked:
{
const address = hostnameField.text
if (!networkingUtil.isValidIP(address))
{
hostnameField.invalidInputDetected()
return
}
// This address is already in the discovered printer model, no need to add a manual discovery.
if (CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address])
{
addPrinterByIpScreen.discoveredPrinter = CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address]
addPrinterByIpScreen.hasRequestFinished = true
return
}
addPrinterByIpScreen.currentRequestAddress = address
CuraApplication.getDiscoveredPrintersModel().checkManualDevice(address)
}
busy: addPrinterByIpScreen.hasRequestInProgress
@ -161,6 +213,8 @@ Item
Item
{
id: printerInfoLabels
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width
@ -177,10 +231,24 @@ Item
text: !addPrinterByIpScreen.isPrinterDiscovered ? "???" : addPrinterByIpScreen.discoveredPrinter.name
}
Label
{
id: printerCannotBeAddedLabel
width: parent.width
anchors.top: printerNameLabel.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@label", "This printer cannot be added because it's an unknown printer or it's not the host of a group.")
visible: addPrinterByIpScreen.hasRequestFinished && !addPrinterByIpScreen.canAddPrinter
font: UM.Theme.getFont("default_bold")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
wrapMode: Text.WordWrap
}
GridLayout
{
id: printerInfoGrid
anchors.top: printerNameLabel.bottom
anchors.top: printerCannotBeAddedLabel ? printerCannotBeAddedLabel.bottom : printerNameLabel.bottom
anchors.margins: UM.Theme.getSize("default_margin").width
columns: 2
columnSpacing: UM.Theme.getSize("default_margin").width
@ -253,7 +321,7 @@ Item
}
}
Cura.PrimaryButton
Cura.SecondaryButton
{
id: backButton
anchors.left: parent.left
@ -278,6 +346,6 @@ Item
base.showNextPage()
}
enabled: addPrinterByIpScreen.isPrinterDiscovered
enabled: addPrinterByIpScreen.canAddPrinter
}
}

View file

@ -35,7 +35,7 @@ Item
horizontalAlignment: Text.AlignHCenter
text: catalog.i18nc("@label", "Ultimaker Cloud")
color: UM.Theme.getColor("primary_button")
font: UM.Theme.getFont("large_bold")
font: UM.Theme.getFont("huge")
renderType: Text.NativeRendering
}

View file

@ -31,7 +31,6 @@ Window
property var model: null // Needs to be set by whoever is using this dialog.
property alias progressBarVisible: wizardPanel.progressBarVisible
property alias hasCancelButton: cancelButton.visible
onVisibilityChanged:
{
@ -54,21 +53,4 @@ Window
target: model
onAllFinished: dialog.hide()
}
Cura.SecondaryButton
{
id: cancelButton
text: catalog.i18nc("@button", "Cancel")
visible: false
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: UM.Theme.getSize("default_margin").width
anchors.leftMargin: UM.Theme.getSize("wide_margin").width
enabled: true
onClicked: dialog.visible = false
}
}