diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f8b081bdcc..1192455312 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -114,6 +114,8 @@ from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions from cura.ObjectsModel import ObjectsModel +from cura.Machines.Models.DiscoveredPrintersModel import DiscoveredPrintersModel + from cura.PrinterOutputDevice import PrinterOutputDevice from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage @@ -210,6 +212,8 @@ class CuraApplication(QtApplication): self._cura_scene_controller = None self._machine_error_checker = None + self._discovered_printer_model = DiscoveredPrintersModel(self) + self._welcome_pages_model = WelcomePagesModel(self) self._quality_profile_drop_down_menu_model = None @@ -846,6 +850,10 @@ class CuraApplication(QtApplication): # Hide the splash screen self.closeSplash() + @pyqtSlot(result = QObject) + def getDiscoveredPrintersModel(self, *args) -> "DiscoveredPrintersModel": + return self._discovered_printer_model + @pyqtSlot(result = QObject) def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel: return self._setting_visibility_presets_model @@ -1003,6 +1011,8 @@ class CuraApplication(QtApplication): qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel") + qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel") + qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel) qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 68e894642d..aff79218c8 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -537,6 +537,16 @@ class MaterialManager(QObject): return nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list + # Sort all nodes with respect to the container ID lengths in the ascending order so the base material container + # will be the first one to be removed. We need to do this to ensure that all containers get loaded & deleted. + nodes_to_remove = sorted(nodes_to_remove, key = lambda x: len(x.getMetaDataEntry("id", ""))) + # Try to load all containers first. If there is any faulty ones, they will be put into the faulty container + # list, so removeContainer() can ignore those ones. + for node in nodes_to_remove: + container_id = node.getMetaDataEntry("id", "") + results = self._container_registry.findContainers(id = container_id) + if not results: + self._container_registry.addWrongContainerId(container_id) for node in nodes_to_remove: self._container_registry.removeContainer(node.getMetaDataEntry("id", "")) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py new file mode 100644 index 0000000000..0dd07eb9ce --- /dev/null +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -0,0 +1,128 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Callable, List, Optional, TYPE_CHECKING + +from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject + +from UM.i18n import i18nCatalog +from UM.Logger import Logger + +if TYPE_CHECKING: + from PyQt5.QtCore import QObject + + +catalog = i18nCatalog("cura") + + +class DiscoveredPrinter(QObject): + + def __init__(self, ip_address: str, key: str, name: str, create_callback: Callable[[str], None], machine_type: str, + device, parent = None) -> None: + super().__init__(parent) + + self._ip_address = ip_address + self._key = key + self._name = name + self.create_callback = create_callback + self._machine_type = machine_type + self._device = device + + nameChanged = pyqtSignal() + + def getKey(self) -> str: + return self._key + + @pyqtProperty(str, notify = nameChanged) + def name(self) -> str: + return self._name + + def setName(self, name: str) -> None: + if self._name != name: + self._name = name + self.nameChanged.emit() + + machineTypeChanged = pyqtSignal() + + @pyqtProperty(str, notify = machineTypeChanged) + def machine_type(self) -> str: + return self._machine_type + + def setMachineType(self, machine_type: str) -> None: + if self._machine_type != machine_type: + self._machine_type = machine_type + self.machineTypeChanged.emit() + + # Human readable machine type string + @pyqtProperty(str, notify = machineTypeChanged) + def readable_machine_type(self) -> str: + from cura.CuraApplication import CuraApplication + readable_type = CuraApplication.getInstance().getMachineManager().getMachineTypeNameFromId(self._machine_type) + if not readable_type: + readable_type = catalog.i18nc("@label", "Unknown") + return readable_type + + @pyqtProperty(bool, notify = machineTypeChanged) + def is_unknown_machine_type(self) -> bool: + return self.readable_machine_type.lower() == "unknown" + + @pyqtProperty(QObject, constant = True) + def device(self): + return self._device + + +# +# Discovered printers are all the printers that were found on the network, which provide a more convenient way +# to add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then +# add that printer to Cura as the active one). +# +class DiscoveredPrintersModel(QObject): + + def __init__(self, parent: Optional["QObject"]) -> None: + super().__init__(parent) + + self._discovered_printer_dict = dict() + + discoveredPrintersChanged = pyqtSignal() + + @pyqtProperty(list, notify = discoveredPrintersChanged) + def discovered_printers(self) -> "List[DiscoveredPrinter]": + item_list = list(x for x in self._discovered_printer_dict.values()) + item_list.sort(key = lambda x: x.name) + return item_list + + def addDiscoveredPrinter(self, ip_address: str, key: str, name: str, create_callback: Callable[[str], None], + machine_type: str, device) -> None: + if ip_address in self._discovered_printer_dict: + Logger.log("e", "Printer with ip [%s] has already been added", ip_address) + return + + discovered_printer = DiscoveredPrinter(ip_address, key, name, create_callback, machine_type, device, parent = self) + self._discovered_printer_dict[ip_address] = discovered_printer + self.discoveredPrintersChanged.emit() + + def updateDiscoveredPrinter(self, ip_address: str, + name: Optional[str] = None, + machine_type: Optional[str] = None) -> None: + if ip_address not in self._discovered_printer_dict: + Logger.log("e", "Printer with ip [%s] is not known", ip_address) + return + + item = self._discovered_printer_dict[ip_address] + + if name is not None: + item.setName(name) + if machine_type is not None: + item.setMachineType(machine_type) + + def removeDiscoveredPrinter(self, ip_address: str) -> None: + if ip_address not in self._discovered_printer_dict: + Logger.log("i", "Key [%s] does not exist in the discovered printers list.", ip_address) + return + + del self._discovered_printer_dict[ip_address] + self.discoveredPrintersChanged.emit() + + @pyqtSlot("QVariant") + def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None: + discovered_printer.create_callback(discovered_printer.getKey()) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index f8dccb4ba6..b6b7b31936 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -338,7 +338,7 @@ class ExtruderManager(QObject): extruder_train.setNextStack(global_stack) extruders_changed = True - self._fixSingleExtrusionMachineExtruderDefinition(global_stack) + self.fixSingleExtrusionMachineExtruderDefinition(global_stack) if extruders_changed: self.extrudersChanged.emit(global_stack_id) self.setActiveExtruderIndex(0) @@ -346,7 +346,7 @@ class ExtruderManager(QObject): # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing # "fdmextruder". We need to check a machine here so its extruder definition is correct according to this. - def _fixSingleExtrusionMachineExtruderDefinition(self, global_stack: "GlobalStack") -> None: + def fixSingleExtrusionMachineExtruderDefinition(self, global_stack: "GlobalStack") -> None: container_registry = ContainerRegistry.getInstance() expected_extruder_definition_0_id = global_stack.getMetaDataEntry("machine_extruder_trains")["0"] extruder_stack_0 = global_stack.extruders.get("0") diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index d19932a7a0..c910b67d0c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -4,7 +4,7 @@ import time import re import unicodedata -from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast, NamedTuple, Callable +from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -49,8 +49,6 @@ if TYPE_CHECKING: from cura.Machines.QualityChangesGroup import QualityChangesGroup from cura.Machines.QualityGroup import QualityGroup -DiscoveredPrinter = NamedTuple("DiscoveredPrinter", [("key", str), ("name", str), ("create_callback", Callable[[str], None]), ("machine_type", "str")]) - class MachineManager(QObject): def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None: @@ -136,9 +134,6 @@ class MachineManager(QObject): self.globalContainerChanged.connect(self.printerConnectedStatusChanged) self.outputDevicesChanged.connect(self.printerConnectedStatusChanged) - # This will contain all discovered network printers - self._discovered_printers = {} # type: Dict[str, DiscoveredPrinter] - activeQualityGroupChanged = pyqtSignal() activeQualityChangesGroupChanged = pyqtSignal() @@ -178,30 +173,6 @@ class MachineManager(QObject): self.outputDevicesChanged.emit() - # Discovered printers are all the printers that were found on the network, which provide a more convenient way - # to add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then - # add that printer to Cura as the active one). - def addDiscoveredPrinter(self, key: str, name: str, create_callback: Callable[[str], None], machine_type: str) -> None: - if key not in self._discovered_printers: - self._discovered_printers[key] = DiscoveredPrinter(key, name, create_callback, machine_type) - self.discoveredPrintersChanged.emit() - else: - Logger.log("e", "Printer with the key %s was already in the discovered printer list", key) - - def removeDiscoveredPrinter(self, key: str) -> None: - if key in self._discovered_printers: - del self._discovered_printers[key] - self.discoveredPrintersChanged.emit() - - @pyqtProperty("QVariantList", notify = discoveredPrintersChanged) - def discoveredPrinters(self): - return list(self._discovered_printers.values()) - - @pyqtSlot(str) - def addMachineFromDiscoveredPrinter(self, key: str) -> None: - if key in self._discovered_printers: - self._discovered_printers[key].create_callback(key) - @pyqtProperty(QObject, notify = currentConfigurationChanged) def currentConfiguration(self) -> ConfigurationModel: return self._current_printer_configuration @@ -386,7 +357,7 @@ class MachineManager(QObject): # Make sure that the default machine actions for this machine have been added self._application.getMachineActionManager().addDefaultMachineActions(global_stack) - ExtruderManager.getInstance()._fixSingleExtrusionMachineExtruderDefinition(global_stack) + ExtruderManager.getInstance().fixSingleExtrusionMachineExtruderDefinition(global_stack) if not global_stack.isValid(): # Mark global stack as invalid ConfigurationErrorMessage.getInstance().addFaultyContainers(global_stack.getId()) @@ -1686,3 +1657,47 @@ class MachineManager(QObject): abbr_machine += stripped_word return abbr_machine + + def getMachineTypeNameFromId(self, machine_type_id: str) -> str: + machine_type_name = "" + results = self._container_registry.findDefinitionContainersMetadata(id = machine_type_id) + if results: + machine_type_name = results[0]["name"] + return machine_type_name + + @pyqtSlot(QObject) + def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None: + if not printer_device: + return + + Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) + + global_stack = self._global_container_stack + if not global_stack: + return + + meta_data = global_stack.getMetaData() + + 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._container_registry.findContainerStacks(type = "machine", **metadata_filter) + + for container in containers: + container.setMetaDataEntry("um_network_key", printer_device.key) + + # Delete old authentication data. + Logger.log("d", "Removing old authentication id %s for device %s", + global_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_stack.setMetaDataEntry("um_network_key", printer_device.key) + global_stack.addConfiguredConnectionType(printer_device.connectionType.value) diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index afcbee5f01..8aaea86bc9 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -3,7 +3,7 @@ import os from typing import TYPE_CHECKING, Optional -from PyQt5.QtCore import QUrl, Qt +from PyQt5.QtCore import QUrl, Qt, pyqtSlot from UM.Qt.ListModel import ListModel from UM.Resources import Resources @@ -12,6 +12,7 @@ from logging import Logger if TYPE_CHECKING: from PyQt5.QtCore import QObject + class WelcomePagesModel(ListModel): IdRole = Qt.UserRole + 1 # Page ID @@ -52,6 +53,11 @@ class WelcomePagesModel(ListModel): os.path.join("WelcomePages", "DataCollectionsContent.qml"))), }) + self._pages.append({"id": "add_printer_by_selection", + "page_url": QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, + os.path.join("WelcomePages", + "AddPrinterBySelectionContent.qml"))), + }) self._pages.append({"id": "add_printer_by_ip", "page_url": QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, os.path.join("WelcomePages", "AddPrinterByIpContent.qml")))}) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index bf190f7e39..38652361a5 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -26,6 +26,7 @@ from UM.Preferences import Preferences from cura.Machines.VariantType import VariantType from cura.Settings.CuraStackBuilder import CuraStackBuilder +from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.GlobalStack import GlobalStack from cura.Settings.CuraContainerStack import _ContainerIndexes @@ -781,6 +782,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not quality_changes_info.extruder_info_dict: container_info = ContainerInfo(None, None, None) quality_changes_info.extruder_info_dict["0"] = container_info + # If the global stack we're "targeting" has never been active, but was updated from Cura 3.4, + # it might not have it's extruders set properly. + if not global_stack.extruders: + ExtruderManager.getInstance().fixSingleExtrusionMachineExtruderDefinition(global_stack) extruder_stack = global_stack.extruders["0"] container = quality_manager._createQualityChanges(quality_changes_quality_type, quality_changes_name, diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index d3882a1209..ef97364118 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -323,9 +323,10 @@ class StartSliceJob(Job): value = stack.getProperty(key, "value") result[key] = value Job.yieldThread() - + result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings. result["print_temperature"] = result["material_print_temperature"] + result["travel_speed"] = result["speed_travel"] result["time"] = time.strftime("%H:%M:%S") #Some extra settings. result["date"] = time.strftime("%d-%m-%Y") result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))] diff --git a/plugins/PostProcessingPlugin/scripts/InsertAtLayerChange.py b/plugins/PostProcessingPlugin/scripts/InsertAtLayerChange.py new file mode 100644 index 0000000000..32fcc85fe1 --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/InsertAtLayerChange.py @@ -0,0 +1,49 @@ +# Created by Wayne Porter + +from ..Script import Script + +class InsertAtLayerChange(Script): + def __init__(self): + super().__init__() + + def getSettingDataString(self): + return """{ + "name": "Insert at layer change", + "key": "InsertAtLayerChange", + "metadata": {}, + "version": 2, + "settings": + { + "insert_location": + { + "label": "When to insert", + "description": "Whether to insert code before or after layer change.", + "type": "enum", + "options": {"before": "Before", "after": "After"}, + "default_value": "before" + }, + "gcode_to_add": + { + "label": "GCODE to insert.", + "description": "GCODE to add before or after layer change.", + "type": "str", + "default_value": "" + } + } + }""" + + def execute(self, data): + gcode_to_add = self.getSettingValueByKey("gcode_to_add") + "\n" + for layer in data: + # Check that a layer is being printed + lines = layer.split("\n") + if ";LAYER:" in lines[0]: + index = data.index(layer) + if self.getSettingValueByKey("insert_location") == "before": + layer = gcode_to_add + layer + else: + layer = layer + gcode_to_add + + data[index] = layer + + return data diff --git a/plugins/PostProcessingPlugin/scripts/TimeLapse.py b/plugins/PostProcessingPlugin/scripts/TimeLapse.py new file mode 100644 index 0000000000..76d9143459 --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/TimeLapse.py @@ -0,0 +1,94 @@ +# Created by Wayne Porter + +from ..Script import Script + +class TimeLapse(Script): + def __init__(self): + super().__init__() + + def getSettingDataString(self): + return """{ + "name": "Time Lapse", + "key": "TimeLapse", + "metadata": {}, + "version": 2, + "settings": + { + "trigger_command": + { + "label": "Trigger camera command", + "description": "Gcode command used to trigger camera.", + "type": "str", + "default_value": "M240" + }, + "pause_length": + { + "label": "Pause length", + "description": "How long to wait (in ms) after camera was triggered.", + "type": "int", + "default_value": 700, + "minimum_value": 0, + "unit": "ms" + }, + "park_print_head": + { + "label": "Park Print Head", + "description": "Park the print head out of the way. Assumes absolute positioning.", + "type": "bool", + "default_value": true + }, + "head_park_x": + { + "label": "Park Print Head X", + "description": "What X location does the head move to for photo.", + "unit": "mm", + "type": "float", + "default_value": 0, + "enabled": "park_print_head" + }, + "head_park_y": + { + "label": "Park Print Head Y", + "description": "What Y location does the head move to for photo.", + "unit": "mm", + "type": "float", + "default_value": 190, + "enabled": "park_print_head" + }, + "park_feed_rate": + { + "label": "Park Feed Rate", + "description": "How fast does the head move to the park coordinates.", + "unit": "mm/s", + "type": "float", + "default_value": 9000, + "enabled": "park_print_head" + } + } + }""" + + def execute(self, data): + feed_rate = self.getSettingValueByKey("park_feed_rate") + park_print_head = self.getSettingValueByKey("park_print_head") + x_park = self.getSettingValueByKey("head_park_x") + y_park = self.getSettingValueByKey("head_park_y") + trigger_command = self.getSettingValueByKey("trigger_command") + pause_length = self.getSettingValueByKey("pause_length") + gcode_to_append = ";TimeLapse Begin\n" + + if park_print_head: + gcode_to_append += self.putValue(G = 1, F = feed_rate, X = x_park, Y = y_park) + ";Park print head\n" + gcode_to_append += self.putValue(M = 400) + ";Wait for moves to finish\n" + gcode_to_append += trigger_command + ";Snap Photo\n" + gcode_to_append += self.putValue(G = 4, P = pause_length) + ";Wait for camera\n" + gcode_to_append += ";TimeLapse End\n" + for layer in data: + # Check that a layer is being printed + lines = layer.split("\n") + if ";LAYER:" in lines[0]: + index = data.index(layer) + layer += gcode_to_append + + data[index] = layer + + return data diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 7b5add276a..d052d925d2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -12,8 +12,10 @@ from UM.Backend.Backend import BackendState from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode + from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -82,8 +84,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._account = api_client.account # We use the Cura Connect monitor tab to get most functionality right away. - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../resources/qml/MonitorStage.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) # Trigger the printersChanged signal when the private signal is triggered. self.printersChanged.connect(self._clusterPrintersChanged) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index c1a6362455..1d97127637 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -10,13 +10,13 @@ import os from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.WriteFileJob import WriteFileJob # To call the file writer asynchronously. -from UM.Logger import Logger -from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog -from UM.Qt.Duration import Duration, DurationFormat - +from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode # For typing. +from UM.Settings.ContainerRegistry import ContainerRegistry from cura.CuraApplication import CuraApplication from cura.PrinterOutput.ConfigurationModel import ConfigurationModel @@ -65,7 +65,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorStage.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) # Trigger the printersChanged signal when the private signal is triggered self.printersChanged.connect(self._clusterPrintersChanged) @@ -126,8 +130,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _spawnPrinterSelectionDialog(self): if self._printer_selection_dialog is None: - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/PrintWindow.qml") - self._printer_selection_dialog = self._application.createQmlComponent(path, {"OutputDevice": self}) + if PluginRegistry.getInstance() is not None: + path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "PrintWindow.qml" + ) + self._printer_selection_dialog = self._application.createQmlComponent(path, {"OutputDevice": self}) if self._printer_selection_dialog is not None: self._printer_selection_dialog.show() @@ -624,6 +632,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): printer.updateName(data["friendly_name"]) printer.updateKey(data["uuid"]) printer.updateType(data["machine_variant"]) + self._application.getDiscoveredPrintersModel().updateDiscoveredPrinter(data["ip_address"], + name = data["friendly_name"], + machine_type = data["machine_variant"]) # Do not store the build plate information that comes from connect if the current printer has not build plate information if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False): diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index ecc89b3948..51c234650b 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -118,7 +118,7 @@ class DiscoverUM3Action(MachineAction): if self._network_plugin: # Ensure that the connection states are refreshed. - self._network_plugin.reCheckConnections() + self._network_plugin.refreshConnections() # 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. @@ -160,7 +160,7 @@ class DiscoverUM3Action(MachineAction): if self._network_plugin: # Ensure that the connection states are refreshed. - self._network_plugin.reCheckConnections() + self._network_plugin.refreshConnections() @pyqtSlot(result = str) def getStoredKey(self) -> str: diff --git a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py index 3ce0460d6b..2c7c33d382 100644 --- a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py @@ -1,7 +1,5 @@ from typing import List, Optional -from UM.FileHandler.FileHandler import FileHandler -from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -12,10 +10,13 @@ from cura.PrinterOutputDevice import ConnectionType from cura.Settings.ContainerManager import ContainerManager from cura.Settings.ExtruderManager import ExtruderManager -from UM.Logger import Logger -from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.FileHandler.FileHandler import FileHandler from UM.i18n import i18nCatalog +from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from UM.Scene.SceneNode import SceneNode +from UM.Settings.ContainerRegistry import ContainerRegistry from PyQt5.QtNetwork import QNetworkRequest from PyQt5.QtCore import QTimer, QUrl @@ -76,7 +77,11 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): self.setIconName("print") - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorItem.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) self._output_controller = LegacyUM3PrinterOutputController(self) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 0b0b4a5bf0..c99b12de55 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -16,11 +16,14 @@ from cura.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.OutputDevicePlugin import OutputDevicePlugin +from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal, signalemitter from UM.Version import Version -from UM.Message import Message -from UM.i18n import i18nCatalog from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager @@ -54,7 +57,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) - self._application.globalContainerStackChanged.connect(self.reCheckConnections) + self._application.globalContainerStackChanged.connect(self.refreshConnections) self._discovered_devices = {} @@ -141,7 +144,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.addManualDevice(address) self.resetLastManualDevice() - def reCheckConnections(self): + def refreshConnections(self): active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return @@ -225,7 +228,22 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): def _createMachineFromDiscoveredPrinter(self, key: str) -> None: # TODO: This needs to be implemented. It's supposed to create a machine given a unique key as already discovered # by this plugin. - pass + discovered_device = self._discovered_devices.get(key) + if discovered_device is None: + Logger.log("e", "Could not find discovered device with key [%s]", key) + return + + group_name = discovered_device.getProperty("name") + machine_type_id = discovered_device.getProperty("printer_type") + + Logger.log("i", "Creating machine from network device with key = [%s], group name = [%s], printer type = [%s]", + key, group_name, machine_type_id) + + self._application.getMachineManager().addMachine(machine_type_id, group_name) + # connect the new machine to that network printer + self._application.getMachineManager().associateActiveMachineWithPrinterDevice(discovered_device) + # ensure that the connection states are refreshed. + self.refreshConnections() def _checkManualDevice(self, address): # Check if a UM3 family device exists at this address. @@ -315,7 +333,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): except TypeError: # Disconnect already happened. pass - self._application.getMachineManager().removeDiscoveredPrinter(device.getId()) + self._application.getDiscoveredPrintersModel().removeDiscoveredPrinter(device.address) self.discoveredDevicesChanged.emit() def _onAddDevice(self, name, address, properties): @@ -340,7 +358,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties) else: device = LegacyUM3OutputDevice.LegacyUM3OutputDevice(name, address, properties) - self._application.getMachineManager().addDiscoveredPrinter(device.getId(), name, self._createMachineFromDiscoveredPrinter, properties[b"printer_type"].decode("utf-8")) + self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(address, device.getId(), name, self._createMachineFromDiscoveredPrinter, properties[b"printer_type"].decode("utf-8"), device) self._discovered_devices[device.getId()] = device self.discoveredDevicesChanged.emit() @@ -477,8 +495,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._start_cloud_flow_message = Message( text = i18n_catalog.i18nc("@info:status", "Send and monitor print jobs from anywhere using your Ultimaker account."), lifetime = 0, - image_source = QUrl.fromLocalFile(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", - "resources", "svg", "cloud-flow-start.svg")), + image_source = QUrl.fromLocalFile(os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "svg", "cloud-flow-start.svg" + )), image_caption = i18n_catalog.i18nc("@info:status", "Connect to Ultimaker Cloud"), option_text = i18n_catalog.i18nc("@action", "Don't ask me again for this printer."), option_state = False @@ -499,8 +519,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cloud_flow_complete_message = Message( text = i18n_catalog.i18nc("@info:status", "You can now send and monitor print jobs from anywhere using your Ultimaker account."), lifetime = 30, - image_source = QUrl.fromLocalFile(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", - "resources", "svg", "cloud-flow-completed.svg")), + image_source = QUrl.fromLocalFile(os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "svg", "cloud-flow-completed.svg" + )), image_caption = i18n_catalog.i18nc("@info:status", "Connected!") ) # Don't show the review connection link if we're not on the local network diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 752773723e..3375ac27e7 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -243,7 +243,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._last_temperature_request = time() if re.search(b"[B|T\d*]: ?\d+\.?\d*", line): # Temperature message. 'T:' for extruder and 'B:' for bed - extruder_temperature_matches = re.findall(b"T(\d*): ?(\d+\.?\d*) ?\/?(\d+\.?\d*)?", line) + extruder_temperature_matches = re.findall(b"T(\d*): ?(\d+\.?\d*)\s*\/?(\d+\.?\d*)?", line) # Update all temperature values matched_extruder_nrs = [] for match in extruder_temperature_matches: @@ -265,7 +265,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if match[2]: extruder.updateTargetHotendTemperature(float(match[2])) - bed_temperature_matches = re.findall(b"B: ?(\d+\.?\d*) ?\/?(\d+\.?\d*) ?", line) + bed_temperature_matches = re.findall(b"B: ?(\d+\.?\d*)\s*\/?(\d+\.?\d*)?", line) if bed_temperature_matches: match = bed_temperature_matches[0] if match[0]: diff --git a/resources/definitions/anycubic_chiron.def.json b/resources/definitions/anycubic_chiron.def.json new file mode 100644 index 0000000000..83c2056d76 --- /dev/null +++ b/resources/definitions/anycubic_chiron.def.json @@ -0,0 +1,80 @@ +{ + "version": 2, + "name": "Anycubic Chiron", + "inherits": "fdmprinter", + "metadata": + { + "visible": true, + "author": "Patrick Glatt", + "manufacturer": "Anycubic", + "category": "Other", + "file_formats": "text/x-gcode", + "icon": "icon_ultimaker2", + "platform": "anycubic_chiron_platform.obj", + "platform_texture": "anycubic-chiron.png", + "has_materials": true, + "preferred_material": "generic_pla", + "has_machine_quality": true, + "quality_definition": "anycubic_chiron", + "preferred_quality_type": "normal", + "machine_extruder_trains": + { + "0": "anycubic_chiron_extruder_0" + }, + "firmware_file": "MarlinChiron.hex" + }, + + "overrides": + { + "machine_name": + { + "default_value": "Anycubic Chiron" + }, + "machine_heated_bed": + { + "default_value": true + }, + "machine_width": + { + "default_value": 400 + }, + "machine_height": + { + "default_value": 450 + }, + "machine_depth": + { + "default_value": 400 + }, + "machine_center_is_zero": + { + "default_value": false + }, + "gantry_height": + { + "default_value": 35 + }, + "machine_head_with_fans_polygon": + { + "default_value": + [ + [-45, 50], + [-45, -45], + [45, 50], + [45, -45] + ] + }, + "machine_gcode_flavor": + { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": + { + "default_value": "M107 ;Start with the fan off\nG21 ;Set units to millimeters\nG91 ;Change to relative positioning mode for retract filament and nozzle lifting\nG1 F200 E-3 ;Retract 3mm filament for a clean start\nG92 E0 ;Zero the extruded length\nG1 F1000 Z5 ;Lift the nozzle 5mm before homing axes\nG90 ;Absolute positioning\nM82 ;Set extruder to absolute mode too\nG28 X0 Y0 ;First move X/Y to min endstops\nG28 Z0 ;Then move Z to min endstops\nG1 F1000 Z15 ;After homing lift the nozzle 15mm before start printing\n" + }, + "machine_end_gcode": + { + "default_value": "G91 ;Change to relative positioning mode for filament retraction and nozzle lifting\nG1 F200 E-4;Retract the filament a bit before lifting the nozzle\nG1 F1000 Z5;Lift nozzle 5mm\nG90 ;Change to absolute positioning mode to prepare for part rermoval\nG1 X0 Y400 ;Move the print to max y pos for part rermoval\nM104 S0 ; Turn off hotend\nM106 S0 ; Turn off cooling fan\nM140 S0 ; Turn off bed\nM84 ; Disable motors\n" + } + } +} diff --git a/resources/definitions/deltacomb.def.json b/resources/definitions/deltacomb.def.json index 8fec0f8950..026dfca9ed 100755 --- a/resources/definitions/deltacomb.def.json +++ b/resources/definitions/deltacomb.def.json @@ -1,61 +1,68 @@ { - "version": 2, - "name": "Deltacomb 3D", - "inherits": "fdmprinter", + "version": 2, + "name": "Deltacomb 3D", + "inherits": "fdmprinter", + "metadata": { - "author": "Gabriele Rossetti", - "visible": true, - "manufacturer": "Deltacomb 3D", - "category": "Other", - "file_formats": "text/x-gcode", - "platform": "deltacomb.stl", - "has_machine_quality": true, - "machine_extruder_trains": - { - "0": "deltacomb_extruder_0" - } + "author": "Gabriele Rossetti", + "visible": true, + "manufacturer": "Deltacomb 3D", + "category": "Other", + "file_formats": "text/x-gcode", + "icon": "icon_ultimaker2", + "platform": "deltacomb.stl", + "has_machine_quality": true, + "has_materials": true, + "has_machine_materials": false, + "has_variants": true, + "variants_name": "Head", + "preferred_variant_name": "E3D 0.40mm", + "preferred_material": "generic_pla", + "preferred_quality_type": "normal", + "machine_extruder_trains": { "0": "deltacomb_extruder_0", "1": "deltacomb_extruder_1" } }, "overrides": { - "machine_heated_bed": { "default_value": true }, - "machine_width": { "default_value": 190 }, - "machine_height": { "default_value": 250 }, - "machine_depth": { "default_value": 190 }, - "machine_center_is_zero": { "default_value": true }, - "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home all axes (max endstops)\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\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\nG28 ;Home all axes (max endstops)\nM84 ;steppers off\nG90 ;absolute positioning" }, - "machine_shape": { "default_value": "elliptic" }, - "retraction_hop_enabled": { "default_value": true }, - "retraction_amount" : { "default_value": 3.5 }, - "retraction_speed" : { "default_value": 50 }, - "material_final_print_temperature": { "value": "material_print_temperature - 5" }, - "material_initial_print_temperature": { "value": "material_print_temperature" }, - "material_print_temperature_layer_0": { "value": "material_print_temperature + 5" }, - "material_diameter": { "default_value": 1.75 }, - "travel_avoid_distance": { "default_value": 1, "value": "1" }, - "speed_print" : { "default_value": 70 }, - "speed_travel": { "value": "150.0" }, - "speed_infill": { "value": "round(speed_print * 1.05, 0)" }, - "speed_topbottom": { "value": "round(speed_print * 0.95, 0)" }, - "speed_wall": { "value": "speed_print" }, - "speed_wall_0": { "value": "20" }, - "speed_wall_x": { "value": "speed_wall" }, - "speed_layer_0": { "value": "min(round(speed_print * 0.75, 0), 45.0)" }, - "speed_travel_layer_0": { "value": "round(speed_travel * 0.7, 0)" }, - "skirt_brim_speed": { "value": "speed_layer_0" }, - "skirt_line_count": { "default_value": 3 }, - "skirt_brim_minimal_length": { "default_value": 150 }, - "infill_sparse_density": { "default_value": 90 }, - "gradual_infill_steps": { "default_value": 2 }, - "infill_before_walls" : { "default_value": false }, - "top_bottom_thickness": { "default_value": 0.6 }, - "support_z_distance": { "value": "layer_height * 2" }, - "support_bottom_distance": { "value": "layer_height" }, - "support_use_towers" : { "default_value": false }, - "jerk_wall_0" : { "value": "30" }, - "jerk_travel" : { "default_value": 20 }, - "acceleration_travel" : { "value": 10000 }, - "machine_max_feedrate_z" : { "default_value": 150 } + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_width": { "default_value": 190 }, + "machine_height": { "default_value": 250 }, + "machine_depth": { "default_value": 190 }, + "machine_center_is_zero": { "default_value": true }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home all axes (max endstops)\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F9000\n;Put printing message on LCD screen\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\nG28 ;Home all axes (max endstops)\nM84 ;steppers off\nG90 ;absolute positioning" }, + "machine_shape": { "default_value": "elliptic" }, + "retraction_hop_enabled": { "default_value": true }, + "retraction_hop": { "default_value": 1 }, + "retraction_amount" : { "default_value": 3.5 }, + "retraction_speed" : { "default_value": 30 }, + "retraction_combing" : { "default_value": "noskin" }, + "travel_avoid_distance": { "default_value": 1, "value": "1" }, + "speed_print" : { "default_value": 80 }, + "speed_infill": { "value": "round(speed_print * 1.05, 0)" }, + "speed_topbottom": { "value": "round(speed_print * 0.95, 0)" }, + "speed_wall": { "value": "speed_print" }, + "speed_wall_0": { "value": "30" }, + "speed_wall_x": { "value": "speed_wall" }, + "speed_layer_0": { "value": "min(round(speed_print * 0.75, 0), 45.0)" }, + "speed_travel": { "default_value": 150, "value": 150 }, + "speed_travel_layer_0": { "value": "round(speed_travel * 0.7, 0)" }, + "skirt_brim_speed": { "value": "speed_layer_0" }, + "skirt_line_count": { "default_value": 3 }, + "skirt_brim_minimal_length": { "default_value": 150 }, + "infill_sparse_density": { "default_value": 30 }, + "infill_pattern": { "value": "'cubic'" }, + "infill_before_walls" : { "default_value": false }, + "top_bottom_thickness": { "default_value": 0.8 }, + "support_z_distance": { "value": "layer_height * 2" }, + "support_bottom_distance": { "value": "layer_height" }, + "support_use_towers" : { "default_value": false }, + "jerk_enabled": { "default_value": 1, "value": "1" }, + "jerk_infill" : { "default_value": 5, "value": "5" }, + "jerk_support" : { "default_value": 5, "value": "5" }, + "acceleration_enabled": { "default_value": 1, "value": "1" }, + "acceleration_travel" : { "value": 5000 }, + "machine_max_feedrate_z" : { "default_value": 300 } } } diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 7319bac606..b4a7788c92 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4579,7 +4579,7 @@ "description": "Whether to prime the filament with a blob before printing. Turning this setting on will ensure that the extruder will have material ready at the nozzle before printing. Printing Brim or Skirt can act like priming too, in which case turning this setting off saves some time.", "type": "bool", "resolve": "any(extruderValues('prime_blob_enable'))", - "default_value": true, + "default_value": false, "settable_per_mesh": false, "settable_per_extruder": true, "enabled": false diff --git a/resources/definitions/structur3d_discov3ry1_complete_um2plus.def.json b/resources/definitions/structur3d_discov3ry1_complete_um2plus.def.json new file mode 100644 index 0000000000..2875b949be --- /dev/null +++ b/resources/definitions/structur3d_discov3ry1_complete_um2plus.def.json @@ -0,0 +1,119 @@ +{ + "version": 2, + "name": "Discov3ry Complete (Ultimaker 2+)", + "inherits": "fdmprinter", + "metadata": { + "author": "Andrew Finkle, CTO", + "manufacturer": "Structur3d.io", + "visible": true, + "weight": 1, + "file_formats": "text/x-gcode", + "platform": "ultimaker2_platform.obj", + "platform_texture": "Ultimaker2Plusbackplate.png", + "platform_offset": [0, 0, 0], + "has_materials": true, + "has_variants": true, + "variants_name": "Print core", + "preferred_variant_name": "0.84mm (Green)", + "has_machine_materials": true, + "preferred_material": "structur3d_dap100silicone", + "has_variant_materials": false, + "has_machine_quality": false, + "preferred_quality_type": "extra_fast", + "first_start_actions": [], + "supported_actions": [], + "machine_extruder_trains": + { + "0": "structur3d_discov3ry1_complete_um2plus_extruder_0" + }, + "firmware_file": "MarlinUltimaker2plus.hex" + }, + + "overrides": { + "machine_name": { "default_value": "Discov3ry Complete (Ultimaker 2+)" }, + "speed_infill": { + "value": "speed_print" + }, + "infill_sparse_density": { + "value": 100 + }, + "retraction_hop_enabled": { + "value": true + }, + "adhesion_type": { + "default_value": "skirt" + }, + "skirt_brim_minimal_length": { + "value": 1500 + }, + "speed_print": { + "value": 15 + }, + "speed_wall_x": { + "value": "speed_wall" + }, + "layer_height_0": { + "value": "round(machine_nozzle_size / 1.5, 2)" + }, + "line_width": { + "value": "round(machine_nozzle_size * 0.875, 2)" + }, + "speed_layer_0": { + "default_value": 10 + }, + "speed_support": { + "value": "speed_wall_0" + }, + "machine_height": { + "default_value": 205 + }, + "machine_width": { + "default_value": 205 + }, + "machine_depth": { + "default_value": 205 + }, + "machine_show_variants": { + "default_value": true + }, + "gantry_height": { + "default_value": 52 + }, + "machine_nozzle_head_distance": { + "default_value": 5 + }, + "machine_nozzle_expansion_angle": { + "default_value": 45 + }, + "machine_heat_zone_length": { + "default_value": 20 + }, + "machine_head_with_fans_polygon": + { + "default_value": [ + [ -44, 14 ], + [ -44, -34 ], + [ 64, 14 ], + [ 64, -34 ] + ] + }, + "machine_disallowed_areas": { + "default_value": [ + [[-115, 112.5], [ -78, 112.5], [ -80, 102.5], [-115, 102.5]], + [[ 115, 112.5], [ 115, 102.5], [ 105, 102.5], [ 103, 112.5]], + [[-115, -112.5], [-115, -104.5], [ -84, -104.5], [ -82, -112.5]], + [[ 115, -112.5], [ 108, -112.5], [ 110, -104.5], [ 115, -104.5]] + ] + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": "\n;Updated Firmware (.hex and Marlin .ino) for \n;Ultimaker 2+ with Discov3ry Extruder available at: \n;https://github.com/Structur3d/UM2.1Discov3ry-Firmware-beta \n;**Learn more at https://www.structur3d.io** \n \nM104 S{material_print_temperature} ;Start heating extruder \nM140 S{material_bed_temperature} ;Start heating bed \nG21 ;metric values \nG90 ;absolute positioning \nM82 ;set extruder to absolute mode \nM107 ;start with the fan off \nM302 ;allow cold extrusion \nM92 E2589 ;set extruder EEPROM steps/mm for paste \nG28 Z0 ;move Z to bottom endstops \nG28 X0 Y0 ;move X/Y to endstops \nG1 X15 Y0 F4000 ;move X/Y to front of printer \nG1 Z15.0 F9000 ;move the platform to 15mm \nG92 E0 ;zero the extruded length \nG1 F200 E10 ;extrude 10 mm of feed stock \nG92 E0 ;zero the extruded length again \nG1 F9000 \n;Put printing message on LCD screen \nM117 Printing..." + }, + "machine_end_gcode": { + "default_value": "M104 S0 ;extruder heater off \nM140 S0 ;heated bed heater off (if you have it) \nM92 E282 ;reset extruder EEPROM steps/mm for plastic filament \nG91 ;relative positioning \nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure \nG1 Z+0.5 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 \nM84 ;steppers off\nG90 ;absolute positioning" + } + + } +} diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index a980a1afdf..aec7907dbe 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -7,7 +7,7 @@ "manufacturer": "Ultimaker B.V.", "category": "Ultimaker", "visible": false, - "exclude_materials": [ "generic_hips", "generic_petg" ] + "exclude_materials": [ "generic_hips", "generic_petg", "structur3d_dap100silicone" ] }, "overrides": { "machine_max_feedrate_e": { diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 72756de2a5..9e017688a2 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -78,7 +78,7 @@ "prime_tower_position_x": { "value": "machine_depth - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) - 30" }, "prime_tower_wipe_enabled": { "default_value": false }, - "prime_blob_enable": { "enabled": true }, + "prime_blob_enable": { "enabled": true, "default_value": true }, "acceleration_enabled": { "value": "True" }, "acceleration_layer_0": { "value": "acceleration_topbottom" }, diff --git a/resources/extruders/anycubic_chiron_extruder_0.def.json b/resources/extruders/anycubic_chiron_extruder_0.def.json new file mode 100644 index 0000000000..cc48df08bb --- /dev/null +++ b/resources/extruders/anycubic_chiron_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "anycubic_chiron_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "anycubic_chiron", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} diff --git a/resources/extruders/deltacomb_extruder_0.def.json b/resources/extruders/deltacomb_extruder_0.def.json index 046becfd82..64c512b7fe 100755 --- a/resources/extruders/deltacomb_extruder_0.def.json +++ b/resources/extruders/deltacomb_extruder_0.def.json @@ -9,8 +9,10 @@ }, "overrides": { - "extruder_nr": { "default_value": 0 }, - "machine_nozzle_size": { "default_value": 0.4 }, - "material_diameter": { "default_value": 1.75 } + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } } } diff --git a/resources/extruders/deltacomb_extruder_1.def.json b/resources/extruders/deltacomb_extruder_1.def.json new file mode 100755 index 0000000000..1657688482 --- /dev/null +++ b/resources/extruders/deltacomb_extruder_1.def.json @@ -0,0 +1,18 @@ +{ + "id": "deltacomb_extruder_1", + "version": 2, + "name": "Extruder 2", + "inherits": "fdmextruder", + "metadata": { + "machine": "deltacomb", + "position": "1" + }, + + "overrides": { + "extruder_nr": { "default_value": 1 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} diff --git a/resources/extruders/structur3d_discov3ry1_complete_um2plus_extruder_0.def.json b/resources/extruders/structur3d_discov3ry1_complete_um2plus_extruder_0.def.json new file mode 100644 index 0000000000..8436dc0a94 --- /dev/null +++ b/resources/extruders/structur3d_discov3ry1_complete_um2plus_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "structur3d_discov3ry1_complete_um2plus_extruder_0", + "version": 2, + "name": "Discov3ry Extruder", + "inherits": "fdmextruder", + "metadata": { + "machine": "structur3d_discov3ry1_complete_um2plus", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.84 }, + "material_diameter": { "default_value": 3.175 } + } +} diff --git a/resources/images/anycubic-chiron.png b/resources/images/anycubic-chiron.png new file mode 100644 index 0000000000..dfe07b42a6 Binary files /dev/null and b/resources/images/anycubic-chiron.png differ diff --git a/resources/meshes/anycubic_chiron_platform.obj b/resources/meshes/anycubic_chiron_platform.obj new file mode 100644 index 0000000000..7a9c8642fc --- /dev/null +++ b/resources/meshes/anycubic_chiron_platform.obj @@ -0,0 +1,150 @@ +# WaveFront *.obj file (generated by Autodesk ATF) +#Author: Patrick Glatt +mtllib ae48cbb4-79ca-43fd-95a7-5e2258fec6c5.mtl + +o Anycubic Chiron Platform +#vertex 1 +v 200.000000 200.000000 0.000000 +#vertex 2 +v 200.000000 -200.000000 0.000000 +#vertex 3 +v 200.000000 -200.000000 -4.000000 +#vertex 4 +v 200.000000 200.000000 -4.000000 +#vertex 5 +v -200.000000 200.000000 0.000000 +#vertex 6 +v -200.000000 200.000000 -4.000000 +#vertex 7 +v -200.000000 -200.000000 0.000000 +#vertex 8 +v -200.000000 -200.000000 -4.000000 + +#vt right +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +#vt front +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +#vt left +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +#vt back +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 +#vt top (only the color of the (0.000000 1.000000 0.000000) corner in logo) +#vt 0.000000 1.000000 0.000000 +#vt 0.000000 1.000000 0.000000 +#vt 0.000000 1.000000 0.000000 +#vt 0.000000 1.000000 0.000000 +#vt top (full logo) +vt 0.00000 0.000000 0.000000 +vt 1.000000 0.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 1.000000 1.000000 0.000000 +#vt bottom +vt 0.000000 0.000000 0.000000 +vt 1.000000 0.000000 0.000000 +vt 0.000000 1.000000 0.000000 +vt 1.000000 1.000000 0.000000 + +#vn right +vn 0.000000 0.000000 0.000000 +vn 0.100000 0.000000 0.000000 +vn 0.100000 0.000000 0.000000 +vn 0.100000 0.000000 0.000000 +#vn front +vn 0.000000 0.100000 0.000000 +vn 0.000000 0.100000 0.000000 +vn 0.000000 0.100000 0.000000 +vn 0.000000 0.100000 0.000000 +#vn left +vn -0.100000 0.000000 0.000000 +vn -0.100000 0.000000 0.000000 +vn -0.100000 0.000000 0.000000 +vn -0.100000 0.000000 0.000000 +#vn back +vn 0.000000 -0.100000 0.000000 +vn 0.000000 -0.100000 0.000000 +vn 0.000000 -0.100000 0.000000 +vn 0.000000 -0.100000 0.000000 +#vn top +vn 0.000000 -0.000000 0.100000 +vn 0.000000 -0.000000 0.100000 +vn 0.000000 -0.000000 0.100000 +vn 0.000000 -0.000000 0.100000 +#vn bottom +vn 0.000000 0.000000 0.100000 +vn 0.000000 0.000000 0.100000 +vn 0.000000 0.000000 0.100000 +vn 0.000000 0.000000 0.100000 + +#faces f v{index}/vt{index}/vn{index}.. +#face right +f 1/1/1 2/2/1 4/3/1 +f 4/3/1 2/2/1 3/4/1 +#face front +f 5/5/5 1/6/5 6/7/5 +f 6/7/5 1/6/5 4/8/5 +#face left +f 7/9/9 5/10/9 8/11/9 +f 8/11/9 5/10/9 6/12/9 +#face back +f 2/13/13 7/14/13 3/15/13 +f 3/15/13 7/14/13 8/16/13 +#face top (uncomment this code if you want to see the logo on top too) +#f 7/17/17 2/18/17 1/20/17 5/19/17 +#face bottom +f 6/21/23 4/22/23 3/24/23 8/23/23 + + +o Small Logo Platform +#9 front left +v -50.000000 270.700000 70.700000 +#10 front right +v 50.000000 270.700000 70.700000 +#11 back right +v 50.000000 200.000000 0.000000 +#12 back left +v -50.000000 200.000000 0.000000 +#13 front left 2 +v -50.000000 270.700000 66.700000 +#14 front right2 +v 50.000000 270.700000 66.700000 +#15 back right 3 +v 50.000000 200.000000 -4.000000 +#16 back left2 +v -50.000000 200.000000 -4.000000 + +#vt's +vt 0.000000 1.000000 0.000000 +vt 0.000000 0.000000 0.000000 +vt 1.000000 0.000000 0.000000 +vt 1.000000 1.000000 0.000000 +vt 0.000000 1.000000 0.000000 + +#vn's +vn 0.000000 0.000000 1.000000 + +#faces f v{index}/vt{index}/vn{index}... +#right +f 10/1/1 11/2/1 15/3/1 14/4/1 +#front +f 9/4/5 10/8/5 14/7/5 13/8/5 +#left +f 12/9/9 9/10/9 13/11/9 16/12/9 +#back +f 11/13/13 12/14/13 16/15/13 15/16/13 +#top +f 12/26/25 11/27/25 10/28/25 9/25/25 +#bottom +f 13/13/23 14/14/23 15/15/23 16/16/23 + diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 6f214a7efb..1adb541cc0 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -14,7 +14,7 @@ UM.ManagementPage id: base; title: catalog.i18nc("@title:tab", "Printers"); - model: Cura.MachineManagementModel { } + model: Cura.GlobalStacksModel { } activeId: Cura.MachineManager.activeMachineId activeIndex: activeMachineIndex() diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index ec99dff6c9..33d7958340 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -19,12 +19,19 @@ Button checkable: true hoverEnabled: true + property bool selected: checked + property bool printerTypeLabelAutoFit: false + property var outputDevice: null property var printerTypesList: [] + property var updatePrinterTypesFunction: updatePrinterTypesList + // This function converts the printer type string to another string. + property var printerTypeLabelConversionFunction: Cura.MachineManager.getAbbreviatedMachineName + function updatePrinterTypesList() { - printerTypesList = (checked && (outputDevice != null)) ? outputDevice.uniquePrinterTypes : [] + printerTypesList = (outputDevice != null) ? outputDevice.uniquePrinterTypes : [] } contentItem: Item @@ -67,7 +74,8 @@ Button model: printerTypesList delegate: Cura.PrinterTypeLabel { - text: Cura.MachineManager.getAbbreviatedMachineName(modelData) + autoFit: printerTypeLabelAutoFit + text: printerTypeLabelConversionFunction(modelData) } } } @@ -76,29 +84,30 @@ Button background: Rectangle { id: backgroundRect - color: machineSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + color: + { + if (!machineSelectorButton.enabled) + { + return UM.Theme.getColor("action_button_disabled") + } + return machineSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + } radius: UM.Theme.getSize("action_button_radius").width border.width: UM.Theme.getSize("default_lining").width - border.color: machineSelectorButton.checked ? UM.Theme.getColor("primary") : "transparent" - } - - onClicked: - { - toggleContent() - Cura.MachineManager.setActiveMachine(model.id) + border.color: machineSelectorButton.selected ? UM.Theme.getColor("primary") : "transparent" } Connections { target: outputDevice - onUniqueConfigurationsChanged: updatePrinterTypesList() + onUniqueConfigurationsChanged: updatePrinterTypesFunction() } Connections { target: Cura.MachineManager - onOutputDevicesChanged: updatePrinterTypesList() + onOutputDevicesChanged: updatePrinterTypesFunction() } - Component.onCompleted: updatePrinterTypesList() + Component.onCompleted: updatePrinterTypesFunction() } diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 49d9d31f2b..1043339ae6 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -42,5 +42,11 @@ ListView } return result } + + onClicked: + { + toggleContent() + Cura.MachineManager.setActiveMachine(model.id) + } } } diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml index cfc9e56513..f2e8dc6f48 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -12,7 +12,9 @@ Item { property alias text: printerTypeLabel.text - width: UM.Theme.getSize("printer_type_label").width + property bool autoFit: false + + width: autoFit ? (printerTypeLabel.width + UM.Theme.getSize("default_margin").width) : UM.Theme.getSize("printer_type_label").width height: UM.Theme.getSize("printer_type_label").height Rectangle diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 0e9be649db..af98469921 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -47,7 +47,7 @@ Cura.ExpandablePopup Label { id: title - text: catalog.i18nc("@label", "View types") + text: catalog.i18nc("@label", "View type") verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight diff --git a/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml new file mode 100644 index 0000000000..2e85117d5c --- /dev/null +++ b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml @@ -0,0 +1,190 @@ +// Copyright (c) 2019 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import UM 1.3 as UM +import Cura 1.0 as Cura + + +// +// This is the scroll view widget for adding a (local) printer. This scroll view shows a list view with printers +// categorized into 3 categories: "Ultimaker", "Custom", and "Other". +// +ScrollView +{ + id: base + + property var currentItem: (machineList.currentIndex >= 0) + ? machineList.model.getItem(machineList.currentIndex) + : null + property string currentSection: preferredCategory + property string preferredCategory: "Ultimaker" + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + property int maxItemCountAtOnce: 10 // show at max 10 items at once, otherwise you need to scroll. + height: maxItemCountAtOnce * UM.Theme.getSize("action_button").height + + clip: true + + function updateCurrentItemUponSectionChange() + { + // Find the first machine from this section + for (var i = 0; i < machineList.count; i++) + { + var item = machineList.model.getItem(i) + if (item.section == base.currentSection) + { + machineList.currentIndex = i + break + } + } + } + + Component.onCompleted: + { + updateCurrentItemUponSectionChange() + } + + background: Rectangle + { + anchors.fill: parent + color: "white" + } + + ListView + { + id: machineList + + model: UM.DefinitionContainersModel + { + id: machineDefinitionsModel + filter: { "visible": true } + sectionProperty: "category" + preferredSectionValue: preferredCategory + } + + section.property: "section" + section.delegate: sectionHeader + delegate: machineButton + } + + Component + { + id: sectionHeader + + Button + { + id: button + width: ListView.view.width + height: UM.Theme.getSize("action_button").height + text: section + + property bool isActive: base.currentSection == section + + background: Rectangle + { + anchors.fill: parent + color: isActive ? UM.Theme.getColor("setting_control_highlight") : "transparent" + } + + contentItem: Item + { + width: childrenRect.width + height: UM.Theme.getSize("action_button").height + + UM.RecolorImage + { + id: arrow + anchors.left: parent.left + //anchors.verticalCenter: label.verticalCenter + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: height + color: UM.Theme.getColor("text") + source: base.currentSection == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") + } + + Label + { + id: label + anchors.left: arrow.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + verticalAlignment: Text.AlignVCenter + text: button.text + font.bold: true + renderType: Text.NativeRendering + } + } + + onClicked: + { + base.currentSection = section + base.updateCurrentItemUponSectionChange() + } + } + } + + Component + { + id: machineButton + + RadioButton + { + id: radioButton + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("standard_list_lineheight").width + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + height: visible ? UM.Theme.getSize("standard_list_lineheight").height : 0 + + checked: ListView.view.currentIndex == index + text: name + font: UM.Theme.getFont("default") + visible: base.currentSection == section + + background: Rectangle + { + anchors.fill: parent + color: "transparent" + } + + indicator: Rectangle + { + implicitWidth: 16 + implicitHeight: 16 + anchors.verticalCenter: parent.verticalCenter + radius: width / 2 + border.width: UM.Theme.getSize("default_lining").width + border.color: radioButton.hovered ? UM.Theme.getColor("small_button_text") : UM.Theme.getColor("small_button_text_hover") + + Rectangle { + width: parent.width / 2 + height: width + anchors.centerIn: parent + radius: width / 2 + color: radioButton.hovered ? UM.Theme.getColor("primary_button_hover") : UM.Theme.getColor("primary_button") + visible: radioButton.checked + } + } + + contentItem: Label + { + verticalAlignment: Text.AlignVCenter + leftPadding: radioButton.indicator.width + radioButton.spacing + text: radioButton.text + font: radioButton.font + renderType: Text.NativeRendering + } + + onClicked: + { + ListView.view.currentIndex = index + } + } + } +} diff --git a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml new file mode 100644 index 0000000000..bee0c49e92 --- /dev/null +++ b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml @@ -0,0 +1,217 @@ +// Copyright (c) 2019 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +import "../PrinterSelector" + + +// +// This is the widget for adding a network printer. There are 2 parts in this widget. One is a scroll view of a list +// of discovered network printers. Beneath the scroll view is a container with 3 buttons: "Refresh", "Add by IP", and +// "Troubleshooting". +// +Item +{ + id: base + height: networkPrinterInfo.height + controlsRectangle.height + + property alias maxItemCountAtOnce: networkPrinterScrollView.maxItemCountAtOnce + property var currentItem: (networkPrinterListView.currentIndex >= 0) + ? networkPrinterListView.model[networkPrinterListView.currentIndex] + : null + + signal refreshButtonClicked() + signal addByIpButtonClicked() + + Item + { + id: networkPrinterInfo + height: networkPrinterScrollView.visible ? networkPrinterScrollView.height : noPrinterLabel.height + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + Label + { + id: noPrinterLabel + height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@label", "There is no printer found over your network.") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + visible: !networkPrinterScrollView.visible + } + + ScrollView + { + id: networkPrinterScrollView + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + property int maxItemCountAtOnce: 8 // show at max 8 items at once, otherwise you need to scroll. + height: maxItemCountAtOnce * UM.Theme.getSize("action_button").height + + visible: networkPrinterListView.model.length > 0 + + clip: true + + ListView + { + id: networkPrinterListView + anchors.fill: parent + model: CuraApplication.getDiscoveredPrintersModel().discovered_printers + + Component.onCompleted: + { + // 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 + break + } + } + } + + delegate: MachineSelectorButton + { + text: modelData.device.name + + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 10 + outputDevice: modelData.device + + enabled: !modelData.is_unknown_machine_type + + printerTypeLabelAutoFit: true + + updatePrinterTypesFunction: updateMachineTypes + // show printer type as it is + printerTypeLabelConversionFunction: function(value) { return value } + + function updateMachineTypes() + { + printerTypesList = [ modelData.readable_machine_type ] + } + + checkable: false + selected: ListView.view.currentIndex == model.index + onClicked: + { + ListView.view.currentIndex = index + } + } + } + } + } + + Cura.RoundedRectangle + { + id: controlsRectangle + anchors.left: parent.left + anchors.right: parent.right + anchors.top: networkPrinterInfo.bottom + // Make sure that the left, right, and bottom borders do not show up, otherwise you see double + // borders. + anchors.bottomMargin: -border.width + anchors.leftMargin: -border.width + anchors.rightMargin: -border.width + + height: UM.Theme.getSize("message_action_button").height + UM.Theme.getSize("default_margin").height + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: "white" + cornerSide: Cura.RoundedRectangle.Direction.Down + + Cura.SecondaryButton + { + id: refreshButton + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter + text: catalog.i18nc("@label", "Refresh") + height: UM.Theme.getSize("message_action_button").height + onClicked: base.refreshButtonClicked() + } + + Cura.SecondaryButton + { + id: addPrinterByIpButton + anchors.left: refreshButton.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter + text: catalog.i18nc("@label", "Add printer by IP") + height: UM.Theme.getSize("message_action_button").height + onClicked: base.addByIpButtonClicked() + } + + Item + { + id: troubleshootingButton + + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter + height: troubleshoortingLinkIcon.height + width: troubleshoortingLinkIcon.width + troubleshoortingLabel.width + UM.Theme.getSize("default_margin").width + + UM.RecolorImage + { + id: troubleshoortingLinkIcon + anchors.right: troubleshoortingLabel.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter + height: troubleshoortingLabel.height + width: height + sourceSize.height: width + color: UM.Theme.getColor("text_link") + source: UM.Theme.getIcon("external_link") + } + + Label + { + id: troubleshoortingLabel + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: catalog.i18nc("@label", "Troubleshooting") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_link") + linkColor: UM.Theme.getColor("text_link") + renderType: Text.NativeRendering + } + + MouseArea + { + anchors.fill: parent + hoverEnabled: true + onClicked: + { + // open the throubleshooting URL with web browser + var url = "https://ultimaker.com/incoming-links/cura/material-compatibilty" // TODO + Qt.openUrlExternally(url) + } + onEntered: + { + troubleshoortingLabel.font.underline = true + } + onExited: + { + troubleshoortingLabel.font.underline = false + } + } + } + } +} diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index c09abac863..47e45cda8a 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -201,7 +201,7 @@ Item text: catalog.i18nc("@button", "Cancel") width: 140 fixedWidthMode: true - onClicked: base.showPreviousPage() + onClicked: base.gotoPage("add_printer_by_selection") enabled: true } diff --git a/resources/qml/WelcomePages/AddPrinterBySelectionContent.qml b/resources/qml/WelcomePages/AddPrinterBySelectionContent.qml new file mode 100644 index 0000000000..3282b219c9 --- /dev/null +++ b/resources/qml/WelcomePages/AddPrinterBySelectionContent.qml @@ -0,0 +1,154 @@ +// Copyright (c) 2019 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import UM 1.3 as UM +import Cura 1.1 as Cura + + +// +// This component contains the content for the "Add a printer" (network) page of the welcome on-boarding process. +// +Item +{ + UM.I18nCatalog { id: catalog; name: "cura" } + + Label + { + id: titleLabel + anchors.top: parent.top + anchors.topMargin: 40 + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: catalog.i18nc("@label", "Add a printer") + color: UM.Theme.getColor("primary_button") + font: UM.Theme.getFont("large_bold") + renderType: Text.NativeRendering + } + + DropDownWidget + { + id: addNetworkPrinterDropDown + + anchors.top: titleLabel.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 20 + + title: catalog.i18nc("@label", "Add a networked printer") + contentShown: true // by default expand the network printer list + + onClicked: + { + if (contentShown) + { + addLocalPrinterDropDown.contentShown = false + } + } + + contentComponent: networkPrinterListComponent + + Component + { + id: networkPrinterListComponent + + AddNetworkPrinterScrollView + { + id: networkPrinterScrollView + + maxItemCountAtOnce: 6 // show at max 6 items at once, otherwise you need to scroll. + + onRefreshButtonClicked: + { + UM.OutputDeviceManager.startDiscovery() + } + + onAddByIpButtonClicked: + { + base.gotoPage("add_printer_by_ip") + } + } + } + } + + DropDownWidget + { + id: addLocalPrinterDropDown + + anchors.top: addNetworkPrinterDropDown.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 20 + + title: catalog.i18nc("@label", "Add a non-networked printer") + + onClicked: + { + if (contentShown) + { + addNetworkPrinterDropDown.contentShown = false + } + } + + contentComponent: localPrinterListComponent + + Component + { + id: localPrinterListComponent + + AddLocalPrinterScrollView + { + id: localPrinterView + + maxItemCountAtOnce: 10 // show at max 10 items at once, otherwise you need to scroll. + } + } + } + + Cura.PrimaryButton + { + id: nextButton + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 40 + enabled: + { + // If the network printer dropdown is expanded, make sure that there is a selected item + if (addNetworkPrinterDropDown.contentShown) + { + return addNetworkPrinterDropDown.contentItem.currentItem != null + } + else + { + return addLocalPrinterDropDown.contentItem.currentItem != null + } + } + + text: catalog.i18nc("@button", "Next") + width: 140 + fixedWidthMode: true + onClicked: + { + // Create a network printer or a local printer according to the selection + if (addNetworkPrinterDropDown.contentShown) + { + // Create a network printer + const networkPrinterItem = addNetworkPrinterDropDown.contentItem.currentItem + CuraApplication.getDiscoveredPrintersModel().createMachineFromDiscoveredPrinter(networkPrinterItem) + } + else + { + // Create a local printer + const localPrinterItem = addLocalPrinterDropDown.contentItem.currentItem + Cura.MachineManager.addMachine(localPrinterItem.id) + } + + // TODO: implement machine actions + + // If we have created a machine, go to the last page, which is the "cloud" page. + base.gotoPage("cloud") + } + } +} diff --git a/resources/qml/WelcomePages/AddPrinterNetwork.qml b/resources/qml/WelcomePages/AddPrinterNetwork.qml deleted file mode 100644 index a9ce982f80..0000000000 --- a/resources/qml/WelcomePages/AddPrinterNetwork.qml +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2019 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import UM 1.3 as UM -import Cura 1.1 as Cura - -import "../PrinterSelector" - -// -// This component contains the content for the "Add a printer" (network) page of the welcome on-boarding process. -// -Item -{ - UM.I18nCatalog { id: catalog; name: "cura" } - - Label - { - id: titleLabel - anchors.top: parent.top - anchors.topMargin: 40 - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - text: catalog.i18nc("@label", "Add a printer") - color: UM.Theme.getColor("primary_button") - font: UM.Theme.getFont("large_bold") - renderType: Text.NativeRendering - } - - DropDownWidget - { - id: addNetworkPrinterDropDown - - anchors.top: titleLabel.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 20 - - title: catalog.i18nc("@label", "Add a network printer") - - contentComponent: networkPrinterListComponent - - Component - { - id: networkPrinterListComponent - - ScrollView - { - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AlwaysOn - - property int maxItemCountAtOnce: 5 // show at max 10 items at once, otherwise you need to scroll. - height: maxItemCountAtOnce * (UM.Theme.getSize("action_button").height) - - clip: true - - ListView - { - id: listView - anchors.fill: parent - model: Cura.GlobalStacksModel {} // TODO: change this to the network printers - - delegate: MachineSelectorButton - { - text: model.name - width: listView.width - UM.Theme.getSize("default_margin").width - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - checked: ListView.view.currentIndex == index - onClicked: - { - ListView.view.currentIndex = index - } - } - } - } - } - } - - DropDownWidget - { - id: addLocalPrinterDropDown - - anchors.top: addNetworkPrinterDropDown.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 20 - - title: catalog.i18nc("@label", "Add a non-network printer") - } - - Cura.PrimaryButton - { - id: nextButton - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 40 - enabled: true // TODO - text: catalog.i18nc("@button", "Next") - width: 140 - fixedWidthMode: true - onClicked: base.showNextPage() - } -} diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index 31f9037b4b..09e52c17dd 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -99,14 +99,13 @@ Item text: catalog.i18nc("@button", "Create an account") width: 140 fixedWidthMode: true - onClicked: base.showNextPage() // TODO: create account + onClicked: Qt.openUrlExternally(CuraApplication.ultimakerCloudAccountRootUrl + "/app/create") } Cura.SecondaryButton { id: signInButton anchors.left: createAccountButton.right - //anchors.leftMargin: 10 anchors.verticalCenter: finishButton.verticalCenter text: catalog.i18nc("@button", "Sign in") width: 80 @@ -115,6 +114,6 @@ Item hoverColor: "transparent" textHoverColor: UM.Theme.getColor("text_light_blue") fixedWidthMode: true - onClicked: base.showNextPage() // TODO: sign in + onClicked: Cura.API.account.login() } } diff --git a/resources/qml/WelcomePages/DropDownHeader.qml b/resources/qml/WelcomePages/DropDownHeader.qml index b115efe3d2..a712382850 100644 --- a/resources/qml/WelcomePages/DropDownHeader.qml +++ b/resources/qml/WelcomePages/DropDownHeader.qml @@ -11,7 +11,7 @@ import ".." // -// This is DropDown Header bar of the expandable drop down list. +// This is DropDown Header bar of the expandable drop down list. See comments in DropDownWidget for details. // Cura.RoundedRectangle { @@ -34,6 +34,8 @@ Cura.RoundedRectangle // If the content is shown property bool contentShown: false + signal clicked() + MouseArea { anchors.fill: parent @@ -41,7 +43,7 @@ Cura.RoundedRectangle onEntered: base.hovered = true onExited: base.hovered = false - onClicked: base.contentShown = !base.contentShown + onClicked: base.clicked() } Label diff --git a/resources/qml/WelcomePages/DropDownWidget.qml b/resources/qml/WelcomePages/DropDownWidget.qml index 5addac13ed..cff9cf8ac1 100644 --- a/resources/qml/WelcomePages/DropDownWidget.qml +++ b/resources/qml/WelcomePages/DropDownWidget.qml @@ -8,6 +8,13 @@ import UM 1.3 as UM import Cura 1.1 as Cura +// +// This is the dropdown list widget in the welcome wizard. The dropdown list has a header bar which is always present, +// and its content whose visibility can be toggled by clicking on the header bar. The content is displayed as an +// expandable dropdown box that will appear below the header bar. +// +// The content is configurable via the property "contentComponent", which will be loaded by a Loader when set. +// Item { UM.I18nCatalog { id: catalog; name: "cura" } @@ -15,12 +22,25 @@ Item id: base implicitWidth: 200 - height: header.contentShown ? childrenRect.height : header.height + height: header.contentShown ? (header.height + contentRectangle.height + 30) : header.height property var contentComponent: null + property alias contentItem: contentLoader.item property alias title: header.title - property alias contentShown: header.contentShown + property bool contentShown: false + + signal clicked() + + Connections + { + target: header + onClicked: + { + base.contentShown = !base.contentShown + clicked() + } + } DropDownHeader { @@ -30,15 +50,16 @@ Item anchors.right: parent.right height: UM.Theme.getSize("expandable_component_content_header").height rightIconSource: contentShown ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") + contentShown: base.contentShown } Cura.RoundedRectangle { id: contentRectangle anchors.top: header.bottom - anchors.horizontalCenter: header.horizontalCenter - width: header.width - height: childrenRect.height + anchors.left: header.left + anchors.right: header.right + height: contentLoader.height + 2 border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") @@ -53,7 +74,6 @@ Item anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - height: childrenRect.height anchors.margins: 1 sourceComponent: base.contentComponent != null ? base.contentComponent : emptyComponent } diff --git a/resources/qml/WelcomePages/StepPanel.qml b/resources/qml/WelcomePages/StepPanel.qml index 40569fe39b..bc99320e02 100644 --- a/resources/qml/WelcomePages/StepPanel.qml +++ b/resources/qml/WelcomePages/StepPanel.qml @@ -33,6 +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. onShowNextPage: { @@ -53,6 +54,29 @@ Item } } + onGotoPage: + { + // find the page index + var page_index = -1 + for (var i = 0; i < base.model.count; i++) + { + const item = base.model.getItem(i) + if (item.id == page_id) + { + page_index = i + break + } + } + if (page_index > 0) + { + currentStep = page_index + } + else + { + console.log("Error: cannot find page with page_id = [", page_id, "]") + } + } + onVisibleChanged: { if (visible) diff --git a/resources/quality/anycubic_chiron/anycubic_chiron_draft.inst.cfg b/resources/quality/anycubic_chiron/anycubic_chiron_draft.inst.cfg new file mode 100644 index 0000000000..6011fdbb32 --- /dev/null +++ b/resources/quality/anycubic_chiron/anycubic_chiron_draft.inst.cfg @@ -0,0 +1,61 @@ +[general] +version = 4 +name = Draft +definition = anycubic_chiron + +[metadata] +setting_version = 7 +type = quality +quality_type = draft +weight = -2 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 1800 +acceleration_travel = 3000 +adhesion_type = skirt +brim_width = 4.0 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 15 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 140 +jerk_enabled = True +jerk_print = 8 +jerk_travel = 10 +layer_height = 0.3 +layer_height_0 = 0.3 +material_bed_temperature = 60 +material_diameter = 1.75 +material_print_temperature = 200 +material_print_temperature_layer_0 = 0 +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 40 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 60 +speed_support = 60 +speed_topbottom = =math.ceil(speed_print * 30 / 60) +speed_travel = 100 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 60 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_xy_distance = 0.7 +top_bottom_thickness = 1.2 +wall_thickness = 1.2 diff --git a/resources/quality/anycubic_chiron/anycubic_chiron_high.inst.cfg b/resources/quality/anycubic_chiron/anycubic_chiron_high.inst.cfg new file mode 100644 index 0000000000..93561d9956 --- /dev/null +++ b/resources/quality/anycubic_chiron/anycubic_chiron_high.inst.cfg @@ -0,0 +1,61 @@ +[general] +version = 4 +name = High +definition = anycubic_chiron + +[metadata] +setting_version = 7 +type = quality +quality_type = high +weight = 1 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 1800 +acceleration_travel = 3000 +adhesion_type = skirt +brim_width = 4.0 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 15 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 140 +jerk_enabled = True +jerk_print = 8 +jerk_travel = 10 +layer_height = 0.1 +layer_height_0 = 0.1 +material_bed_temperature = 60 +material_diameter = 1.75 +material_print_temperature = 200 +material_print_temperature_layer_0 = 0 +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 40 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 50 +speed_support = 30 +speed_topbottom = =math.ceil(speed_print * 20 / 50) +speed_travel = 50 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 60 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_xy_distance = 0.7 +top_bottom_thickness = 1.2 +wall_thickness = 1.2 diff --git a/resources/quality/anycubic_chiron/anycubic_chiron_normal.inst.cfg b/resources/quality/anycubic_chiron/anycubic_chiron_normal.inst.cfg new file mode 100644 index 0000000000..d1496ff187 --- /dev/null +++ b/resources/quality/anycubic_chiron/anycubic_chiron_normal.inst.cfg @@ -0,0 +1,61 @@ +[general] +version = 4 +name = Normal +definition = anycubic_chiron + +[metadata] +setting_version = 7 +type = quality +quality_type = normal +weight = 0 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 1800 +acceleration_travel = 3000 +adhesion_type = skirt +brim_width = 4.0 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 15 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 140 +jerk_enabled = True +jerk_print = 8 +jerk_travel = 10 +layer_height = 0.2 +layer_height_0 = 0.2 +material_bed_temperature = 60 +material_diameter = 1.75 +material_print_temperature = 200 +material_print_temperature_layer_0 = 0 +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 40 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 50 +speed_support = 30 +speed_topbottom = =math.ceil(speed_print * 20 / 50) +speed_travel = 100 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 60 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_xy_distance = 0.7 +top_bottom_thickness = 1.2 +wall_thickness = 1.2 diff --git a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg old mode 100644 new mode 100755 index 9ac0791b7d..3defed4dbc --- a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_abs [values] adhesion_type = raft cool_fan_enabled = True -cool_fan_full_at_height = =layer_height * 2 +cool_fan_full_at_height = =layer_height * 6 cool_fan_speed = 50 cool_fan_speed_max = 50 cool_fan_speed_min = 50 diff --git a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg old mode 100644 new mode 100755 index 43d6bfb778..bcbeba5964 --- a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_abs [values] adhesion_type = raft cool_fan_enabled = True -cool_fan_full_at_height = =layer_height * 2 +cool_fan_full_at_height = =layer_height * 6 cool_fan_speed = 50 cool_fan_speed_max = 50 cool_fan_speed_min = 50 diff --git a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg old mode 100644 new mode 100755 index 7116247ca3..d4b9185108 --- a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg @@ -7,13 +7,13 @@ definition = deltacomb setting_version = 7 type = quality quality_type = high -weight = 1 +weight = 0 material = generic_abs [values] adhesion_type = raft cool_fan_enabled = True -cool_fan_full_at_height = =layer_height * 2 +cool_fan_full_at_height = =layer_height * 6 cool_fan_speed = 50 cool_fan_speed_max = 50 cool_fan_speed_min = 50 diff --git a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg old mode 100644 new mode 100755 index 9b968e1a47..843176f4a1 --- a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_abs [values] adhesion_type = raft cool_fan_enabled = True -cool_fan_full_at_height = =layer_height * 2 +cool_fan_full_at_height = =layer_height * 6 cool_fan_speed = 50 cool_fan_speed_max = 50 cool_fan_speed_min = 50 diff --git a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg old mode 100644 new mode 100755 index 68846ce68b..5035bad786 --- a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_abs [values] adhesion_type = raft cool_fan_enabled = True -cool_fan_full_at_height = =layer_height * 2 +cool_fan_full_at_height = =layer_height * 6 cool_fan_speed = 50 cool_fan_speed_max = 50 cool_fan_speed_min = 50 diff --git a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg index 6495b09042..9b8c8080a2 100755 --- a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = deltacomb setting_version = 7 type = quality quality_type = high -weight = 1 +weight = 0 global_quality = True [values] diff --git a/resources/quality/deltacomb/deltacomb_petg_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_petg_Draft_Quality.inst.cfg new file mode 100644 index 0000000000..003f312fa8 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_petg_Draft_Quality.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 4 +name = Fast +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = draft +weight = -2 +material = generic_petg + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 4 +cool_fan_speed = 60 +cool_fan_speed_max = 100 +cool_fan_speed_min = 60 +cool_min_layer_time = 5 +cool_min_speed = 20 +speed_print = 50 +default_material_print_temperature = 235 +material_standby_temperature = 215 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 diff --git a/resources/quality/deltacomb/deltacomb_petg_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_petg_Fast_Quality.inst.cfg new file mode 100644 index 0000000000..6dbdb759d1 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_petg_Fast_Quality.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 4 +name = Normal +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = fast +weight = -1 +material = generic_petg + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 4 +cool_fan_speed = 60 +cool_fan_speed_max = 100 +cool_fan_speed_min = 60 +cool_min_layer_time = 5 +cool_min_speed = 20 +speed_print = 50 +default_material_print_temperature = 235 +material_standby_temperature = 215 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 diff --git a/resources/quality/deltacomb/deltacomb_petg_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_petg_High_Quality.inst.cfg new file mode 100644 index 0000000000..275edfe4d1 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_petg_High_Quality.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 4 +name = Extra Fine +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = high +weight = 0 +material = generic_petg + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 4 +cool_fan_speed = 60 +cool_fan_speed_max = 100 +cool_fan_speed_min = 60 +cool_min_layer_time = 5 +cool_min_speed = 20 +speed_print = 50 +default_material_print_temperature = 235 +material_standby_temperature = 215 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 diff --git a/resources/quality/deltacomb/deltacomb_petg_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_petg_Normal_Quality.inst.cfg new file mode 100644 index 0000000000..5d746aad68 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_petg_Normal_Quality.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 4 +name = Fine +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = normal +weight = 0 +material = generic_petg + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 4 +cool_fan_speed = 60 +cool_fan_speed_max = 100 +cool_fan_speed_min = 60 +cool_min_layer_time = 5 +cool_min_speed = 20 +speed_print = 50 +default_material_print_temperature = 235 +material_standby_temperature = 215 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 diff --git a/resources/quality/deltacomb/deltacomb_petg_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_petg_Verydraft_Quality.inst.cfg new file mode 100644 index 0000000000..8dd0f1fbb6 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_petg_Verydraft_Quality.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 4 +name = Extra Fast +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = verydraft +weight = -3 +material = generic_petg + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 4 +cool_fan_speed = 60 +cool_fan_speed_max = 100 +cool_fan_speed_min = 60 +cool_min_layer_time = 5 +cool_min_speed = 20 +speed_print = 50 +default_material_print_temperature = 235 +material_standby_temperature = 215 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 diff --git a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg old mode 100644 new mode 100755 index d5387f3014..9c966d726c --- a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_pla [values] adhesion_type = skirt cool_fan_enabled = True -cool_fan_full_at_height = =layer_height +cool_fan_full_at_height = =layer_height * 4 cool_fan_speed = 100 cool_fan_speed_max = 100 cool_fan_speed_min = 100 diff --git a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg old mode 100644 new mode 100755 index b41eb9d81f..a1a1fde055 --- a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_pla [values] adhesion_type = skirt cool_fan_enabled = True -cool_fan_full_at_height = =layer_height +cool_fan_full_at_height = =layer_height * 4 cool_fan_speed = 100 cool_fan_speed_max = 100 cool_fan_speed_min = 100 diff --git a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg old mode 100644 new mode 100755 index ceaeb4667e..0872d6d0a2 --- a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg @@ -7,13 +7,13 @@ definition = deltacomb setting_version = 7 type = quality quality_type = high -weight = 1 +weight = 0 material = generic_pla [values] adhesion_type = skirt cool_fan_enabled = True -cool_fan_full_at_height = =layer_height +cool_fan_full_at_height = =layer_height * 4 cool_fan_speed = 100 cool_fan_speed_max = 100 cool_fan_speed_min = 100 diff --git a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg old mode 100644 new mode 100755 index ba82feb97d..cae00671c4 --- a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_pla [values] adhesion_type = skirt cool_fan_enabled = True -cool_fan_full_at_height = =layer_height +cool_fan_full_at_height = =layer_height * 4 cool_fan_speed = 100 cool_fan_speed_max = 100 cool_fan_speed_min = 100 diff --git a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg old mode 100644 new mode 100755 index f312c27233..c26cec5127 --- a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg @@ -13,7 +13,7 @@ material = generic_pla [values] adhesion_type = skirt cool_fan_enabled = True -cool_fan_full_at_height = =layer_height +cool_fan_full_at_height = =layer_height * 4 cool_fan_speed = 100 cool_fan_speed_max = 100 cool_fan_speed_min = 100 diff --git a/resources/quality/deltacomb/deltacomb_tpu_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_tpu_Draft_Quality.inst.cfg new file mode 100755 index 0000000000..5fcbd76229 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_tpu_Draft_Quality.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 4 +name = Fast +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = draft +weight = -2 +material = generic_tpu + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 6 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 70 +cool_min_layer_time = 5 +cool_min_speed = 20 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 +speed_print = 25 +speed_travel = 300 +acceleration_travel = 10000 +retraction_amount = 5 +retraction_hop_enabled = False diff --git a/resources/quality/deltacomb/deltacomb_tpu_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_tpu_Fast_Quality.inst.cfg new file mode 100755 index 0000000000..299d12ac00 --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_tpu_Fast_Quality.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 4 +name = Normal +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = fast +weight = -1 +material = generic_tpu + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 6 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 70 +cool_min_layer_time = 5 +cool_min_speed = 20 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 +speed_print = 25 +speed_travel = 300 +acceleration_travel = 10000 +retraction_amount = 5 +retraction_hop_enabled = False diff --git a/resources/quality/deltacomb/deltacomb_tpu_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_tpu_High_Quality.inst.cfg new file mode 100755 index 0000000000..32b2aadd0b --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_tpu_High_Quality.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 4 +name = Extra Fine +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = high +weight = 0 +material = generic_tpu + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 6 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 70 +cool_min_layer_time = 5 +cool_min_speed = 20 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 +speed_print = 25 +speed_travel = 300 +acceleration_travel = 10000 +retraction_amount = 5 +retraction_hop_enabled = False diff --git a/resources/quality/deltacomb/deltacomb_tpu_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_tpu_Normal_Quality.inst.cfg new file mode 100755 index 0000000000..880c35c14e --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_tpu_Normal_Quality.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 4 +name = Fine +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = normal +weight = 0 +material = generic_tpu + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 6 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 70 +cool_min_layer_time = 5 +cool_min_speed = 20 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 +speed_print = 25 +speed_travel = 300 +acceleration_travel = 10000 +retraction_amount = 5 +retraction_hop_enabled = False diff --git a/resources/quality/deltacomb/deltacomb_tpu_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_tpu_Verydraft_Quality.inst.cfg new file mode 100755 index 0000000000..104df50eee --- /dev/null +++ b/resources/quality/deltacomb/deltacomb_tpu_Verydraft_Quality.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 4 +name = Extra Fast +definition = deltacomb + +[metadata] +setting_version = 7 +type = quality +quality_type = verydraft +weight = -3 +material = generic_tpu + +[values] +adhesion_type = skirt +cool_fan_enabled = True +cool_fan_full_at_height = =layer_height * 6 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 70 +cool_min_layer_time = 5 +cool_min_speed = 20 +material_print_temperature_layer_0 = =default_material_print_temperature + 5 +speed_print = 25 +speed_travel = 300 +acceleration_travel = 10000 +retraction_amount = 5 +retraction_hop_enabled = False diff --git a/resources/variants/deltacomb_025_e3d.inst.cfg b/resources/variants/deltacomb_025_e3d.inst.cfg new file mode 100755 index 0000000000..a741ee38b6 --- /dev/null +++ b/resources/variants/deltacomb_025_e3d.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = E3D 0.25mm +version = 4 +definition = deltacomb + +[metadata] +author = Deltacomb 3D +setting_version = 5 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_size = 0.25 diff --git a/resources/variants/deltacomb_040_e3d.inst.cfg b/resources/variants/deltacomb_040_e3d.inst.cfg new file mode 100755 index 0000000000..3f8f122af1 --- /dev/null +++ b/resources/variants/deltacomb_040_e3d.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = E3D 0.40mm +version = 4 +definition = deltacomb + +[metadata] +author = Deltacomb 3D +setting_version = 5 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_size = 0.4 diff --git a/resources/variants/deltacomb_080_e3d.inst.cfg b/resources/variants/deltacomb_080_e3d.inst.cfg new file mode 100755 index 0000000000..bcef7198b8 --- /dev/null +++ b/resources/variants/deltacomb_080_e3d.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = E3D 0.80mm +version = 4 +definition = deltacomb + +[metadata] +author = Deltacomb 3D +setting_version = 5 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_size = 0.8 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_0.20.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.20.inst.cfg new file mode 100644 index 0000000000..bd67b654cd --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.20.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 0.20mm (Clear) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 0.20mm (Clear) +machine_nozzle_size = 0.20 +machine_nozzle_tip_outer_diameter = 0.30 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_0.25.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.25.inst.cfg new file mode 100644 index 0000000000..389984f293 --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.25.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 0.25mm (Red) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 0.25mm (Red) +machine_nozzle_size = 0.25 +machine_nozzle_tip_outer_diameter = 0.35 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_0.41.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.41.inst.cfg new file mode 100644 index 0000000000..f6936233e9 --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.41.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 0.41mm (Blue) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 0.41mm (Blue) +machine_nozzle_size = 0.41 +machine_nozzle_tip_outer_diameter = 0.51 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_0.58.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.58.inst.cfg new file mode 100644 index 0000000000..b30770afc4 --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.58.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 0.58mm (Pink) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 0.58mm (Pink) +machine_nozzle_size = 0.58 +machine_nozzle_tip_outer_diameter = 0.68 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_0.84.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.84.inst.cfg new file mode 100644 index 0000000000..b83bef727d --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_0.84.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 0.84mm (Green) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 0.84mm (Green) +machine_nozzle_size = 0.84 +machine_nozzle_tip_outer_diameter = 0.94 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_1.19.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_1.19.inst.cfg new file mode 100644 index 0000000000..9a0f4922ef --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_1.19.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 1.19mm (Grey) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 1.19mm (Grey) +machine_nozzle_size = 1.19 +machine_nozzle_tip_outer_diameter = 1.29 diff --git a/resources/variants/structur3d_discov3ry1_complete_um2plus_1.60.inst.cfg b/resources/variants/structur3d_discov3ry1_complete_um2plus_1.60.inst.cfg new file mode 100644 index 0000000000..a235f406c0 --- /dev/null +++ b/resources/variants/structur3d_discov3ry1_complete_um2plus_1.60.inst.cfg @@ -0,0 +1,14 @@ +[general] +name = 1.60mm (Olive) +version = 4 +definition = structur3d_discov3ry1_complete_um2plus + +[metadata] +setting_version = 7 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_id = 1.60mm (Olive) +machine_nozzle_size = 1.60 +machine_nozzle_tip_outer_diameter = 1.70 diff --git a/tests/TestMachineManager.py b/tests/TestMachineManager.py index 34a0bbc35c..36ffe01b96 100644 --- a/tests/TestMachineManager.py +++ b/tests/TestMachineManager.py @@ -61,4 +61,4 @@ def test_discoveredMachine(machine_manager): # Just in case, nothing should happen. machine_manager.addMachineFromDiscoveredPrinter("test") - assert mocked_callback.call_count == 1 \ No newline at end of file + assert mocked_callback.call_count == 1