diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index a4d7bc303e..19f8174a95 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -207,6 +207,7 @@ class CuraApplication(QtApplication):
self._first_start_machine_actions_model = None
self._welcome_pages_model = WelcomePagesModel(self, parent = self)
self._add_printer_pages_model = AddPrinterPagesModel(self, parent = self)
+ self._add_printer_pages_model_without_cancel = AddPrinterPagesModel(self, parent = self)
self._whats_new_pages_model = WhatsNewPagesModel(self, parent = self)
self._text_manager = TextManager(parent = self)
@@ -647,7 +648,7 @@ class CuraApplication(QtApplication):
return self._global_container_stack
@override(Application)
- def setGlobalContainerStack(self, stack: "GlobalStack") -> None:
+ def setGlobalContainerStack(self, stack: Optional["GlobalStack"]) -> None:
self._setLoadingHint(self._i18n_catalog.i18nc("@info:progress", "Initializing Active Machine..."))
super().setGlobalContainerStack(stack)
@@ -812,6 +813,7 @@ class CuraApplication(QtApplication):
self._output_device_manager.start()
self._welcome_pages_model.initialize()
self._add_printer_pages_model.initialize()
+ self._add_printer_pages_model_without_cancel.initialize(cancellable = False)
self._whats_new_pages_model.initialize()
# Detect in which mode to run and execute that mode
@@ -849,6 +851,7 @@ class CuraApplication(QtApplication):
self.callLater(self._openFile, file_name)
initializationFinished = pyqtSignal()
+ showAddPrintersUncancellableDialog = pyqtSignal() # Used to show the add printers dialog with a greyed background
def runWithoutGUI(self):
"""Run Cura without GUI elements and interaction (server mode)."""
@@ -939,6 +942,10 @@ class CuraApplication(QtApplication):
def getAddPrinterPagesModel(self, *args) -> "AddPrinterPagesModel":
return self._add_printer_pages_model
+ @pyqtSlot(result = QObject)
+ def getAddPrinterPagesModelWithoutCancel(self, *args) -> "AddPrinterPagesModel":
+ return self._add_printer_pages_model_without_cancel
+
@pyqtSlot(result = QObject)
def getWhatsNewPagesModel(self, *args) -> "WhatsNewPagesModel":
return self._whats_new_pages_model
diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py
index aa8552bebb..776d540867 100644
--- a/cura/Machines/Models/BaseMaterialsModel.py
+++ b/cura/Machines/Models/BaseMaterialsModel.py
@@ -154,7 +154,7 @@ class BaseMaterialsModel(ListModel):
# Update the available materials (ContainerNode) for the current active machine and extruder setup.
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
- if not global_stack.hasMaterials:
+ if not global_stack or not global_stack.hasMaterials:
return # There are no materials for this machine, so nothing to do.
extruder_list = global_stack.extruderList
if self._extruder_position > len(extruder_list):
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index 523572dae0..423df167cd 100755
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -290,9 +290,15 @@ class MachineManager(QObject):
self.activeStackValueChanged.emit()
@pyqtSlot(str)
- def setActiveMachine(self, stack_id: str) -> None:
+ def setActiveMachine(self, stack_id: Optional[str]) -> None:
self.blurSettings.emit() # Ensure no-one has focus.
+ if not stack_id:
+ self._application.setGlobalContainerStack(None)
+ self.globalContainerChanged.emit()
+ self._application.showAddPrintersUncancellableDialog.emit()
+ return
+
container_registry = CuraContainerRegistry.getInstance()
containers = container_registry.findContainerStacks(id = stack_id)
if not containers:
@@ -721,6 +727,8 @@ class MachineManager(QObject):
other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
if other_machine_stacks:
self.setActiveMachine(other_machine_stacks[0]["id"])
+ else:
+ self.setActiveMachine(None)
metadatas = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)
if not metadatas:
diff --git a/cura/UI/AddPrinterPagesModel.py b/cura/UI/AddPrinterPagesModel.py
index b06f220374..9b35dbcacc 100644
--- a/cura/UI/AddPrinterPagesModel.py
+++ b/cura/UI/AddPrinterPagesModel.py
@@ -10,12 +10,11 @@ from .WelcomePagesModel import WelcomePagesModel
#
class AddPrinterPagesModel(WelcomePagesModel):
- def initialize(self) -> None:
+ def initialize(self, cancellable: bool = True) -> None:
self._pages.append({"id": "add_network_or_local_printer",
"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"),
@@ -30,6 +29,9 @@ class AddPrinterPagesModel(WelcomePagesModel):
"page_url": self._getBuiltinWelcomePagePath("FirstStartMachineActionsContent.qml"),
"should_show_function": self.shouldShowMachineActions,
})
+ if cancellable:
+ self._pages[0]["previous_page_button_text"] = self._catalog.i18nc("@action:button", "Cancel")
+
self.setItems(self._pages)
diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py
index 9f93d0fe54..33c9caba05 100644
--- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py
+++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py
@@ -4,6 +4,7 @@ import os
from typing import Dict, List, Optional, Set
from PyQt5.QtNetwork import QNetworkReply
+from PyQt5.QtWidgets import QMessageBox
from UM import i18nCatalog
from UM.Logger import Logger # To log errors talking to the API.
@@ -50,6 +51,7 @@ class CloudOutputDeviceManager:
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
self._account.loginStateChanged.connect(self._onLoginStateChanged)
+ self._removed_printers_message = None # type: Optional[Message]
# Ensure we don't start twice.
self._running = False
@@ -120,6 +122,11 @@ class CloudOutputDeviceManager:
self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
self._onDevicesDiscovered(new_clusters)
+ # Hide the current removed_printers_message, if there is any
+ if self._removed_printers_message:
+ self._removed_printers_message.actionTriggered.disconnect(self._onRemovedPrintersMessageActionTriggered)
+ self._removed_printers_message.hide()
+
# Remove the CloudOutput device for offline printers
offline_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys())
for device_id in offline_device_keys:
@@ -269,14 +276,13 @@ class CloudOutputDeviceManager:
return
# Generate message
- removed_printers_message = Message(
+ self._removed_printers_message = Message(
title = self.I18N_CATALOG.i18ncp(
"info:status",
"Cloud connection is not available for a printer",
"Cloud connection is not available for some printers",
len(self.reported_device_ids)
- ),
- lifetime = 0
+ )
)
device_names = "\n".join(["
{} ({})".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids])
message_text = self.I18N_CATALOG.i18ncp(
@@ -291,13 +297,19 @@ class CloudOutputDeviceManager:
"Ultimaker Digital Factory.",
device_names
)
- removed_printers_message.setText(message_text)
- removed_printers_message.addAction("keep_printer_configurations_action",
- name = self.I18N_CATALOG.i18nc("@action:button", "Keep printer configurations"),
- icon = "",
- description = "Keep the configuration of the cloud printer(s) synced with Cura which are not linked to your account.",
- button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
- removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered)
+ self._removed_printers_message.setText(message_text)
+ self._removed_printers_message.addAction("keep_printer_configurations_action",
+ name = self.I18N_CATALOG.i18nc("@action:button", "Keep printer configurations"),
+ icon = "",
+ description = "Keep the configuration of the cloud printer(s) synced with Cura which are not linked to your account.",
+ button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
+ self._removed_printers_message.addAction("remove_printers_action",
+ name = self.I18N_CATALOG.i18nc("@action:button", "Remove printers"),
+ icon = "",
+ description = "Remove the cloud printer(s) which are not linked to your account.",
+ button_style = Message.ActionButtonStyle.SECONDARY,
+ button_align = Message.ActionButtonAlignment.ALIGN_LEFT)
+ self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered)
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
@@ -314,7 +326,7 @@ class CloudOutputDeviceManager:
# Update the printer's metadata to mark it as not linked to the account
device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)
- removed_printers_message.show()
+ self._removed_printers_message.show()
def _onDiscoveredDeviceRemoved(self, device_id: str) -> None:
device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice]
@@ -402,7 +414,23 @@ class CloudOutputDeviceManager:
if container_cluster_id in self._remote_clusters.keys():
del self._remote_clusters[container_cluster_id]
- @staticmethod
- def _onRemovedPrintersMessageActionTriggered(removed_printers_message: Message, action: str) -> None:
+ def _onRemovedPrintersMessageActionTriggered(self, removed_printers_message: Message, action: str) -> None:
if action == "keep_printer_configurations_action":
removed_printers_message.hide()
+ elif action == "remove_printers_action":
+ machine_manager = CuraApplication.getInstance().getMachineManager()
+ remove_printers_ids = {self._um_cloud_printers[i].getId() for i in self.reported_device_ids}
+ all_ids = {m.getId() for m in CuraApplication.getInstance().getContainerRegistry().findContainerStacks(type = "machine")}
+
+ question_title = self.I18N_CATALOG.i18nc("@title:window", "Remove printers?")
+ question_content = self.I18N_CATALOG.i18nc("@label", "You are about to remove {} printer(s) from Cura. This action cannot be undone. \nAre you sure you want to continue?".format(len(remove_printers_ids)))
+ if remove_printers_ids == all_ids:
+ question_content = self.I18N_CATALOG.i18nc("@label", "You are about to remove all printers from Cura. This action cannot be undone. \nAre you sure you want to continue?")
+ result = QMessageBox.question(None, question_title, question_content)
+ if result == QMessageBox.No:
+ return
+
+ for machine_cloud_id in self.reported_device_ids:
+ machine_manager.setActiveMachine(self._um_cloud_printers[machine_cloud_id].getId())
+ machine_manager.removeMachine(self._um_cloud_printers[machine_cloud_id].getId())
+ removed_printers_message.hide()
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index ed2c6dc5fe..8ba651a5b0 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -84,6 +84,21 @@ UM.MainWindow
CuraApplication.purgeWindows()
}
+ Connections
+ {
+ // This connection is used when there is no ActiveMachine and the user is logged in
+ target: CuraApplication
+ onShowAddPrintersUncancellableDialog:
+ {
+ Cura.Actions.parent = backgroundItem
+
+ // Reuse the welcome dialog item to show "Add a printer" only.
+ welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel()
+ welcomeDialogItem.progressBarVisible = false
+ welcomeDialogItem.visible = true
+ }
+ }
+
Connections
{
target: CuraApplication
@@ -117,6 +132,15 @@ UM.MainWindow
welcomeDialogItem.progressBarVisible = false
welcomeDialogItem.visible = true
}
+
+ // Reuse the welcome dialog item to show the "Add printers" dialog. Triggered when there is no active
+ // machine and the user is logged in.
+ if (!Cura.MachineManager.activeMachine && Cura.API.account.isLoggedIn)
+ {
+ welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel()
+ welcomeDialogItem.progressBarVisible = false
+ welcomeDialogItem.visible = true
+ }
}
}
diff --git a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml
index 6fe9607274..670766204f 100644
--- a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml
+++ b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml
@@ -143,7 +143,7 @@ UM.Dialog
{
width: parent.width
height: childrenRect.height
- model: Cura.MachineManager.activeMachine.extruderList
+ model: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.extruderList : null
delegate: Column
{
height: childrenRect.height
diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
index 5d8414846b..cb498bcef0 100644
--- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
+++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
@@ -33,7 +33,7 @@ Cura.ExpandablePopup
}
contentPadding: UM.Theme.getSize("default_lining").width
- enabled: Cura.MachineManager.activeMachine.hasMaterials || Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates; //Only let it drop down if there is any configuration that you could change.
+ enabled: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasMaterials || Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates : false; //Only let it drop down if there is any configuration that you could change.
headerItem: Item
{
@@ -84,7 +84,7 @@ Cura.ExpandablePopup
{
id: variantLabel
- visible: Cura.MachineManager.activeMachine.hasVariants
+ visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasVariants : false
text: model.variant
elide: Text.ElideRight
@@ -114,7 +114,7 @@ Cura.ExpandablePopup
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
- visible: !Cura.MachineManager.activeMachine.hasMaterials && (Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates)
+ visible: Cura.MachineManager.activeMachine ? !Cura.MachineManager.activeMachine.hasMaterials && (Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates) : false
anchors
{
diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml
index 65f5bcce8c..010e2e77b0 100644
--- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml
+++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml
@@ -244,7 +244,7 @@ Item
Row
{
height: visible ? UM.Theme.getSize("print_setup_big_item").height : 0
- visible: Cura.MachineManager.activeMachine.hasMaterials
+ visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasMaterials : false
Label
{
@@ -305,7 +305,7 @@ Item
Row
{
height: visible ? UM.Theme.getSize("print_setup_big_item").height : 0
- visible: Cura.MachineManager.activeMachine.hasVariants
+ visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasVariants : false
Label
{
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml
index f227dddaf9..92f0024b23 100644
--- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml
@@ -130,7 +130,11 @@ Item
target: extruderModel
onModelChanged:
{
- supportExtruderCombobox.color = supportExtruderCombobox.model.getItem(supportExtruderCombobox.currentIndex).color
+ var maybeColor = supportExtruderCombobox.model.getItem(supportExtruderCombobox.currentIndex).color
+ if (maybeColor)
+ {
+ supportExtruderCombobox.color = maybeColor
+ }
}
}
onCurrentIndexChanged:
diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml
index a7c041630f..18b1a68b20 100644
--- a/resources/qml/PrinterSelector/MachineSelectorList.qml
+++ b/resources/qml/PrinterSelector/MachineSelectorList.qml
@@ -28,11 +28,11 @@ ListView
delegate: MachineSelectorButton
{
- text: model.name
+ text: model.name ? model.name : ""
width: listView.width
outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
- checked: Cura.MachineManager.activeMachine.id == model.id
+ checked: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.id == model.id : false
onClicked:
{