diff --git a/cura/API/Account.py b/cura/API/Account.py index 4868204a94..e190fe9b42 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -10,7 +10,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.Models import OAuth2Settings -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants if TYPE_CHECKING: from cura.CuraApplication import CuraApplication @@ -69,7 +69,7 @@ class Account(QObject): self._last_sync_str = "-" self._callback_port = 32118 - self._oauth_root = UltimakerCloudAuthentication.CuraCloudAccountAPIRoot + self._oauth_root = UltimakerCloudConstants.CuraCloudAccountAPIRoot self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, diff --git a/cura/API/ConnectionStatus.py b/cura/API/ConnectionStatus.py index 332e519ca9..007f03fdd1 100644 --- a/cura/API/ConnectionStatus.py +++ b/cura/API/ConnectionStatus.py @@ -4,14 +4,14 @@ from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty from PyQt5.QtNetwork import QNetworkReply from UM.TaskManagement.HttpRequestManager import HttpRequestManager -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class ConnectionStatus(QObject): """Status info for some web services""" UPDATE_INTERVAL = 10.0 # seconds - ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/" + ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudConstants.CuraCloudAPIRoot + "/connect/v1/" __instance = None # type: Optional[ConnectionStatus] diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3af6ebeaf8..90197f8037 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -106,7 +106,7 @@ from cura.UI.RecommendedMode import RecommendedMode from cura.UI.TextManager import TextManager from cura.UI.WelcomePagesModel import WelcomePagesModel from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants from cura.Utils.NetworkingUtil import NetworkingUtil from . import BuildVolume from . import CameraAnimation @@ -207,6 +207,7 @@ class CuraApplication(QtApplication): self._first_start_machine_actions_model = None self._welcome_pages_model = WelcomePagesModel(self, parent = self) self._add_printer_pages_model = AddPrinterPagesModel(self, parent = self) + self._add_printer_pages_model_without_cancel = AddPrinterPagesModel(self, parent = self) self._whats_new_pages_model = WhatsNewPagesModel(self, parent = self) self._text_manager = TextManager(parent = self) @@ -255,11 +256,11 @@ class CuraApplication(QtApplication): @pyqtProperty(str, constant=True) def ultimakerCloudApiRootUrl(self) -> str: - return UltimakerCloudAuthentication.CuraCloudAPIRoot + return UltimakerCloudConstants.CuraCloudAPIRoot @pyqtProperty(str, constant = True) def ultimakerCloudAccountRootUrl(self) -> str: - return UltimakerCloudAuthentication.CuraCloudAccountAPIRoot + return UltimakerCloudConstants.CuraCloudAccountAPIRoot @pyqtProperty(str, constant=True) def ultimakerDigitalFactoryUrl(self) -> str: @@ -651,7 +652,7 @@ class CuraApplication(QtApplication): return self._global_container_stack @override(Application) - def setGlobalContainerStack(self, stack: "GlobalStack") -> None: + def setGlobalContainerStack(self, stack: Optional["GlobalStack"]) -> None: self._setLoadingHint(self._i18n_catalog.i18nc("@info:progress", "Initializing Active Machine...")) super().setGlobalContainerStack(stack) @@ -816,6 +817,7 @@ class CuraApplication(QtApplication): self._output_device_manager.start() self._welcome_pages_model.initialize() self._add_printer_pages_model.initialize() + self._add_printer_pages_model_without_cancel.initialize(cancellable = False) self._whats_new_pages_model.initialize() # Detect in which mode to run and execute that mode @@ -853,6 +855,7 @@ class CuraApplication(QtApplication): self.callLater(self._openFile, file_name) initializationFinished = pyqtSignal() + showAddPrintersUncancellableDialog = pyqtSignal() # Used to show the add printers dialog with a greyed background def runWithoutGUI(self): """Run Cura without GUI elements and interaction (server mode).""" @@ -943,6 +946,10 @@ class CuraApplication(QtApplication): def getAddPrinterPagesModel(self, *args) -> "AddPrinterPagesModel": return self._add_printer_pages_model + @pyqtSlot(result = QObject) + def getAddPrinterPagesModelWithoutCancel(self, *args) -> "AddPrinterPagesModel": + return self._add_printer_pages_model_without_cancel + @pyqtSlot(result = QObject) def getWhatsNewPagesModel(self, *args) -> "WhatsNewPagesModel": return self._whats_new_pages_model diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index aa8552bebb..776d540867 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -154,7 +154,7 @@ class BaseMaterialsModel(ListModel): # Update the available materials (ContainerNode) for the current active machine and extruder setup. global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() - if not global_stack.hasMaterials: + if not global_stack or not global_stack.hasMaterials: return # There are no materials for this machine, so nothing to do. extruder_list = global_stack.extruderList if self._extruder_position > len(extruder_list): diff --git a/cura/OAuth2/AuthorizationRequestServer.py b/cura/OAuth2/AuthorizationRequestServer.py index 74b0b5f012..4ed3975638 100644 --- a/cura/OAuth2/AuthorizationRequestServer.py +++ b/cura/OAuth2/AuthorizationRequestServer.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from http.server import HTTPServer +from socketserver import ThreadingMixIn from typing import Callable, Any, TYPE_CHECKING if TYPE_CHECKING: @@ -9,7 +10,7 @@ if TYPE_CHECKING: from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers -class AuthorizationRequestServer(HTTPServer): +class AuthorizationRequestServer(ThreadingMixIn, HTTPServer): """The authorization request callback handler server. This subclass is needed to be able to pass some data to the request handler. This cannot be done on the request diff --git a/cura/OAuth2/LocalAuthorizationServer.py b/cura/OAuth2/LocalAuthorizationServer.py index a41de2d406..ac14b00985 100644 --- a/cura/OAuth2/LocalAuthorizationServer.py +++ b/cura/OAuth2/LocalAuthorizationServer.py @@ -81,7 +81,7 @@ class LocalAuthorizationServer: if self._web_server: try: - self._web_server.server_close() + self._web_server.shutdown() except OSError: # OS error can happen if the socket was already closed. We really don't care about that case. pass diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5086c68a73..423df167cd 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -22,6 +22,7 @@ from UM.Settings.SettingFunction import SettingFunction from UM.Signal import postponeSignals, CompressTechnique import cura.CuraApplication # Imported like this to prevent circular references. +from UM.Util import parseBool from cura.Machines.ContainerNode import ContainerNode from cura.Machines.ContainerTree import ContainerTree @@ -37,6 +38,7 @@ from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container, empty_material_container, empty_quality_container, empty_quality_changes_container, empty_intent_container) +from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT from .CuraStackBuilder import CuraStackBuilder @@ -288,9 +290,15 @@ class MachineManager(QObject): self.activeStackValueChanged.emit() @pyqtSlot(str) - def setActiveMachine(self, stack_id: str) -> None: + def setActiveMachine(self, stack_id: Optional[str]) -> None: self.blurSettings.emit() # Ensure no-one has focus. + if not stack_id: + self._application.setGlobalContainerStack(None) + self.globalContainerChanged.emit() + self._application.showAddPrintersUncancellableDialog.emit() + return + container_registry = CuraContainerRegistry.getInstance() containers = container_registry.findContainerStacks(id = stack_id) if not containers: @@ -494,6 +502,10 @@ class MachineManager(QObject): group_size = int(self.activeMachine.getMetaDataEntry("group_size", "-1")) return group_size > 1 + @pyqtProperty(bool, notify = printerConnectedStatusChanged) + def activeMachineIsLinkedToCurrentAccount(self) -> bool: + return parseBool(self.activeMachine.getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "True")) + @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasNetworkConnection(self) -> bool: # A network connection is only available if any output device is actually a network connected device. @@ -715,6 +727,8 @@ class MachineManager(QObject): other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id] if other_machine_stacks: self.setActiveMachine(other_machine_stacks[0]["id"]) + else: + self.setActiveMachine(None) metadatas = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id) if not metadatas: diff --git a/cura/UI/AddPrinterPagesModel.py b/cura/UI/AddPrinterPagesModel.py index b06f220374..9b35dbcacc 100644 --- a/cura/UI/AddPrinterPagesModel.py +++ b/cura/UI/AddPrinterPagesModel.py @@ -10,12 +10,11 @@ from .WelcomePagesModel import WelcomePagesModel # class AddPrinterPagesModel(WelcomePagesModel): - def initialize(self) -> None: + def initialize(self, cancellable: bool = True) -> None: self._pages.append({"id": "add_network_or_local_printer", "page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"), "next_page_id": "machine_actions", "next_page_button_text": self._catalog.i18nc("@action:button", "Add"), - "previous_page_button_text": self._catalog.i18nc("@action:button", "Cancel"), }) self._pages.append({"id": "add_printer_by_ip", "page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"), @@ -30,6 +29,9 @@ class AddPrinterPagesModel(WelcomePagesModel): "page_url": self._getBuiltinWelcomePagePath("FirstStartMachineActionsContent.qml"), "should_show_function": self.shouldShowMachineActions, }) + if cancellable: + self._pages[0]["previous_page_button_text"] = self._catalog.i18nc("@action:button", "Cancel") + self.setItems(self._pages) diff --git a/cura/UltimakerCloud/UltimakerCloudAuthentication.py b/cura/UltimakerCloud/UltimakerCloudConstants.py similarity index 90% rename from cura/UltimakerCloud/UltimakerCloudAuthentication.py rename to cura/UltimakerCloud/UltimakerCloudConstants.py index 707b814d34..f3f862b689 100644 --- a/cura/UltimakerCloud/UltimakerCloudAuthentication.py +++ b/cura/UltimakerCloud/UltimakerCloudConstants.py @@ -9,6 +9,10 @@ DEFAULT_CLOUD_API_VERSION = "1" # type: str DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str DEFAULT_DIGITAL_FACTORY_URL = "https://digitalfactory.ultimaker.com" # type: str +# Container Metadata keys +META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account" +"""(bool) Whether a cloud printer is linked to an Ultimaker account""" + try: from cura.CuraVersion import CuraCloudAPIRoot # type: ignore if CuraCloudAPIRoot == "": diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 639c63b45f..56158922dc 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,13 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class Settings: # Keeps the plugin settings. DRIVE_API_VERSION = 1 - DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) + DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" diff --git a/plugins/PostProcessingPlugin/scripts/Stretch.py b/plugins/PostProcessingPlugin/scripts/Stretch.py index 480ba60606..e56a9f48b1 100644 --- a/plugins/PostProcessingPlugin/scripts/Stretch.py +++ b/plugins/PostProcessingPlugin/scripts/Stretch.py @@ -289,6 +289,13 @@ class Stretcher: self.layergcode = self.layergcode + sout + "\n" ipos = ipos + 1 else: + # The command is intended to be passed through unmodified via + # the comment field. In the case of an extruder only move, though, + # the extruder and potentially the feed rate are modified. + # We need to update self.outpos accordingly so that subsequent calls + # to stepToGcode() knows about the extruder and feed rate change. + self.outpos.step_e = layer_steps[i].step_e + self.outpos.step_f = layer_steps[i].step_f self.layergcode = self.layergcode + layer_steps[i].comment + "\n" def workOnSequence(self, orig_seq, modif_seq): diff --git a/plugins/Toolbox/src/CloudApiModel.py b/plugins/Toolbox/src/CloudApiModel.py index b4ff00c6cd..bef37d8173 100644 --- a/plugins/Toolbox/src/CloudApiModel.py +++ b/plugins/Toolbox/src/CloudApiModel.py @@ -1,13 +1,13 @@ from typing import Union from cura import ApplicationMetadata -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class CloudApiModel: sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] - cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: str - cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str + cloud_api_version = UltimakerCloudConstants.CuraCloudAPIVersion # type: str + cloud_api_root = UltimakerCloudConstants.CuraCloudAPIRoot # type: str api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( cloud_api_root = cloud_api_root, cloud_api_version = cloud_api_version, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index ab14d7ff06..713ee25170 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -13,7 +13,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope from cura.API import Account from cura.CuraApplication import CuraApplication -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope from .ToolPathUploader import ToolPathUploader from ..Models.BaseModel import BaseModel @@ -35,7 +35,7 @@ class CloudApiClient: """ # The cloud URL to use for this remote cluster. - ROOT_PATH = UltimakerCloudAuthentication.CuraCloudAPIRoot + ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f0b1bb130c..33c9caba05 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,21 +1,23 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Set -from PyQt5.QtCore import QTimer from PyQt5.QtNetwork import QNetworkReply +from PyQt5.QtWidgets import QMessageBox from UM import i18nCatalog from UM.Logger import Logger # To log errors talking to the API. from UM.Message import Message from UM.Settings.Interfaces import ContainerInterface from UM.Signal import Signal +from UM.Util import parseBool from cura.API import Account from cura.API.Account import SyncState from cura.CuraApplication import CuraApplication from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.GlobalStack import GlobalStack +from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse @@ -30,6 +32,7 @@ class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" META_NETWORK_KEY = "um_network_key" + SYNC_SERVICE_NAME = "CloudOutputDeviceManager" # The translation catalog for this device. @@ -41,9 +44,14 @@ class CloudOutputDeviceManager: def __init__(self) -> None: # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] + + # Dictionary containing all the cloud printers loaded in Cura + self._um_cloud_printers = {} # type: Dict[str, GlobalStack] + self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error))) self._account.loginStateChanged.connect(self._onLoginStateChanged) + self._removed_printers_message = None # type: Optional[Message] # Ensure we don't start twice. self._running = False @@ -98,23 +106,41 @@ class CloudOutputDeviceManager: def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None: """Callback for when the request for getting the clusters is finished.""" + self._um_cloud_printers = {m.getMetaDataEntry(self.META_CLUSTER_ID): m for m in + CuraApplication.getInstance().getContainerRegistry().findContainerStacks( + type = "machine") if m.getMetaDataEntry(self.META_CLUSTER_ID, None)} new_clusters = [] all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse] online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] + # Add the new printers in Cura. If a printer was previously added and is rediscovered, set its metadata to + # reflect that and mark the printer not removed from the account for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) - + if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) self._onDevicesDiscovered(new_clusters) - removed_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys()) - for device_id in removed_device_keys: + # Hide the current removed_printers_message, if there is any + if self._removed_printers_message: + self._removed_printers_message.actionTriggered.disconnect(self._onRemovedPrintersMessageActionTriggered) + self._removed_printers_message.hide() + + # Remove the CloudOutput device for offline printers + offline_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys()) + for device_id in offline_device_keys: self._onDiscoveredDeviceRemoved(device_id) - if new_clusters or removed_device_keys: - self.discoveredDevicesChanged.emit() + # Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were + # removed from the account) + removed_device_keys = set(self._um_cloud_printers.keys()) - set(all_clusters.keys()) if removed_device_keys: + self._devicesRemovedFromAccount(removed_device_keys) + + if new_clusters or offline_device_keys or removed_device_keys: + self.discoveredDevicesChanged.emit() + if offline_device_keys: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() @@ -144,10 +170,13 @@ class CloudOutputDeviceManager: if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \ and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key. new_devices.append(device) - elif device.getId() not in self._remote_clusters: self._remote_clusters[device.getId()] = device remote_clusters_added = True + # If a printer that was removed from the account is re-added, change its metadata to mark it not removed + # from the account + elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) # Inform the Cloud printers model about new devices. new_devices_list_of_dicts = [{ @@ -163,7 +192,11 @@ class CloudOutputDeviceManager: self._connectToActiveMachine() return - new_devices.sort(key = lambda x: x.name.lower()) + # Sort new_devices on online status first, alphabetical second. + # Since the first device might be activated in case there is no active printer yet, + # it would be nice to prioritize online devices + online_cluster_names = {c.friendly_name.lower() for c in clusters if c.is_online and not c.friendly_name is None} + new_devices.sort(key = lambda x: ("a{}" if x.name.lower() in online_cluster_names else "b{}").format(x.name.lower())) image_path = os.path.join( CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting") or "", @@ -204,19 +237,97 @@ class CloudOutputDeviceManager: max_disp_devices = 3 if len(new_devices) > max_disp_devices: num_hidden = len(new_devices) - max_disp_devices + 1 - device_name_list = ["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] - device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "- and {} others", num_hidden)) + device_name_list = ["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] + device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "
  • ... and {} others
  • ", num_hidden)) device_names = "\n".join(device_name_list) else: - device_names = "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices]) + device_names = "\n".join(["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices]) message_text = self.I18N_CATALOG.i18nc( "info:status", - "Cloud printers added from your account:\n{}", + "Cloud printers added from your account:\n", device_names ) message.setText(message_text) + def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None: + """ + Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from + account". In addition, it generates a message to inform the user about the printers that are no longer linked to + his/her account. The message is not generated if all the printers have been previously reported as not linked + to the account. + + :param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed + :return: None + """ + + if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: + return + + # Do not report device ids which have been previously marked as non-linked to the account + ignored_device_ids = set() + for device_id in removed_device_ids: + if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + ignored_device_ids.add(device_id) + # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and + # take the necessary steps to fulfill their purpose. + self.reported_device_ids = removed_device_ids - ignored_device_ids + if not self.reported_device_ids: + return + + # Generate message + self._removed_printers_message = Message( + title = self.I18N_CATALOG.i18ncp( + "info:status", + "Cloud connection is not available for a printer", + "Cloud connection is not available for some printers", + len(self.reported_device_ids) + ) + ) + device_names = "\n".join(["
  • {} ({})
  • ".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids]) + message_text = self.I18N_CATALOG.i18ncp( + "info:status", + "The following cloud printer is not linked to your account:\n", + "The following cloud printers are not linked to your account:\n", + len(self.reported_device_ids) + ) + message_text += self.I18N_CATALOG.i18nc( + "info:status", + "\nTo establish a connection, please visit the " + "Ultimaker Digital Factory.", + device_names + ) + self._removed_printers_message.setText(message_text) + self._removed_printers_message.addAction("keep_printer_configurations_action", + name = self.I18N_CATALOG.i18nc("@action:button", "Keep printer configurations"), + icon = "", + description = "Keep the configuration of the cloud printer(s) synced with Cura which are not linked to your account.", + button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) + self._removed_printers_message.addAction("remove_printers_action", + name = self.I18N_CATALOG.i18nc("@action:button", "Remove printers"), + icon = "", + description = "Remove the cloud printer(s) which are not linked to your account.", + button_style = Message.ActionButtonStyle.SECONDARY, + button_align = Message.ActionButtonAlignment.ALIGN_LEFT) + self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered) + + output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() + + # Remove the output device from the printers + for device_id in removed_device_ids: + device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack] + if not device: + continue + if device_id in output_device_manager.getOutputDeviceIds(): + output_device_manager.removeOutputDevice(device_id) + if device_id in self._remote_clusters: + del self._remote_clusters[device_id] + + # Update the printer's metadata to mark it as not linked to the account + device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False) + + self._removed_printers_message.show() + def _onDiscoveredDeviceRemoved(self, device_id: str) -> None: device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice] if not device: @@ -302,3 +413,24 @@ class CloudOutputDeviceManager: container_cluster_id = container.getMetaDataEntry(self.META_CLUSTER_ID, None) if container_cluster_id in self._remote_clusters.keys(): del self._remote_clusters[container_cluster_id] + + def _onRemovedPrintersMessageActionTriggered(self, removed_printers_message: Message, action: str) -> None: + if action == "keep_printer_configurations_action": + removed_printers_message.hide() + elif action == "remove_printers_action": + machine_manager = CuraApplication.getInstance().getMachineManager() + remove_printers_ids = {self._um_cloud_printers[i].getId() for i in self.reported_device_ids} + all_ids = {m.getId() for m in CuraApplication.getInstance().getContainerRegistry().findContainerStacks(type = "machine")} + + question_title = self.I18N_CATALOG.i18nc("@title:window", "Remove printers?") + question_content = self.I18N_CATALOG.i18nc("@label", "You are about to remove {} printer(s) from Cura. This action cannot be undone. \nAre you sure you want to continue?".format(len(remove_printers_ids))) + if remove_printers_ids == all_ids: + question_content = self.I18N_CATALOG.i18nc("@label", "You are about to remove all printers from Cura. This action cannot be undone. \nAre you sure you want to continue?") + result = QMessageBox.question(None, question_title, question_content) + if result == QMessageBox.No: + return + + for machine_cloud_id in self.reported_device_ids: + machine_manager.setActiveMachine(self._um_cloud_printers[machine_cloud_id].getId()) + machine_manager.removeMachine(self._um_cloud_printers[machine_cloud_id].getId()) + removed_printers_message.hide() diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7978c0cdba..6fe4d4242b 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -465,7 +465,7 @@ class XmlMaterialProfile(InstanceContainer): return "materials" @classmethod - def getVersionFromSerialized(cls, serialized: str) -> Optional[int]: + def getVersionFromSerialized(cls, serialized: str) -> int: data = ET.fromstring(serialized) version = XmlMaterialProfile.Version diff --git a/resources/definitions/dagoma_delta.def.json b/resources/definitions/dagoma_delta.def.json new file mode 100644 index 0000000000..6ff837e757 --- /dev/null +++ b/resources/definitions/dagoma_delta.def.json @@ -0,0 +1,68 @@ +{ + "name": "Dagoma Delta", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": false, + "author": "Dagoma", + "manufacturer": "Dagoma" + }, + "overrides": { + "machine_width": { + "default_value": 195.55 + }, + "machine_height": { + "default_value": 205 + }, + "machine_depth": { + "default_value": 195.55 + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-36, -42], + [-36, 42], + [36, 42], + [36, -42] + ] + }, + "gantry_height": { + "value": "0" + }, + "machine_shape": { + "default_value": "elliptic" + }, + "machine_start_gcode": { + "default_value": ";Gcode by Cura\nG90\nG28\nM107\nM109 R100\nG29\nM109 S{material_print_temperature_layer_0} U-55 X55 V-85 Y-85 W0.26 Z0.26\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n" + }, + "machine_end_gcode": { + "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n" + }, + "default_material_print_temperature": { + "default_value": 205 + }, + "speed_print": { + "default_value": 40 + }, + "retraction_amount": { + "default_value": 3.8 + }, + "retraction_speed": { + "default_value": 60 + }, + "adhesion_type": { + "default_value": "skirt" + }, + "skirt_line_count": { + "default_value": 2 + }, + "layer_height_0": { + "default_value": 0.26 + }, + "top_bottom_thickness": { + "default_value": 1 + } + } +} diff --git a/resources/definitions/dagoma_disco.def.json b/resources/definitions/dagoma_disco.def.json new file mode 100644 index 0000000000..a62948c9a7 --- /dev/null +++ b/resources/definitions/dagoma_disco.def.json @@ -0,0 +1,62 @@ +{ + "name": "Dagoma Disco", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": false, + "author": "Dagoma", + "manufacturer": "Dagoma" + }, + "overrides": { + "machine_width": { + "default_value": 205 + }, + "machine_height": { + "default_value": 205 + }, + "machine_depth": { + "default_value": 205 + }, + "machine_center_is_zero": { + "default_value": false + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-17, -70], + [-17, 40], + [17, 40], + [17, -70] + ] + }, + "gantry_height": { + "value": "10" + }, + "default_material_print_temperature": { + "default_value": 205 + }, + "material_standby_temperature": { + "default_value": 90 + }, + "speed_print": { + "default_value": 60 + }, + "retraction_amount": { + "default_value": 3.5 + }, + "retraction_speed": { + "default_value": 50 + }, + "adhesion_type": { + "default_value": "skirt" + }, + "skirt_line_count": { + "default_value": 2 + }, + "layer_height_0": { + "default_value": 0.26 + }, + "top_bottom_thickness": { + "default_value": 1 + } + } +} diff --git a/resources/definitions/dagoma_discoeasy200.def.json b/resources/definitions/dagoma_discoeasy200.def.json index 1032a249f8..16ce5585e4 100644 --- a/resources/definitions/dagoma_discoeasy200.def.json +++ b/resources/definitions/dagoma_discoeasy200.def.json @@ -1,82 +1,34 @@ { "name": "Dagoma DiscoEasy200", "version": 2, - "inherits": "fdmprinter", + "inherits": "dagoma_disco", "metadata": { "visible": true, "author": "Dagoma", "manufacturer": "Dagoma", "file_formats": "text/x-gcode", "platform": "dagoma_discoeasy200.3mf", - "platform_offset": [0, -57.3, -11], + "platform_offset": [0, -57, -39], "has_machine_quality": true, "has_materials": true, "preferred_material": "chromatik_pla", "machine_extruder_trains": { - "0": "dagoma_discoeasy200_extruder_0", - "1": "dagoma_discoeasy200_extruder_1" + "0": "dagoma_discoeasy200_extruder" } }, "overrides": { + "machine_name": { + "default_value": "Dagoma DiscoEasy200" + }, "machine_extruder_count": { - "default_value": 2 - }, - "machine_extruders_share_heater": { - "default_value": true - }, - "machine_width": { - "default_value": 205 - }, - "machine_height": { - "default_value": 205 - }, - "machine_depth": { - "default_value": 205 - }, - "machine_center_is_zero": { - "default_value": false - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [-17, -70], - [-17, 40], - [17, 40], - [17, -70] - ] - }, - "gantry_height": { - "value": "10" + "default_value": 1 }, "machine_start_gcode": { - "default_value": ";Gcode by Cura\nG90\nM106 S255\nG28 X Y\nG1 X50\nM109 R90\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n" + "default_value": ";Begin Start Gcode for Dagoma DiscoEasy 200\n;Sliced: {date} {time}\n;Initial extruder: {initial_extruder_nr}\n\nG90 ;Absolute positioning\nM106 S255 ;Fan on full\nG28 X Y ;Home stop X Y\nG1 X100 ;Centre back during cooldown in case of oozing\nM109 R{material_standby_temperature} ;Cooldown in case too hot\nG28 ;Centre\nG29 ;Auto-level\nM104 S{material_print_temperature_layer_0} ;Pre-heat\nM107 ;Fan off\nG0 X100 Y5 Z0.5 ;Front centre for degunk\nM109 S{material_print_temperature_layer_0} ;Wait for initial temp\nM83 ;E Relative\nG1 E10 F200 ;Degunk\nG1 E-3 F5000 ;Retract\nG0 Z3 ;Withdraw\nM82 ;E absolute\nG92 E0 ;E reset\nG1 F6000 ;Set feedrate\n\n;Finish Start Gcode for Dagoma DiscoEasy 200\n" }, "machine_end_gcode": { - "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 F3000\nG90\nG28 X Y\nM107\nM84\n" - }, - "default_material_print_temperature": { - "default_value": 205 - }, - "speed_print": { - "default_value": 60 - }, - "retraction_amount": { - "default_value": 3.5 - }, - "retraction_speed": { - "default_value": 50 - }, - "adhesion_type": { - "default_value": "skirt" - }, - "skirt_line_count": { - "default_value": 2 - }, - "layer_height_0": { - "default_value": 0.26 - }, - "top_bottom_thickness": { - "default_value": 1 + "default_value": ";Begin End Gcode for Dagoma DiscoEasy 200\n\nM106 S255 ;Fan on full\nM104 S0 ;Cool hotend\nM140 S0 ;Cool heated bed\nG91 ;Relative positioning\nG1 E-3 F5000 ;Retract filament to stop oozing\nG0 Z+3 ;Withdraw\nG90 ;Absolute positioning\nG28 X Y ;Home\nM109 R{material_standby_temperature} ;Wait until head has cooled to standby temp\nM107 ;Fan off\nM18 ;Stepper motors off\n\n;Finish End Gcode for Dagoma DiscoEasy 200\n" } } } diff --git a/resources/definitions/dagoma_discoeasy200_bicolor.def.json b/resources/definitions/dagoma_discoeasy200_bicolor.def.json new file mode 100644 index 0000000000..4786c03fc2 --- /dev/null +++ b/resources/definitions/dagoma_discoeasy200_bicolor.def.json @@ -0,0 +1,38 @@ +{ + "name": "Dagoma DiscoEasy200 Bicolor", + "version": 2, + "inherits": "dagoma_disco", + "metadata": { + "visible": true, + "author": "Dagoma", + "manufacturer": "Dagoma", + "file_formats": "text/x-gcode", + "platform": "dagoma_discoeasy200_bicolor.3mf", + "platform_offset": [0, -57.3, -11], + "has_machine_quality": true, + "has_materials": true, + "preferred_material": "chromatik_pla", + "machine_extruder_trains": + { + "0": "dagoma_discoeasy200_extruder_0", + "1": "dagoma_discoeasy200_extruder_1" + } + }, + "overrides": { + "machine_name": { + "default_value": "Dagoma DiscoEasy200 Bicolor" + }, + "machine_extruder_count": { + "default_value": 2 + }, + "machine_extruders_share_heater": { + "default_value": true + }, + "machine_start_gcode": { + "default_value": ";Begin Start Gcode for Dagoma DiscoEasy 200 Bicolor\n;Sliced: {date} {time}\n;Initial extruder: {initial_extruder_nr}\n\nG90 ;Absolute positioning\nM106 S255 ;Fan on full\nG28 X Y ;Home stop X Y\nG1 X100 ;Centre back during cooldown in case of oozing\nM109 R{material_standby_temperature} ;Cooldown in case too hot\nG28 ;Centre\nG29 ;Auto-level\nM104 S{material_print_temperature_layer_0} ;Pre-heat\nM107 ;Fan off\nG0 X100 Y5 Z0.5 ;Front centre for degunk\nM109 S{material_print_temperature_layer_0} ;Wait for initial temp\n;M83 ;E Relative\n;G1 E60 F3000 ;Reverse multi-extruder retract\n;G1 E10 F200 ;Degunk\n;G1 E-3 F5000 ;Retract\nG0 Z3 ;Withdraw\n;M82 ;E absolute\n;G92 E0 ;E reset\n;G1 F6000 ;Set feedrate\n\n;Finish Start Gcode for Dagoma DiscoEasy 200 Bicolor\n" + }, + "machine_end_gcode": { + "default_value": ";Begin End Gcode for Dagoma DiscoEasy 200 Bicolor\n\nM106 S255 ;Fan on full\nM104 S0 ;Cool hotend\nM140 S0 ;Cool heated bed\nG91 ;Relative positioning\nG1 E-3 F5000 ;Retract filament to stop oozing\nG1 E-60 F5000 ;Retract filament multi-extruder\nG0 Z+3 ;Withdraw\nG90 ; Absolute positioning\nG28 X Y ;Home\nM109 R{material_standby_temperature} ;Wait until head has cooled to standby temp\nM107 ;Fan off\nM18 ;Stepper motors off\n\n;Finish End Gcode for Dagoma DiscoEasy 200 Bicolor\n" + } + } +} diff --git a/resources/definitions/dagoma_discoultimate.def.json b/resources/definitions/dagoma_discoultimate.def.json index 13ab591212..ec41318e86 100644 --- a/resources/definitions/dagoma_discoultimate.def.json +++ b/resources/definitions/dagoma_discoultimate.def.json @@ -1,82 +1,34 @@ { "name": "Dagoma DiscoUltimate", "version": 2, - "inherits": "fdmprinter", + "inherits": "dagoma_disco", "metadata": { "visible": true, "author": "Dagoma", "manufacturer": "Dagoma", "file_formats": "text/x-gcode", "platform": "dagoma_discoultimate.3mf", - "platform_offset": [0, -58.5, -11], + "platform_offset": [0, -58.5, -39.5], "has_machine_quality": true, "has_materials": true, "preferred_material": "chromatik_pla", "machine_extruder_trains": { - "0": "dagoma_discoultimate_extruder_0", - "1": "dagoma_discoultimate_extruder_1" + "0": "dagoma_discoultimate_extruder" } }, "overrides": { + "machine_name": { + "default_value": "Dagoma DiscoUltimate" + }, "machine_extruder_count": { - "default_value": 2 - }, - "machine_extruders_share_heater": { - "default_value": true - }, - "machine_width": { - "default_value": 205 - }, - "machine_height": { - "default_value": 205 - }, - "machine_depth": { - "default_value": 205 - }, - "machine_center_is_zero": { - "default_value": false - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [-17, -70], - [-17, 40], - [17, 40], - [17, -70] - ] - }, - "gantry_height": { - "value": "10" + "default_value": 1 }, "machine_start_gcode": { - "default_value": ";Gcode by Cura\nG90\nM106 S255\nG28 X Y\nG1 X50\nM109 R90\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n" + "default_value": ";Begin Start Gcode for Dagoma DiscoUltimate\n;Sliced: {date} {time}\n;Initial extruder: {initial_extruder_nr}\n\nG90 ;Absolute positioning\nM106 S255 ;Fan on full\nG28 X Y ;Home stop X Y\nG1 X100 ;Centre back during cooldown in case of oozing\nM109 R{material_standby_temperature} ;Cooldown in case too hot\nG28 ;Centre\nG29 ;Auto-level\nM104 S{material_print_temperature_layer_0} ;Pre-heat\nM107 ;Fan off\nG0 X100 Y5 Z0.5 ;Front centre for degunk\nM109 S{material_print_temperature_layer_0} ;Wait for initial temp\nM83 ;E Relative\nG1 E10 F200 ;Degunk\nG1 E-3 F5000 ;Retract\nG0 Z3 ;Withdraw\nM82 ;E absolute\nG92 E0 ;E reset\nG1 F6000 ;Set feedrate\n\n;Finish Start Gcode for Dagoma DiscoUltimate\n" }, "machine_end_gcode": { - "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 F3000\nG90\nG28 X Y\nM107\nM84\n" - }, - "default_material_print_temperature": { - "default_value": 205 - }, - "speed_print": { - "default_value": 60 - }, - "retraction_amount": { - "default_value": 3.5 - }, - "retraction_speed": { - "default_value": 50 - }, - "adhesion_type": { - "default_value": "skirt" - }, - "skirt_line_count": { - "default_value": 2 - }, - "layer_height_0": { - "default_value": 0.26 - }, - "top_bottom_thickness": { - "default_value": 1 + "default_value": ";Begin End Gcode for Dagoma DiscoUltimate\n\nM106 S255 ;Fan on full\nM104 S0 ;Cool hotend\nM140 S0 ;Cool heated bed\nG91 ;Relative positioning\nG1 E-3 F5000 ;Retract filament to stop oozing\nG0 Z+3 ;Withdraw\nG90 ;Absolute positioning\nG28 X Y ;Home\nM109 R{material_standby_temperature} ;Wait until head has cooled to standby temp\nM107 ;Fan off\nM18 ;Stepper motors off\n\n;Finish End Gcode for Dagoma DiscoUltimate\n" } } } diff --git a/resources/definitions/dagoma_discoultimate_bicolor.def.json b/resources/definitions/dagoma_discoultimate_bicolor.def.json new file mode 100644 index 0000000000..3b5215c944 --- /dev/null +++ b/resources/definitions/dagoma_discoultimate_bicolor.def.json @@ -0,0 +1,38 @@ +{ + "name": "Dagoma DiscoUltimate Bicolor", + "version": 2, + "inherits": "dagoma_disco", + "metadata": { + "visible": true, + "author": "Dagoma", + "manufacturer": "Dagoma", + "file_formats": "text/x-gcode", + "platform": "dagoma_discoultimate_bicolor.3mf", + "platform_offset": [0, -58.5, -11], + "has_machine_quality": true, + "has_materials": true, + "preferred_material": "chromatik_pla", + "machine_extruder_trains": + { + "0": "dagoma_discoultimate_extruder_0", + "1": "dagoma_discoultimate_extruder_1" + } + }, + "overrides": { + "machine_name": { + "default_value": "Dagoma DiscoUltimate Bicolor" + }, + "machine_extruder_count": { + "default_value": 2 + }, + "machine_extruders_share_heater": { + "default_value": true + }, + "machine_start_gcode": { + "default_value": ";Begin Start Gcode for Dagoma DiscoUltimate Bicolor\n;Sliced: {date} {time}\n;Initial extruder: {initial_extruder_nr}\n\nG90 ;Absolute positioning\nM106 S255 ;Fan on full\nG28 X Y ;Home stop X Y\nG1 X100 ;Centre back during cooldown in case of oozing\nM109 R{material_standby_temperature} ;Cooldown in case too hot\nG28 ;Centre\nG29 ;Auto-level\nM104 S{material_print_temperature_layer_0} ;Pre-heat\nM107 ;Fan off\nG0 X100 Y5 Z0.5 ;Front centre for degunk\nM109 S{material_print_temperature_layer_0} ;Wait for initial temp\n;M83 ;E Relative\n;G1 E60 F3000 ;Reverse multi-extruder retract\n;G1 E10 F200 ;Degunk\n;G1 E-3 F5000 ;Retract\nG0 Z3 ;Withdraw\n;M82 ;E absolute\n;G92 E0 ;E reset\n;G1 F6000 ;Set feedrate\n\n;Finish Start Gcode for Dagoma DiscoUltimate Bicolor\n" + }, + "machine_end_gcode": { + "default_value": ";Begin End Gcode for Dagoma DiscoUltimate Bicolor\n\nM106 S255 ;Fan on full\nM104 S0 ;Cool hotend\nM140 S0 ;Cool heated bed\nG91 ;Relative positioning\nG1 E-3 F5000 ;Retract filament to stop oozing\nG1 E-60 F5000 ;Retract filament multi-extruder\nG0 Z+3 ;Withdraw\nG90 ; Absolute positioning\nG28 X Y ;Home\nM109 R{material_standby_temperature} ;Wait until head has cooled to standby temp\nM107 ;Fan off\nM18 ;Stepper motors off\n\n;Finish End Gcode for Dagoma DiscoUltimate Bicolor\n" + } + } +} diff --git a/resources/definitions/dagoma_magis.def.json b/resources/definitions/dagoma_magis.def.json index e7c0847e7e..e3c9c3e693 100644 --- a/resources/definitions/dagoma_magis.def.json +++ b/resources/definitions/dagoma_magis.def.json @@ -1,7 +1,7 @@ { "name": "Dagoma Magis", "version": 2, - "inherits": "fdmprinter", + "inherits": "dagoma_delta", "metadata": { "visible": true, "author": "Dagoma", @@ -14,65 +14,12 @@ "preferred_material": "chromatik_pla", "machine_extruder_trains": { - "0": "dagoma_magis_extruder_0" + "0": "dagoma_magis_extruder" } }, "overrides": { - "machine_width": { - "default_value": 195.55 - }, - "machine_height": { - "default_value": 205 - }, - "machine_depth": { - "default_value": 195.55 - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [-36, -42], - [-36, 42], - [36, 42], - [36, -42] - ] - }, - "gantry_height": { - "value": "0" - }, - "machine_shape": { - "default_value": "elliptic" - }, - "machine_start_gcode": { - "default_value": ";Gcode by Cura\nG90\nG28\nM107\nM109 R100\nG29\nM109 S{material_print_temperature_layer_0} U-55 X55 V-85 Y-85 W0.26 Z0.26\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n" - }, - "machine_end_gcode": { - "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n" - }, - "default_material_print_temperature": { - "default_value": 205 - }, - "speed_print": { - "default_value": 40 - }, - "retraction_amount": { - "default_value": 3.8 - }, - "retraction_speed": { - "default_value": 60 - }, - "adhesion_type": { - "default_value": "skirt" - }, - "skirt_line_count": { - "default_value": 2 - }, - "layer_height_0": { - "default_value": 0.26 - }, - "top_bottom_thickness": { - "default_value": 1 + "machine_name": { + "default_value": "Dagoma Magis" } } } diff --git a/resources/definitions/dagoma_neva.def.json b/resources/definitions/dagoma_neva.def.json index f6a6ccf511..24d3f8ab8d 100644 --- a/resources/definitions/dagoma_neva.def.json +++ b/resources/definitions/dagoma_neva.def.json @@ -1,7 +1,7 @@ { - "name": "Dagoma NEVA", + "name": "Dagoma Neva", "version": 2, - "inherits": "fdmprinter", + "inherits": "dagoma_delta", "metadata": { "visible": true, "author": "Dagoma", @@ -14,65 +14,12 @@ "preferred_material": "chromatik_pla", "machine_extruder_trains": { - "0": "dagoma_neva_extruder_0" + "0": "dagoma_neva_extruder" } }, "overrides": { - "machine_width": { - "default_value": 195.55 - }, - "machine_height": { - "default_value": 205 - }, - "machine_depth": { - "default_value": 195.55 - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [-36, -42], - [-36, 42], - [36, 42], - [36, -42] - ] - }, - "gantry_height": { - "value": "0" - }, - "machine_shape": { - "default_value": "elliptic" - }, - "machine_start_gcode": { - "default_value": ";Gcode by Cura\nG90\nG28\nM107\nM109 R100\nG29\nM109 S{material_print_temperature_layer_0} U-55 X55 V-85 Y-85 W0.26 Z0.26\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n" - }, - "machine_end_gcode": { - "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n" - }, - "default_material_print_temperature": { - "default_value": 205 - }, - "speed_print": { - "default_value": 40 - }, - "retraction_amount": { - "default_value": 3.8 - }, - "retraction_speed": { - "default_value": 60 - }, - "adhesion_type": { - "default_value": "skirt" - }, - "skirt_line_count": { - "default_value": 2 - }, - "layer_height_0": { - "default_value": 0.26 - }, - "top_bottom_thickness": { - "default_value": 1 + "machine_name": { + "default_value": "Dagoma Neva" } } } diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 85d18078e9..f863d78b3a 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2082,7 +2082,7 @@ "description": "Skin areas narrower than this are not expanded. This avoids expanding the narrow skin areas that are created when the model surface has a slope close to the vertical.", "unit": "mm", "type": "float", - "default_value": 2.24, + "default_value": 0, "value": "top_layers * layer_height / math.tan(math.radians(max_skin_angle_for_expansion))", "minimum_value": "0", "enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)", diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index 10359f2fe6..34d22934d6 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -55,7 +55,7 @@ "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 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 Y50 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { "value": "'M104 S0 ;extruder heater off' + ('\\nM140 S0 ;heated bed heater off' if machine_heated_bed else '') + '\\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/extruders/dagoma_discoeasy200_extruder.def.json b/resources/extruders/dagoma_discoeasy200_extruder.def.json new file mode 100644 index 0000000000..7bef0a7649 --- /dev/null +++ b/resources/extruders/dagoma_discoeasy200_extruder.def.json @@ -0,0 +1,21 @@ +{ + "version": 2, + "name": "Extruder", + "inherits": "fdmextruder", + "metadata": { + "machine": "dagoma_discoeasy200", + "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/dagoma_discoeasy200_extruder_0.def.json b/resources/extruders/dagoma_discoeasy200_extruder_0.def.json index f2ca729582..af1c2c42e9 100644 --- a/resources/extruders/dagoma_discoeasy200_extruder_0.def.json +++ b/resources/extruders/dagoma_discoeasy200_extruder_0.def.json @@ -3,7 +3,7 @@ "name": "Extruder 1", "inherits": "fdmextruder", "metadata": { - "machine": "dagoma_discoeasy200", + "machine": "dagoma_discoeasy200_bicolor", "position": "0" }, @@ -18,10 +18,10 @@ "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;Start T0\nG92 E0\nG1 E-{retraction_amount} F10000\nG92 E0G1 E1.5 F3000\nG1 E-60 F10000\nG92 E0\n" + "default_value": ";Start T0\nM83\nG1 E{retraction_amount} F3000\nG1 E60 F3000\nG1 E-{retraction_amount} F5000\nM82\nG92 E0" }, "machine_extruder_end_code": { - "default_value": "\nG92 E0\nG1 E{retraction_amount} F3000\nG92 E0\nG1 E60 F3000\nG92 E0\nG1 E-{retraction_amount} F5000\n;End T0\n\n" + "default_value": "M83\nG1 E-{retraction_amount} F10000\nG1 E1.5 F3000\nG1 E-60 F10000\nM82\nG92 E0\n;End T0" } } } diff --git a/resources/extruders/dagoma_discoeasy200_extruder_1.def.json b/resources/extruders/dagoma_discoeasy200_extruder_1.def.json index ac5fe5015d..f01a805270 100644 --- a/resources/extruders/dagoma_discoeasy200_extruder_1.def.json +++ b/resources/extruders/dagoma_discoeasy200_extruder_1.def.json @@ -3,7 +3,7 @@ "name": "Extruder 2", "inherits": "fdmextruder", "metadata": { - "machine": "dagoma_discoeasy200", + "machine": "dagoma_discoeasy200_bicolor", "position": "1" }, @@ -18,10 +18,10 @@ "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;Start T1\nG92 E0\nG1 E-{retraction_amount} F10000\nG92 E0G1 E1.5 F3000\nG1 E-60 F10000\nG92 E0\n" + "default_value": ";Start T1\nM83\nG1 E{retraction_amount} F3000\nG1 E60 F3000\nG1 E-{retraction_amount} F5000\nM82\nG92 E0" }, "machine_extruder_end_code": { - "default_value": "\nG92 E0\nG1 E{retraction_amount} F3000\nG92 E0\nG1 E60 F3000\nG92 E0\nG1 E-{retraction_amount} F5000\n;End T1\n\n" + "default_value": "M83\nG1 E-{retraction_amount} F10000\nG1 E1.5 F3000\nG1 E-60 F10000\nM82\nG92 E0\n;End T1" } } } diff --git a/resources/extruders/dagoma_discoultimate_extruder.def.json b/resources/extruders/dagoma_discoultimate_extruder.def.json new file mode 100644 index 0000000000..a53145f429 --- /dev/null +++ b/resources/extruders/dagoma_discoultimate_extruder.def.json @@ -0,0 +1,21 @@ +{ + "version": 2, + "name": "Extruder", + "inherits": "fdmextruder", + "metadata": { + "machine": "dagoma_discoultimate", + "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/dagoma_discoultimate_extruder_0.def.json b/resources/extruders/dagoma_discoultimate_extruder_0.def.json index 878d6fdc75..766d5b2902 100644 --- a/resources/extruders/dagoma_discoultimate_extruder_0.def.json +++ b/resources/extruders/dagoma_discoultimate_extruder_0.def.json @@ -3,7 +3,7 @@ "name": "Extruder 1", "inherits": "fdmextruder", "metadata": { - "machine": "dagoma_discoultimate", + "machine": "dagoma_discoultimate_bicolor", "position": "0" }, @@ -18,10 +18,10 @@ "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;Start T0\nG92 E0\nG1 E-{retraction_amount} F10000\nG92 E0G1 E1.5 F3000\nG1 E-60 F10000\nG92 E0\n" + "default_value": ";Start T0\nM83\nG1 E{retraction_amount} F3000\nG1 E60 F3000\nG1 E-{retraction_amount} F5000\nM82\nG92 E0" }, "machine_extruder_end_code": { - "default_value": "\nG92 E0\nG1 E{retraction_amount} F3000\nG92 E0\nG1 E60 F3000\nG92 E0\nG1 E-{retraction_amount} F5000\n;End T0\n\n" + "default_value": "M83\nG1 E-{retraction_amount} F10000\nG1 E1.5 F3000\nG1 E-60 F10000\nM82\nG92 E0\n;End T0" } } } diff --git a/resources/extruders/dagoma_discoultimate_extruder_1.def.json b/resources/extruders/dagoma_discoultimate_extruder_1.def.json index e6f8031e03..f71b875261 100644 --- a/resources/extruders/dagoma_discoultimate_extruder_1.def.json +++ b/resources/extruders/dagoma_discoultimate_extruder_1.def.json @@ -3,7 +3,7 @@ "name": "Extruder 2", "inherits": "fdmextruder", "metadata": { - "machine": "dagoma_discoultimate", + "machine": "dagoma_discoultimate_bicolor", "position": "1" }, @@ -18,10 +18,10 @@ "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;Start T1\nG92 E0\nG1 E-{retraction_amount} F10000\nG92 E0G1 E1.5 F3000\nG1 E-60 F10000\nG92 E0\n" + "default_value": ";Start T1\nM83\nG1 E{retraction_amount} F3000\nG1 E60 F3000\nG1 E-{retraction_amount} F5000\nM82\nG92 E0" }, "machine_extruder_end_code": { - "default_value": "\nG92 E0\nG1 E{retraction_amount} F3000\nG92 E0\nG1 E60 F3000\nG92 E0\nG1 E-{retraction_amount} F5000\n;End T1\n\n" + "default_value": "M83\nG1 E-{retraction_amount} F10000\nG1 E1.5 F3000\nG1 E-60 F10000\nM82\nG92 E0\n;End T1" } } } diff --git a/resources/extruders/dagoma_magis_extruder_0.def.json b/resources/extruders/dagoma_magis_extruder.def.json similarity index 93% rename from resources/extruders/dagoma_magis_extruder_0.def.json rename to resources/extruders/dagoma_magis_extruder.def.json index 0a5850f2ed..06cf6127ce 100644 --- a/resources/extruders/dagoma_magis_extruder_0.def.json +++ b/resources/extruders/dagoma_magis_extruder.def.json @@ -1,6 +1,6 @@ { "version": 2, - "name": "Extruder 1", + "name": "Extruder", "inherits": "fdmextruder", "metadata": { "machine": "dagoma_magis", diff --git a/resources/extruders/dagoma_neva_extruder_0.def.json b/resources/extruders/dagoma_neva_extruder.def.json similarity index 93% rename from resources/extruders/dagoma_neva_extruder_0.def.json rename to resources/extruders/dagoma_neva_extruder.def.json index 95035f63f2..3fa26ab5c9 100644 --- a/resources/extruders/dagoma_neva_extruder_0.def.json +++ b/resources/extruders/dagoma_neva_extruder.def.json @@ -1,6 +1,6 @@ { "version": 2, - "name": "Extruder 1", + "name": "Extruder", "inherits": "fdmextruder", "metadata": { "machine": "dagoma_neva", diff --git a/resources/meshes/dagoma_discoeasy200.3mf b/resources/meshes/dagoma_discoeasy200.3mf index 9c2c674cfc..d71b5da20a 100644 Binary files a/resources/meshes/dagoma_discoeasy200.3mf and b/resources/meshes/dagoma_discoeasy200.3mf differ diff --git a/resources/meshes/dagoma_discoeasy200_bicolor.3mf b/resources/meshes/dagoma_discoeasy200_bicolor.3mf new file mode 100644 index 0000000000..9c2c674cfc Binary files /dev/null and b/resources/meshes/dagoma_discoeasy200_bicolor.3mf differ diff --git a/resources/meshes/dagoma_discoultimate.3mf b/resources/meshes/dagoma_discoultimate.3mf index 71b1e4dfba..f68fe2677c 100644 Binary files a/resources/meshes/dagoma_discoultimate.3mf and b/resources/meshes/dagoma_discoultimate.3mf differ diff --git a/resources/meshes/dagoma_discoultimate_bicolor.3mf b/resources/meshes/dagoma_discoultimate_bicolor.3mf new file mode 100644 index 0000000000..71b1e4dfba Binary files /dev/null and b/resources/meshes/dagoma_discoultimate_bicolor.3mf differ diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index ed2c6dc5fe..8ba651a5b0 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -84,6 +84,21 @@ UM.MainWindow CuraApplication.purgeWindows() } + Connections + { + // This connection is used when there is no ActiveMachine and the user is logged in + target: CuraApplication + onShowAddPrintersUncancellableDialog: + { + Cura.Actions.parent = backgroundItem + + // Reuse the welcome dialog item to show "Add a printer" only. + welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel() + welcomeDialogItem.progressBarVisible = false + welcomeDialogItem.visible = true + } + } + Connections { target: CuraApplication @@ -117,6 +132,15 @@ UM.MainWindow welcomeDialogItem.progressBarVisible = false welcomeDialogItem.visible = true } + + // Reuse the welcome dialog item to show the "Add printers" dialog. Triggered when there is no active + // machine and the user is logged in. + if (!Cura.MachineManager.activeMachine && Cura.API.account.isLoggedIn) + { + welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel() + welcomeDialogItem.progressBarVisible = false + welcomeDialogItem.visible = true + } } } diff --git a/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml b/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml index b7fe022d78..a5ee7b5986 100644 --- a/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml +++ b/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml @@ -14,8 +14,8 @@ UM.Dialog id: base title: catalog.i18nc("@title:window", "Discard or Keep changes") - width: UM.Theme.getSize("popup_dialog").width - height: UM.Theme.getSize("popup_dialog").height + minimumWidth: UM.Theme.getSize("popup_dialog").width + minimumHeight: UM.Theme.getSize("popup_dialog").height property var changesModel: Cura.UserChangesModel{ id: userChangesModel} onVisibilityChanged: { @@ -80,6 +80,8 @@ UM.Dialog property var extruder_name: userChangesModel.getItem(styleData.row).extruder anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.right: parent.right + elide: Text.ElideRight font: UM.Theme.getFont("system") text: { diff --git a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml index 6fe9607274..670766204f 100644 --- a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml +++ b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml @@ -143,7 +143,7 @@ UM.Dialog { width: parent.width height: childrenRect.height - model: Cura.MachineManager.activeMachine.extruderList + model: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.extruderList : null delegate: Column { height: childrenRect.height diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 9891fc1d69..cb498bcef0 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -33,7 +33,7 @@ Cura.ExpandablePopup } contentPadding: UM.Theme.getSize("default_lining").width - enabled: Cura.MachineManager.activeMachine.hasMaterials || Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates; //Only let it drop down if there is any configuration that you could change. + enabled: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasMaterials || Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates : false; //Only let it drop down if there is any configuration that you could change. headerItem: Item { @@ -46,7 +46,7 @@ Cura.ExpandablePopup model: extrudersModel delegate: Item { - Layout.fillWidth: true + Layout.preferredWidth: Math.round(parent.width / extrudersModel.count) Layout.fillHeight: true // Extruder icon. Shows extruder index and has the same color as the active material. @@ -84,7 +84,7 @@ Cura.ExpandablePopup { id: variantLabel - visible: Cura.MachineManager.activeMachine.hasVariants + visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasVariants : false text: model.variant elide: Text.ElideRight @@ -114,7 +114,7 @@ Cura.ExpandablePopup color: UM.Theme.getColor("text") renderType: Text.NativeRendering - visible: !Cura.MachineManager.activeMachine.hasMaterials && (Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates) + visible: Cura.MachineManager.activeMachine ? !Cura.MachineManager.activeMachine.hasMaterials && (Cura.MachineManager.activeMachine.hasVariants || Cura.MachineManager.activeMachine.hasVariantBuildplates) : false anchors { diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 65f5bcce8c..010e2e77b0 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -244,7 +244,7 @@ Item Row { height: visible ? UM.Theme.getSize("print_setup_big_item").height : 0 - visible: Cura.MachineManager.activeMachine.hasMaterials + visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasMaterials : false Label { @@ -305,7 +305,7 @@ Item Row { height: visible ? UM.Theme.getSize("print_setup_big_item").height : 0 - visible: Cura.MachineManager.activeMachine.hasVariants + visible: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.hasVariants : false Label { diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index f227dddaf9..92f0024b23 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -130,7 +130,11 @@ Item target: extruderModel onModelChanged: { - supportExtruderCombobox.color = supportExtruderCombobox.model.getItem(supportExtruderCombobox.currentIndex).color + var maybeColor = supportExtruderCombobox.model.getItem(supportExtruderCombobox.currentIndex).color + if (maybeColor) + { + supportExtruderCombobox.color = maybeColor + } } } onCurrentIndexChanged: diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 56a3d858ec..0907767eea 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -35,14 +35,21 @@ Cura.ExpandablePopup } } - readonly property string connectionStatusMessage: { + function getConnectionStatusMessage() { if (connectionStatus == "printer_cloud_not_available") { if(Cura.API.connectionStatus.isInternetReachable) { if (Cura.API.account.isLoggedIn) { - return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.") + if (Cura.MachineManager.activeMachineIsLinkedToCurrentAccount) + { + return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.") + } + else + { + return catalog.i18nc("@status", "This printer is not linked to your account. Please visit the Ultimaker Digital Factory to establish a connection.") + } } else { @@ -139,12 +146,13 @@ Cura.ExpandablePopup { id: connectionStatusTooltipHoverArea anchors.fill: parent - hoverEnabled: connectionStatusMessage !== "" + hoverEnabled: getConnectionStatusMessage() !== "" acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks onEntered: { machineSelector.mouseArea.entered() // we want both this and the outer area to be entered + tooltip.tooltipText = getConnectionStatusMessage() tooltip.show() } onExited: { tooltip.hide() } @@ -155,7 +163,7 @@ Cura.ExpandablePopup id: tooltip width: 250 * screenScaleFactor - tooltipText: connectionStatusMessage + tooltipText: getConnectionStatusMessage() arrowSize: UM.Theme.getSize("button_tooltip_arrow").width x: connectionStatusImage.x - UM.Theme.getSize("narrow_margin").width y: connectionStatusImage.y + connectionStatusImage.height + UM.Theme.getSize("narrow_margin").height diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index a7c041630f..18b1a68b20 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -28,11 +28,11 @@ ListView delegate: MachineSelectorButton { - text: model.name + text: model.name ? model.name : "" width: listView.width outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - checked: Cura.MachineManager.activeMachine.id == model.id + checked: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.id == model.id : false onClicked: { diff --git a/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fast.inst.cfg new file mode 100644 index 0000000000..8e126094cc --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fast.inst.cfg @@ -0,0 +1,26 @@ +[general] +version = 4 +name = Fast +definition = dagoma_discoeasy200_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = draft +weight = -2 +material = chromatik_pla + +[values] +layer_height = 0.2 +line_width = =machine_nozzle_size * 0.875 + +material_print_temperature = =default_material_print_temperature + 10 +material_bed_temperature_layer_0 = =default_material_bed_temperature + 10 + +speed_print = 60 +speed_travel = 75 +speed_layer_0 = 17 +speed_infill = 60 +speed_wall_0 = 50 +speed_wall_x = 60 +speed_topbottom = 60 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fine.inst.cfg new file mode 100644 index 0000000000..b26282ecb7 --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_fine.inst.cfg @@ -0,0 +1,23 @@ +[general] +version = 4 +name = Fine +definition = dagoma_discoeasy200_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = normal +weight = 0 +material = chromatik_pla + +[values] +layer_height = 0.1 +line_width = =machine_nozzle_size * 0.875 + +speed_print = 35 +speed_travel = 50 +speed_layer_0 = 15 +speed_infill = 40 +speed_wall_0 = 25 +speed_wall_x = 35 +speed_topbottom = 35 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_standard.inst.cfg new file mode 100644 index 0000000000..6bc4310ce4 --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoeasy200_bicolor_pla_standard.inst.cfg @@ -0,0 +1,26 @@ +[general] +version = 4 +name = Standard +definition = dagoma_discoeasy200_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = fast +weight = -1 +material = chromatik_pla + +[values] +layer_height = 0.15 +line_width = =machine_nozzle_size * 0.875 + +material_print_temperature = =default_material_print_temperature + 5 +material_bed_temperature_layer_0 = =default_material_bed_temperature + 5 + +speed_print = 50 +speed_travel = 60 +speed_layer_0 = 17 +speed_infill = 50 +speed_wall_0 = 40 +speed_wall_x = 45 +speed_topbottom = 50 diff --git a/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fast.inst.cfg new file mode 100644 index 0000000000..c08bd89d1e --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fast.inst.cfg @@ -0,0 +1,26 @@ +[general] +version = 4 +name = Fast +definition = dagoma_discoultimate_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = draft +weight = -2 +material = chromatik_pla + +[values] +layer_height = 0.2 +line_width = =machine_nozzle_size * 0.875 + +material_print_temperature = =default_material_print_temperature + 10 +material_bed_temperature_layer_0 = =default_material_bed_temperature + 10 + +speed_print = 60 +speed_travel = 75 +speed_layer_0 = 17 +speed_infill = 60 +speed_wall_0 = 50 +speed_wall_x = 60 +speed_topbottom = 60 diff --git a/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fine.inst.cfg new file mode 100644 index 0000000000..7795d6aaa9 --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_fine.inst.cfg @@ -0,0 +1,23 @@ +[general] +version = 4 +name = Fine +definition = dagoma_discoultimate_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = normal +weight = 0 +material = chromatik_pla + +[values] +layer_height = 0.1 +line_width = =machine_nozzle_size * 0.875 + +speed_print = 35 +speed_travel = 50 +speed_layer_0 = 15 +speed_infill = 40 +speed_wall_0 = 25 +speed_wall_x = 35 +speed_topbottom = 35 diff --git a/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_standard.inst.cfg new file mode 100644 index 0000000000..7015261d3b --- /dev/null +++ b/resources/quality/dagoma/dagoma_discoultimate_bicolor_pla_standard.inst.cfg @@ -0,0 +1,26 @@ +[general] +version = 4 +name = Standard +definition = dagoma_discoultimate_bicolor + +[metadata] +setting_version = 15 +type = quality +quality_type = fast +weight = -1 +material = chromatik_pla + +[values] +layer_height = 0.15 +line_width = =machine_nozzle_size * 0.875 + +material_print_temperature = =default_material_print_temperature + 5 +material_bed_temperature_layer_0 = =default_material_bed_temperature + 5 + +speed_print = 50 +speed_travel = 60 +speed_layer_0 = 17 +speed_infill = 50 +speed_wall_0 = 40 +speed_wall_x = 45 +speed_topbottom = 50