diff --git a/cura/MachineAction.py b/cura/MachineAction.py index 15d9ab1ca1..c38be5261f 100644 --- a/cura/MachineAction.py +++ b/cura/MachineAction.py @@ -94,7 +94,7 @@ class MachineAction(QObject, PluginObject): plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) if plugin_path is None: - Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId()) + Logger.error(f"Cannot create QML view: cannot find plugin path for plugin {self.getPluginId()}") return None path = os.path.join(plugin_path, self._qml_url) @@ -106,7 +106,7 @@ class MachineAction(QObject, PluginObject): def qmlPath(self) -> "QUrl": plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) if plugin_path is None: - Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId()) + Logger.error(f"Cannot create QML view: cannot find plugin path for plugin {self.getPluginId()}") return QUrl("") path = os.path.join(plugin_path, self._qml_url) return QUrl.fromLocalFile(path) diff --git a/cura/Machines/Models/MachineListModel.py b/cura/Machines/Models/MachineListModel.py index 323848697d..55db072180 100644 --- a/cura/Machines/Models/MachineListModel.py +++ b/cura/Machines/Models/MachineListModel.py @@ -5,7 +5,7 @@ # online cloud connected printers are represented within this ListModel. Additional information such as the number of # connected printers for each printer type is gathered. -from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtCore import Qt, QTimer, pyqtSlot, pyqtProperty, pyqtSignal from UM.Qt.ListModel import ListModel from UM.Settings.ContainerStack import ContainerStack @@ -24,11 +24,14 @@ class MachineListModel(ListModel): MetaDataRole = Qt.ItemDataRole.UserRole + 4 IsOnlineRole = Qt.ItemDataRole.UserRole + 5 MachineCountRole = Qt.ItemDataRole.UserRole + 6 - IsAbstractMachine = Qt.ItemDataRole.UserRole + 7 + IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7 + ComponentTypeRole = Qt.ItemDataRole.UserRole + 8 def __init__(self, parent=None) -> None: super().__init__(parent) + self._show_cloud_printers = False + self._catalog = i18nCatalog("cura") self.addRoleName(self.NameRole, "name") @@ -37,7 +40,8 @@ class MachineListModel(ListModel): self.addRoleName(self.MetaDataRole, "metadata") self.addRoleName(self.IsOnlineRole, "isOnline") self.addRoleName(self.MachineCountRole, "machineCount") - self.addRoleName(self.IsAbstractMachine, "isAbstractMachine") + self.addRoleName(self.IsAbstractMachineRole, "isAbstractMachine") + self.addRoleName(self.ComponentTypeRole, "componentType") self._change_timer = QTimer() self._change_timer.setInterval(200) @@ -50,6 +54,18 @@ class MachineListModel(ListModel): CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) self._updateDelayed() + showCloudPrintersChanged = pyqtSignal(bool) + + @pyqtProperty(bool, notify=showCloudPrintersChanged) + def showCloudPrinters(self) -> bool: + return self._show_cloud_printers + + @pyqtSlot(bool) + def setShowCloudPrinters(self, show_cloud_printers: bool) -> None: + self._show_cloud_printers = show_cloud_printers + self._updateDelayed() + self.showCloudPrintersChanged.emit(show_cloud_printers) + def _onContainerChanged(self, container) -> None: """Handler for container added/removed events from registry""" @@ -61,13 +77,12 @@ class MachineListModel(ListModel): self._change_timer.start() def _update(self) -> None: - self.setItems([]) # Clear items + self.clear() other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True") abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True) - for abstract_machine in abstract_machine_stacks: definition_id = abstract_machine.definition.getId() from cura.CuraApplication import CuraApplication @@ -77,14 +92,31 @@ class MachineListModel(ListModel): # Create a list item for abstract machine self.addItem(abstract_machine, len(online_machine_stacks)) other_machine_stacks.remove(abstract_machine) + if abstract_machine in online_machine_stacks: + online_machine_stacks.remove(abstract_machine) # Create list of machines that are children of the abstract machine for stack in online_machine_stacks: - self.addItem(stack) + if self._show_cloud_printers: + self.addItem(stack) # Remove this machine from the other stack list if stack in other_machine_stacks: other_machine_stacks.remove(stack) + if len(abstract_machine_stacks) > 0: + if self._show_cloud_printers: + self.appendItem({"componentType": "HIDE_BUTTON", + "isOnline": True, + "isAbstractMachine": False, + "machineCount": 0 + }) + else: + self.appendItem({"componentType": "SHOW_BUTTON", + "isOnline": True, + "isAbstractMachine": False, + "machineCount": 0 + }) + for stack in other_machine_stacks: self.addItem(stack) @@ -99,7 +131,9 @@ class MachineListModel(ListModel): for connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]: has_connection |= connection_type in container_stack.configuredConnectionTypes - self.appendItem({"name": container_stack.getName(), + self.appendItem({ + "componentType": "MACHINE", + "name": container_stack.getName(), "id": container_stack.getId(), "metadata": container_stack.getMetaData().copy(), "isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)) and has_connection, diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d5bcc2b7bc..dcedb79118 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1443,7 +1443,63 @@ "value": "0", "limit_to_extruder": "roofing_extruder_nr", "settable_per_mesh": true, - "enabled": "top_layers > 0" + "enabled": "top_layers > 0", + "children": { + "roofing_line_width": + { + "label": "Top Surface Skin Line Width", + "description": "Width of a single line of the areas at the top of the print.", + "unit": "mm", + "minimum_value": "0.001", + "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size", + "maximum_value_warning": "2 * machine_nozzle_size", + "default_value": 0.4, + "type": "float", + "value": "skin_line_width", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true, + "enabled": "roofing_layer_count > 0 and top_layers > 0" + }, + "roofing_pattern": + { + "label": "Top Surface Skin Pattern", + "description": "The pattern of the top most layers.", + "type": "enum", + "options": + { + "lines": "Lines", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "lines", + "value": "top_bottom_pattern", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true, + "enabled": "roofing_layer_count > 0 and top_layers > 0" + }, + "roofing_monotonic": + { + "label": "Monotonic Top Surface Order", + "description": "Print top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes flat surfaces look more consistent.", + "type": "bool", + "default_value": false, + "value": "skin_monotonic", + "enabled": "roofing_layer_count > 0 and top_layers > 0 and roofing_pattern != 'concentric'", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true + }, + "roofing_angles": + { + "label": "Top Surface Skin Line Directions", + "description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).", + "type": "[int]", + "default_value": "[ ]", + "value": "skin_angles", + "enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true + } + } }, "top_bottom_extruder_nr": { @@ -6646,60 +6702,6 @@ "default_value": "middle", "settable_per_mesh": true }, - "roofing_line_width": - { - "label": "Top Surface Skin Line Width", - "description": "Width of a single line of the areas at the top of the print.", - "unit": "mm", - "minimum_value": "0.001", - "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size", - "maximum_value_warning": "2 * machine_nozzle_size", - "default_value": 0.4, - "type": "float", - "value": "skin_line_width", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true, - "enabled": "roofing_layer_count > 0 and top_layers > 0" - }, - "roofing_pattern": - { - "label": "Top Surface Skin Pattern", - "description": "The pattern of the top most layers.", - "type": "enum", - "options": - { - "lines": "Lines", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default_value": "lines", - "value": "top_bottom_pattern", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true, - "enabled": "roofing_layer_count > 0 and top_layers > 0" - }, - "roofing_monotonic": - { - "label": "Monotonic Top Surface Order", - "description": "Print top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes flat surfaces look more consistent.", - "type": "bool", - "default_value": false, - "value": "skin_monotonic", - "enabled": "roofing_layer_count > 0 and top_layers > 0 and roofing_pattern != 'concentric'", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true - }, - "roofing_angles": - { - "label": "Top Surface Skin Line Directions", - "description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).", - "type": "[int]", - "default_value": "[ ]", - "value": "skin_angles", - "enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true - }, "infill_enable_travel_optimization": { "label": "Infill Travel Optimization", diff --git a/resources/qml/PrinterSelector/MachineListButton.qml b/resources/qml/PrinterSelector/MachineListButton.qml index 1ed9f98c46..70e7564aa9 100644 --- a/resources/qml/PrinterSelector/MachineListButton.qml +++ b/resources/qml/PrinterSelector/MachineListButton.qml @@ -7,81 +7,133 @@ import QtQuick.Controls 2.3 import UM 1.5 as UM import Cura 1.0 as Cura - -Button -{ - id: machineListButton - +Loader { + id: loader width: parent.width - height: UM.Theme.getSize("large_button").height - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - checkable: true - hoverEnabled: true + sourceComponent: { + switch (model.componentType) { + case "HIDE_BUTTON": + hideButtonComponent + break; + case "SHOW_BUTTON": + showButtonComponent + break; + case "MACHINE": + machineListButtonComponent + break; + default: + } + } + property var onClicked - contentItem: Item + Component { - width: machineListButton.width - machineListButton.leftPadding - machineListButton.rightPadding - height: UM.Theme.getSize("action_button").height - - UM.ColorImage + id: hideButtonComponent + Cura.TertiaryButton { - id: printerIcon - height: UM.Theme.getSize("medium_button").height - width: UM.Theme.getSize("medium_button").width - color: UM.Theme.getColor("machine_selector_printer_icon") - visible: model.isAbstractMachine || !model.isOnline - source: model.isAbstractMachine ? UM.Theme.getIcon("PrinterTriple", "medium") : UM.Theme.getIcon("Printer", "medium") - - anchors - { - left: parent.left - verticalCenter: parent.verticalCenter - } - } - - UM.Label - { - id: buttonText - anchors - { - left: printerIcon.right - right: printerCount.left - verticalCenter: parent.verticalCenter - leftMargin: UM.Theme.getSize("default_margin").width - } - text: machineListButton.text - font: model.isAbstractMachine ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium") - visible: text != "" - elide: Text.ElideRight - } - - Rectangle - { - id: printerCount - color: UM.Theme.getColor("background_2") - radius: height - width: height - anchors - { - right: parent.right - top: buttonText.top - bottom: buttonText.bottom - } - visible: model.isAbstractMachine ? model.isAbstractMachine : false - - UM.Label - { - text: model.machineCount ? model.machineCount : "" - anchors.centerIn: parent - font: UM.Theme.getFont("default_bold") - } + text: catalog.i18nc("@label", "Hide all connected printers") + height: UM.Theme.getSize("large_button").height + onClicked: if (loader.onClicked) loader.onClicked() + iconSource: UM.Theme.getIcon("ChevronSingleUp") + width: parent.width } } - background: Rectangle + Component { - id: backgroundRect - color: machineListButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + id: showButtonComponent + Cura.TertiaryButton + { + text: catalog.i18nc("@label", "Show all connected printers") + height: UM.Theme.getSize("large_button").height + onClicked: if (loader.onClicked) loader.onClicked() + iconSource: UM.Theme.getIcon("ChevronSingleDown") + width: parent.width + } + } + + Component + { + id: machineListButtonComponent + + Button + { + id: machineListButton + + onClicked: if (loader.onClicked) loader.onClicked() + + width: parent.width + height: UM.Theme.getSize("large_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + checkable: true + hoverEnabled: true + + contentItem: Item + { + width: machineListButton.width - machineListButton.leftPadding - machineListButton.rightPadding + height: UM.Theme.getSize("action_button").height + + UM.ColorImage + { + id: printerIcon + height: UM.Theme.getSize("medium_button").height + width: UM.Theme.getSize("medium_button").width + color: UM.Theme.getColor("machine_selector_printer_icon") + visible: model.isAbstractMachine || !model.isOnline + source: model.isAbstractMachine ? UM.Theme.getIcon("PrinterTriple", "medium") : UM.Theme.getIcon("Printer", "medium") + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + } + } + + UM.Label + { + id: buttonText + anchors + { + left: printerIcon.right + right: printerCount.left + verticalCenter: parent.verticalCenter + leftMargin: UM.Theme.getSize("default_margin").width + } + text: model.name ? model.name : "" + font: model.isAbstractMachine ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium") + visible: text != "" + elide: Text.ElideRight + } + + Rectangle + { + id: printerCount + color: UM.Theme.getColor("background_2") + radius: height + width: height + anchors + { + right: parent.right + top: buttonText.top + bottom: buttonText.bottom + } + visible: model.isAbstractMachine ? model.isAbstractMachine : false + + UM.Label + { + text: model.machineCount ? model.machineCount : "" + anchors.centerIn: parent + font: UM.Theme.getFont("default_bold") + } + } + } + + background: Rectangle + { + id: backgroundRect + color: machineListButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + } + } } } diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 06c2fdb40c..a328ae69d9 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -22,8 +22,8 @@ ListView section.delegate: UM.Label { text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Other printers") - width: parent.width - scrollBar.width height: UM.Theme.getSize("action_button").height + width: parent.width - scrollBar.width leftPadding: UM.Theme.getSize("default_margin").width font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text_medium") @@ -31,13 +31,23 @@ ListView delegate: MachineListButton { - text: model.name ? model.name : "" width: listView.width - scrollBar.width - onClicked: + onClicked: function() { - toggleContent() - Cura.MachineManager.setActiveMachine(model.id) + switch (model.componentType) { + case "HIDE_BUTTON": + listView.model.setShowCloudPrinters(false); + break; + case "SHOW_BUTTON": + listView.model.setShowCloudPrinters(true); + break; + case "MACHINE": + toggleContent() + Cura.MachineManager.setActiveMachine(model.id) + break; + default: + } } } }