Merge remote-tracking branch 'origin/master' into doxygen_to_restructuredtext_comments

# Conflicts:
#	cura/API/__init__.py
#	cura/Settings/CuraContainerRegistry.py
#	cura/Settings/ExtruderManager.py
#	plugins/PostProcessingPlugin/scripts/PauseAtHeight.py
#	plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py
#	plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py
#	plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py
This commit is contained in:
Nino van Hooff 2020-05-28 17:31:24 +02:00
commit 58ffc9dcae
234 changed files with 3945 additions and 1142 deletions

View file

@ -23,6 +23,7 @@ class SyncState:
SYNCING = 0 SYNCING = 0
SUCCESS = 1 SUCCESS = 1
ERROR = 2 ERROR = 2
IDLE = 3
class Account(QObject): class Account(QObject):
"""The account API provides a version-proof bridge to use Ultimaker Accounts """The account API provides a version-proof bridge to use Ultimaker Accounts
@ -54,6 +55,7 @@ class Account(QObject):
""" """
lastSyncDateTimeChanged = pyqtSignal() lastSyncDateTimeChanged = pyqtSignal()
syncStateChanged = pyqtSignal(int) # because SyncState is an int Enum syncStateChanged = pyqtSignal(int) # because SyncState is an int Enum
manualSyncEnabledChanged = pyqtSignal(bool)
def __init__(self, application: "CuraApplication", parent = None) -> None: def __init__(self, application: "CuraApplication", parent = None) -> None:
super().__init__(parent) super().__init__(parent)
@ -62,7 +64,8 @@ class Account(QObject):
self._error_message = None # type: Optional[Message] self._error_message = None # type: Optional[Message]
self._logged_in = False self._logged_in = False
self._sync_state = SyncState.SUCCESS self._sync_state = SyncState.IDLE
self._manual_sync_enabled = False
self._last_sync_str = "-" self._last_sync_str = "-"
self._callback_port = 32118 self._callback_port = 32118
@ -110,16 +113,21 @@ class Account(QObject):
:param state: One of SyncState :param state: One of SyncState
""" """
Logger.info("Service {service} enters sync state {state}", service = service_name, state = state)
prev_state = self._sync_state prev_state = self._sync_state
self._sync_services[service_name] = state self._sync_services[service_name] = state
if any(val == SyncState.SYNCING for val in self._sync_services.values()): if any(val == SyncState.SYNCING for val in self._sync_services.values()):
self._sync_state = SyncState.SYNCING self._sync_state = SyncState.SYNCING
self._setManualSyncEnabled(False)
elif any(val == SyncState.ERROR for val in self._sync_services.values()): elif any(val == SyncState.ERROR for val in self._sync_services.values()):
self._sync_state = SyncState.ERROR self._sync_state = SyncState.ERROR
self._setManualSyncEnabled(True)
else: else:
self._sync_state = SyncState.SUCCESS self._sync_state = SyncState.SUCCESS
self._setManualSyncEnabled(False)
if self._sync_state != prev_state: if self._sync_state != prev_state:
self.syncStateChanged.emit(self._sync_state) self.syncStateChanged.emit(self._sync_state)
@ -162,11 +170,31 @@ class Account(QObject):
self._logged_in = logged_in self._logged_in = logged_in
self.loginStateChanged.emit(logged_in) self.loginStateChanged.emit(logged_in)
if logged_in: if logged_in:
self.sync() self._setManualSyncEnabled(False)
self._sync()
else: else:
if self._update_timer.isActive(): if self._update_timer.isActive():
self._update_timer.stop() self._update_timer.stop()
def _sync(self) -> None:
"""Signals all sync services to start syncing
This can be considered a forced sync: even when a
sync is currently running, a sync will be requested.
"""
if self._update_timer.isActive():
self._update_timer.stop()
elif self._sync_state == SyncState.SYNCING:
Logger.warning("Starting a new sync while previous sync was not completed\n{}", str(self._sync_services))
self.syncRequested.emit()
def _setManualSyncEnabled(self, enabled: bool) -> None:
if self._manual_sync_enabled != enabled:
self._manual_sync_enabled = enabled
self.manualSyncEnabledChanged.emit(enabled)
@pyqtSlot() @pyqtSlot()
@pyqtSlot(bool) @pyqtSlot(bool)
def login(self, force_logout_before_login: bool = False) -> None: def login(self, force_logout_before_login: bool = False) -> None:
@ -217,20 +245,23 @@ class Account(QObject):
def lastSyncDateTime(self) -> str: def lastSyncDateTime(self) -> str:
return self._last_sync_str return self._last_sync_str
@pyqtProperty(bool, notify=manualSyncEnabledChanged)
def manualSyncEnabled(self) -> bool:
return self._manual_sync_enabled
@pyqtSlot() @pyqtSlot()
def sync(self) -> None: @pyqtSlot(bool)
"""Signals all sync services to start syncing def sync(self, user_initiated: bool = False) -> None:
if user_initiated:
self._setManualSyncEnabled(False)
This can be considered a forced sync: even when a self._sync()
sync is currently running, a sync will be requested.
"""
if self._update_timer.isActive(): @pyqtSlot()
self._update_timer.stop() def popupOpened(self) -> None:
elif self._sync_state == SyncState.SYNCING: self._setManualSyncEnabled(True)
Logger.warning("Starting a new sync while previous sync was not completed\n{}", str(self._sync_services)) self._sync_state = SyncState.IDLE
self.syncStateChanged.emit(self._sync_state)
self.syncRequested.emit()
@pyqtSlot() @pyqtSlot()
def logout(self) -> None: def logout(self) -> None:

View file

@ -0,0 +1,64 @@
from typing import Optional
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty
from PyQt5.QtNetwork import QNetworkReply
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from cura.UltimakerCloud import UltimakerCloudAuthentication
class ConnectionStatus(QObject):
"""Status info for some web services"""
UPDATE_INTERVAL = 10.0 # seconds
ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/"
__instance = None # type: Optional[ConnectionStatus]
internetReachableChanged = pyqtSignal()
umCloudReachableChanged = pyqtSignal()
@classmethod
def getInstance(cls, *args, **kwargs) -> "ConnectionStatus":
if cls.__instance is None:
cls.__instance = cls(*args, **kwargs)
return cls.__instance
def __init__(self, parent: Optional["QObject"] = None):
super().__init__(parent)
self._http = HttpRequestManager.getInstance()
self._statuses = {
self.ULTIMAKER_CLOUD_STATUS_URL: True,
"http://example.com": True
}
# Create a timer for automatic updates
self._update_timer = QTimer()
self._update_timer.setInterval(int(self.UPDATE_INTERVAL * 1000))
# The timer is restarted automatically
self._update_timer.setSingleShot(False)
self._update_timer.timeout.connect(self._update)
self._update_timer.start()
@pyqtProperty(bool, notify=internetReachableChanged)
def isInternetReachable(self) -> bool:
# Is any of the test urls reachable?
return any(self._statuses.values())
def _update(self):
for url in self._statuses.keys():
self._http.get(
url = url,
callback = self._statusCallback,
error_callback = self._statusCallback,
timeout = 5
)
def _statusCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError = None):
url = reply.request().url().toString()
prev_statuses = self._statuses.copy()
self._statuses[url] = HttpRequestManager.replyIndicatesSuccess(reply, error)
if any(self._statuses.values()) != any(prev_statuses.values()):
self.internetReachableChanged.emit()

View file

@ -5,6 +5,7 @@ from typing import Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtProperty from PyQt5.QtCore import QObject, pyqtProperty
from cura.API.Backups import Backups from cura.API.Backups import Backups
from cura.API.ConnectionStatus import ConnectionStatus
from cura.API.Interface import Interface from cura.API.Interface import Interface
from cura.API.Account import Account from cura.API.Account import Account
@ -44,6 +45,9 @@ class CuraAPI(QObject):
self._backups = Backups(self._application) self._backups = Backups(self._application)
self._connectionStatus = ConnectionStatus()
# Interface API
self._interface = Interface(self._application) self._interface = Interface(self._application)
def initialize(self) -> None: def initialize(self) -> None:
@ -55,6 +59,10 @@ class CuraAPI(QObject):
return self._account return self._account
@pyqtProperty(QObject, constant = True)
def connectionStatus(self) -> "ConnectionStatus":
return self._connectionStatus
@property @property
def backups(self) -> "Backups": def backups(self) -> "Backups":
"""Backups API""" """Backups API"""

View file

@ -40,10 +40,10 @@ class CuraPackageManager(PackageManager):
machine_with_qualities = [] machine_with_qualities = []
for container_id in ids: for container_id in ids:
for global_stack in global_stacks: for global_stack in global_stacks:
for extruder_nr, extruder_stack in global_stack.extruders.items(): for extruder_nr, extruder_stack in enumerate(global_stack.extruderList):
if container_id in (extruder_stack.material.getId(), extruder_stack.material.getMetaData().get("base_file")): if container_id in (extruder_stack.material.getId(), extruder_stack.material.getMetaData().get("base_file")):
machine_with_materials.append((global_stack, extruder_nr, container_id)) machine_with_materials.append((global_stack, str(extruder_nr), container_id))
if container_id == extruder_stack.quality.getId(): if container_id == extruder_stack.quality.getId():
machine_with_qualities.append((global_stack, extruder_nr, container_id)) machine_with_qualities.append((global_stack, str(extruder_nr), container_id))
return machine_with_materials, machine_with_qualities return machine_with_materials, machine_with_qualities

View file

@ -156,9 +156,10 @@ class BaseMaterialsModel(ListModel):
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack.hasMaterials: if not global_stack.hasMaterials:
return # There are no materials for this machine, so nothing to do. return # There are no materials for this machine, so nothing to do.
extruder_stack = global_stack.extruders.get(str(self._extruder_position)) extruder_list = global_stack.extruderList
if not extruder_stack: if self._extruder_position > len(extruder_list):
return return
extruder_stack = extruder_list[self._extruder_position]
nozzle_name = extruder_stack.variant.getName() nozzle_name = extruder_stack.variant.getName()
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()] machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
if nozzle_name not in machine_node.variants: if nozzle_name not in machine_node.variants:

View file

@ -128,6 +128,7 @@ class DiscoveredPrintersModel(QObject):
self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter] self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter]
self._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin] self._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin]
self._network_plugin_queue = [] # type: List[OutputDevicePlugin]
self._manual_device_address = "" self._manual_device_address = ""
self._manual_device_request_timeout_in_seconds = 5 # timeout for adding a manual device in seconds self._manual_device_request_timeout_in_seconds = 5 # timeout for adding a manual device in seconds
@ -152,17 +153,22 @@ class DiscoveredPrintersModel(QObject):
all_plugins_dict = self._application.getOutputDeviceManager().getAllOutputDevicePlugins() all_plugins_dict = self._application.getOutputDeviceManager().getAllOutputDevicePlugins()
can_add_manual_plugins = [item for item in filter( self._network_plugin_queue = [item for item in filter(
lambda plugin_item: plugin_item.canAddManualDevice(address) in priority_order, lambda plugin_item: plugin_item.canAddManualDevice(address) in priority_order,
all_plugins_dict.values())] all_plugins_dict.values())]
if not can_add_manual_plugins: if not self._network_plugin_queue:
Logger.log("d", "Could not find a plugin to accept adding %s manually via address.", address) Logger.log("d", "Could not find a plugin to accept adding %s manually via address.", address)
return return
plugin = max(can_add_manual_plugins, key = lambda p: priority_order.index(p.canAddManualDevice(address))) self._attemptToAddManualDevice(address)
self._plugin_for_manual_device = plugin
self._plugin_for_manual_device.addManualDevice(address, callback = self._onManualDeviceRequestFinished) def _attemptToAddManualDevice(self, address: str) -> None:
if self._network_plugin_queue:
self._plugin_for_manual_device = self._network_plugin_queue.pop()
Logger.log("d", "Network plugin %s: attempting to add manual device with address %s.",
self._plugin_for_manual_device.getId(), address)
self._plugin_for_manual_device.addManualDevice(address, callback=self._onManualDeviceRequestFinished)
self._manual_device_address = address self._manual_device_address = address
self._manual_device_request_timer.start() self._manual_device_request_timer.start()
self.hasManualDeviceRequestInProgressChanged.emit() self.hasManualDeviceRequestInProgressChanged.emit()
@ -180,8 +186,11 @@ class DiscoveredPrintersModel(QObject):
self.manualDeviceRequestFinished.emit(False) self.manualDeviceRequestFinished.emit(False)
def _onManualRequestTimeout(self) -> None: def _onManualRequestTimeout(self) -> None:
Logger.log("w", "Manual printer [%s] request timed out. Cancel the current request.", self._manual_device_address) address = self._manual_device_address
Logger.log("w", "Manual printer [%s] request timed out. Cancel the current request.", address)
self.cancelCurrentManualDeviceRequest() self.cancelCurrentManualDeviceRequest()
if self._network_plugin_queue:
self._attemptToAddManualDevice(address)
hasManualDeviceRequestInProgressChanged = pyqtSignal() hasManualDeviceRequestInProgressChanged = pyqtSignal()
@ -197,6 +206,8 @@ class DiscoveredPrintersModel(QObject):
self._manual_device_address = "" self._manual_device_address = ""
self.hasManualDeviceRequestInProgressChanged.emit() self.hasManualDeviceRequestInProgressChanged.emit()
self.manualDeviceRequestFinished.emit(success) self.manualDeviceRequestFinished.emit(success)
if not success and self._network_plugin_queue:
self._attemptToAddManualDevice(address)
@pyqtProperty("QVariantMap", notify = discoveredPrintersChanged) @pyqtProperty("QVariantMap", notify = discoveredPrintersChanged)
def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]: def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]:

View file

@ -19,6 +19,7 @@ class GlobalStacksModel(ListModel):
ConnectionTypeRole = Qt.UserRole + 4 ConnectionTypeRole = Qt.UserRole + 4
MetaDataRole = Qt.UserRole + 5 MetaDataRole = Qt.UserRole + 5
DiscoverySourceRole = Qt.UserRole + 6 # For separating local and remote printers in the machine management page DiscoverySourceRole = Qt.UserRole + 6 # For separating local and remote printers in the machine management page
RemovalWarningRole = Qt.UserRole + 7
def __init__(self, parent = None) -> None: def __init__(self, parent = None) -> None:
super().__init__(parent) super().__init__(parent)
@ -66,13 +67,21 @@ class GlobalStacksModel(ListModel):
if parseBool(container_stack.getMetaDataEntry("hidden", False)): if parseBool(container_stack.getMetaDataEntry("hidden", False)):
continue continue
section_name = "Network enabled printers" if has_remote_connection else "Local printers" device_name = container_stack.getMetaDataEntry("group_name", container_stack.getName())
section_name = "Connected printers" if has_remote_connection else "Preset printers"
section_name = self._catalog.i18nc("@info:title", section_name) section_name = self._catalog.i18nc("@info:title", section_name)
items.append({"name": container_stack.getMetaDataEntry("group_name", container_stack.getName()), default_removal_warning = self._catalog.i18nc(
"@label ({} is object name)",
"Are you sure you wish to remove {}? This cannot be undone!", device_name
)
removal_warning = container_stack.getMetaDataEntry("removal_warning", default_removal_warning)
items.append({"name": device_name,
"id": container_stack.getId(), "id": container_stack.getId(),
"hasRemoteConnection": has_remote_connection, "hasRemoteConnection": has_remote_connection,
"metadata": container_stack.getMetaData().copy(), "metadata": container_stack.getMetaData().copy(),
"discoverySource": section_name}) "discoverySource": section_name,
items.sort(key = lambda i: (not i["hasRemoteConnection"], i["name"])) "removalWarning": removal_warning})
items.sort(key=lambda i: (not i["hasRemoteConnection"], i["name"]))
self.setItems(items) self.setItems(items)

View file

@ -151,7 +151,7 @@ class QualitySettingsModel(ListModel):
if self._selected_position == self.GLOBAL_STACK_POSITION: if self._selected_position == self.GLOBAL_STACK_POSITION:
user_value = global_container_stack.userChanges.getProperty(definition.key, "value") user_value = global_container_stack.userChanges.getProperty(definition.key, "value")
else: else:
extruder_stack = global_container_stack.extruders[str(self._selected_position)] extruder_stack = global_container_stack.extruderList[self._selected_position]
user_value = extruder_stack.userChanges.getProperty(definition.key, "value") user_value = extruder_stack.userChanges.getProperty(definition.key, "value")
if profile_value is None and user_value is None: if profile_value is None and user_value is None:

View file

@ -241,7 +241,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._node is None: if self._node is None:
return None return None
if self._node.callDecoration("isGroup"): if self._node.callDecoration("isGroup"):
points = numpy.zeros((0, 2), dtype=numpy.int32) points = numpy.zeros((0, 2), dtype = numpy.int32)
for child in self._node.getChildren(): for child in self._node.getChildren():
child_hull = child.callDecoration("_compute2DConvexHull") child_hull = child.callDecoration("_compute2DConvexHull")
if child_hull: if child_hull:
@ -285,7 +285,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
# Do not throw away vertices: the convex hull may be too small and objects can collide. # Do not throw away vertices: the convex hull may be too small and objects can collide.
# vertex_data = vertex_data[vertex_data[:,1] >= -0.01] # vertex_data = vertex_data[vertex_data[:,1] >= -0.01]
if len(vertex_data) >= 4: # type: ignore # mypy and numpy don't play along well just yet. if vertex_data is not None and len(vertex_data) >= 4: # type: ignore # mypy and numpy don't play along well just yet.
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
# This is done to greatly speed up further convex hull calculations as the convex hull # This is done to greatly speed up further convex hull calculations as the convex hull
# becomes much less complex when dealing with highly detailed models. # becomes much less complex when dealing with highly detailed models.

View file

@ -192,9 +192,7 @@ class CuraContainerRegistry(ContainerRegistry):
return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)} return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)}
container_tree = ContainerTree.getInstance() container_tree = ContainerTree.getInstance()
machine_extruders = [] machine_extruders = global_stack.extruderList
for position in sorted(global_stack.extruders):
machine_extruders.append(global_stack.extruders[position])
plugin_registry = PluginRegistry.getInstance() plugin_registry = PluginRegistry.getInstance()
extension = file_name.split(".")[-1] extension = file_name.split(".")[-1]
@ -275,7 +273,7 @@ class CuraContainerRegistry(ContainerRegistry):
if len(profile_or_list) == 1: if len(profile_or_list) == 1:
global_profile = profile_or_list[0] global_profile = profile_or_list[0]
extruder_profiles = [] extruder_profiles = []
for idx, extruder in enumerate(global_stack.extruders.values()): for idx, extruder in enumerate(global_stack.extruderList):
profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1)) profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
profile = InstanceContainer(profile_id) profile = InstanceContainer(profile_id)
profile.setName(quality_name) profile.setName(quality_name)

View file

@ -65,7 +65,7 @@ class CuraStackBuilder:
except IndexError: except IndexError:
return None return None
for new_extruder in new_global_stack.extruders.values(): # Only register the extruders if we're sure that all of them are correct. for new_extruder in new_global_stack.extruderList: # Only register the extruders if we're sure that all of them are correct.
registry.addContainer(new_extruder) registry.addContainer(new_extruder)
# Register the global stack after the extruder stacks are created. This prevents the registry from adding another # Register the global stack after the extruder stacks are created. This prevents the registry from adding another

View file

@ -154,25 +154,6 @@ class ExtruderManager(QObject):
return self._extruder_trains[global_container_stack.getId()][str(index)] return self._extruder_trains[global_container_stack.getId()][str(index)]
return None return None
def registerExtruder(self, extruder_train: "ExtruderStack", machine_id: str) -> None:
changed = False
if machine_id not in self._extruder_trains:
self._extruder_trains[machine_id] = {}
changed = True
# do not register if an extruder has already been registered at the position on this machine
if any(item.getId() == extruder_train.getId() for item in self._extruder_trains[machine_id].values()):
Logger.log("w", "Extruder [%s] has already been registered on machine [%s], not doing anything",
extruder_train.getId(), machine_id)
return
if extruder_train:
self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
changed = True
if changed:
self.extrudersChanged.emit(machine_id)
def getAllExtruderSettings(self, setting_key: str, prop: str) -> List[Any]: def getAllExtruderSettings(self, setting_key: str, prop: str) -> List[Any]:
"""Gets a property of a setting for all extruders. """Gets a property of a setting for all extruders.

View file

@ -45,9 +45,6 @@ class ExtruderStack(CuraContainerStack):
stack.addExtruder(self) stack.addExtruder(self)
self.setMetaDataEntry("machine", stack.id) self.setMetaDataEntry("machine", stack.id)
# For backward compatibility: Register the extruder with the Extruder Manager
ExtruderManager.getInstance().registerExtruder(self, stack.id)
@override(ContainerStack) @override(ContainerStack)
def getNextStack(self) -> Optional["GlobalStack"]: def getNextStack(self) -> Optional["GlobalStack"]:
return super().getNextStack() return super().getNextStack()

View file

@ -500,6 +500,10 @@ class MachineManager(QObject):
# A cloud connection is only available if any output device actually is a cloud connected device. # A cloud connection is only available if any output device actually is a cloud connected device.
return any(d.connectionType == ConnectionType.CloudConnection for d in self._printer_output_devices) return any(d.connectionType == ConnectionType.CloudConnection for d in self._printer_output_devices)
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
def activeMachineHasCloudRegistration(self) -> bool:
return self.activeMachine is not None and ConnectionType.CloudConnection in self.activeMachine.configuredConnectionTypes
@pyqtProperty(bool, notify = printerConnectedStatusChanged) @pyqtProperty(bool, notify = printerConnectedStatusChanged)
def activeMachineIsUsingCloudConnection(self) -> bool: def activeMachineIsUsingCloudConnection(self) -> bool:
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
@ -943,7 +947,7 @@ class MachineManager(QObject):
@pyqtSlot(int, bool) @pyqtSlot(int, bool)
def setExtruderEnabled(self, position: int, enabled: bool) -> None: def setExtruderEnabled(self, position: int, enabled: bool) -> None:
if self._global_container_stack is None or str(position) not in self._global_container_stack.extruders: if self._global_container_stack is None or position >= len(self._global_container_stack.extruderList):
Logger.log("w", "Could not find extruder on position %s.", position) Logger.log("w", "Could not find extruder on position %s.", position)
return return
extruder = self._global_container_stack.extruderList[position] extruder = self._global_container_stack.extruderList[position]
@ -1106,14 +1110,14 @@ class MachineManager(QObject):
self._global_container_stack.quality = quality_container self._global_container_stack.quality = quality_container
self._global_container_stack.qualityChanges = quality_changes_container self._global_container_stack.qualityChanges = quality_changes_container
for position, extruder in self._global_container_stack.extruders.items(): for position, extruder in enumerate(self._global_container_stack.extruderList):
quality_node = None quality_node = None
if quality_group is not None: if quality_group is not None:
quality_node = quality_group.nodes_for_extruders.get(int(position)) quality_node = quality_group.nodes_for_extruders.get(position)
quality_changes_container = empty_quality_changes_container quality_changes_container = empty_quality_changes_container
quality_container = empty_quality_container quality_container = empty_quality_container
quality_changes_metadata = quality_changes_group.metadata_per_extruder.get(int(position)) quality_changes_metadata = quality_changes_group.metadata_per_extruder.get(position)
if quality_changes_metadata: if quality_changes_metadata:
containers = container_registry.findContainers(id = quality_changes_metadata["id"]) containers = container_registry.findContainers(id = quality_changes_metadata["id"])
if containers: if containers:
@ -1132,7 +1136,7 @@ class MachineManager(QObject):
def _setVariantNode(self, position: str, variant_node: "VariantNode") -> None: def _setVariantNode(self, position: str, variant_node: "VariantNode") -> None:
if self._global_container_stack is None: if self._global_container_stack is None:
return return
self._global_container_stack.extruders[position].variant = variant_node.container self._global_container_stack.extruderList[int(position)].variant = variant_node.container
self.activeVariantChanged.emit() self.activeVariantChanged.emit()
def _setGlobalVariant(self, container_node: "ContainerNode") -> None: def _setGlobalVariant(self, container_node: "ContainerNode") -> None:
@ -1147,7 +1151,7 @@ class MachineManager(QObject):
return return
if material_node and material_node.container: if material_node and material_node.container:
material_container = material_node.container material_container = material_node.container
self._global_container_stack.extruders[position].material = material_container self._global_container_stack.extruderList[int(position)].material = material_container
root_material_id = material_container.getMetaDataEntry("base_file", None) root_material_id = material_container.getMetaDataEntry("base_file", None)
else: else:
self._global_container_stack.extruderList[int(position)].material = empty_material_container self._global_container_stack.extruderList[int(position)].material = empty_material_container
@ -1251,7 +1255,7 @@ class MachineManager(QObject):
if self._global_container_stack is None: if self._global_container_stack is None:
return return
if position is None: if position is None:
position_list = list(self._global_container_stack.extruders.keys()) position_list = [str(position) for position in range(len(self._global_container_stack.extruderList))]
else: else:
position_list = [position] position_list = [position]
@ -1325,17 +1329,15 @@ class MachineManager(QObject):
# Keep a temporary copy of the global and per-extruder user changes and transfer them to the user changes # Keep a temporary copy of the global and per-extruder user changes and transfer them to the user changes
# of the new machine after the new_machine becomes active. # of the new machine after the new_machine becomes active.
global_user_changes = self._global_container_stack.userChanges global_user_changes = self._global_container_stack.userChanges
per_extruder_user_changes = {} per_extruder_user_changes = [extruder_stack.userChanges for extruder_stack in self._global_container_stack.extruderList]
for extruder_name, extruder_stack in self._global_container_stack.extruders.items():
per_extruder_user_changes[extruder_name] = extruder_stack.userChanges
self.setActiveMachine(new_machine.getId()) self.setActiveMachine(new_machine.getId())
# Apply the global and per-extruder userChanges to the new_machine (which is of different type than the # Apply the global and per-extruder userChanges to the new_machine (which is of different type than the
# previous one). # previous one).
self._global_container_stack.setUserChanges(global_user_changes) self._global_container_stack.setUserChanges(global_user_changes)
for extruder_name in self._global_container_stack.extruders.keys(): for i, user_changes in enumerate(per_extruder_user_changes):
self._global_container_stack.extruders[extruder_name].setUserChanges(per_extruder_user_changes[extruder_name]) self._global_container_stack.extruderList[i].setUserChanges(per_extruder_user_changes[i])
@pyqtSlot(QObject) @pyqtSlot(QObject)
def applyRemoteConfiguration(self, configuration: PrinterConfigurationModel) -> None: def applyRemoteConfiguration(self, configuration: PrinterConfigurationModel) -> None:
@ -1393,7 +1395,7 @@ class MachineManager(QObject):
material_container_node = variant_node.materials.get(base_file, material_container_node) material_container_node = variant_node.materials.get(base_file, material_container_node)
self._setMaterial(position, material_container_node) self._setMaterial(position, material_container_node)
self._global_container_stack.extruders[position].setEnabled(True) self._global_container_stack.extruderList[int(position)].setEnabled(True)
self.updateMaterialWithVariant(position) self.updateMaterialWithVariant(position)
self.updateDefaultExtruder() self.updateDefaultExtruder()
@ -1446,7 +1448,7 @@ class MachineManager(QObject):
if you update an active machine, special measures have to be taken. if you update an active machine, special measures have to be taken.
""" """
if global_stack is not None and global_stack != self._global_container_stack: if global_stack is not None and global_stack != self._global_container_stack:
global_stack.extruders[position].material = container_node.container global_stack.extruderList[int(position)].material = container_node.container
return return
position = str(position) position = str(position)
self.blurSettings.emit() self.blurSettings.emit()
@ -1639,7 +1641,7 @@ class MachineManager(QObject):
return return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityGroup(self.activeQualityGroup()) self._setQualityGroup(self.activeQualityGroup())
for stack in [self._global_container_stack] + list(self._global_container_stack.extruders.values()): for stack in [self._global_container_stack] + self._global_container_stack.extruderList:
stack.userChanges.clear() stack.userChanges.clear()
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged) @pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)

View file

@ -63,6 +63,7 @@ if with_sentry_sdk:
# Errors to be ignored by Sentry # Errors to be ignored by Sentry
ignore_errors = [KeyboardInterrupt, MemoryError] ignore_errors = [KeyboardInterrupt, MemoryError]
try:
sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564",
before_send = CrashHandler.sentryBeforeSend, before_send = CrashHandler.sentryBeforeSend,
environment = sentry_env, environment = sentry_env,
@ -71,6 +72,8 @@ if with_sentry_sdk:
max_breadcrumbs = 300, max_breadcrumbs = 300,
server_name = "cura", server_name = "cura",
ignore_errors = ignore_errors) ignore_errors = ignore_errors)
except Exception:
with_sentry_sdk = False
if not known_args["debug"]: if not known_args["debug"]:
def get_cura_dir_path(): def get_cura_dir_path():

View file

@ -598,7 +598,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
""" """
application = CuraApplication.getInstance() application = CuraApplication.getInstance()
try:
archive = zipfile.ZipFile(file_name, "r") archive = zipfile.ZipFile(file_name, "r")
except EnvironmentError as e:
message = Message(i18n_catalog.i18nc("@info:error Don't translate the XML tags <filename> or <message>!",
"Project file <filename>{0}</filename> is suddenly inaccessible: <message>{1}</message>.", file_name, str(e)),
title = i18n_catalog.i18nc("@info:title", "Can't Open Project File"))
message.show()
self.setWorkspaceName("")
return [], {}
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")] cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
@ -632,8 +640,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
machine_name = self._container_registry.uniqueName(self._machine_info.name) machine_name = self._container_registry.uniqueName(self._machine_info.name)
global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id) global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id)
if global_stack: #Only switch if creating the machine was successful. if global_stack: # Only switch if creating the machine was successful.
extruder_stack_dict = global_stack.extruders extruder_stack_dict = {str(position): extruder for position, extruder in enumerate(global_stack.extruderList)}
self._container_registry.addContainer(global_stack) self._container_registry.addContainer(global_stack)
else: else:

View file

@ -255,7 +255,7 @@ class StartSliceJob(Job):
global_stack = CuraApplication.getInstance().getGlobalContainerStack() global_stack = CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack: if not global_stack:
return return
extruders_enabled = {position: stack.isEnabled for position, stack in global_stack.extruders.items()} extruders_enabled = [stack.isEnabled for stack in global_stack.extruderList]
filtered_object_groups = [] filtered_object_groups = []
has_model_with_disabled_extruders = False has_model_with_disabled_extruders = False
associated_disabled_extruders = set() associated_disabled_extruders = set()
@ -265,7 +265,7 @@ class StartSliceJob(Job):
for node in group: for node in group:
# Only check if the printing extruder is enabled for printing meshes # Only check if the printing extruder is enabled for printing meshes
is_non_printing_mesh = node.callDecoration("evaluateIsNonPrintingMesh") is_non_printing_mesh = node.callDecoration("evaluateIsNonPrintingMesh")
extruder_position = node.callDecoration("getActiveExtruderPosition") extruder_position = int(node.callDecoration("getActiveExtruderPosition"))
if not is_non_printing_mesh and not extruders_enabled[extruder_position]: if not is_non_printing_mesh and not extruders_enabled[extruder_position]:
skip_group = True skip_group = True
has_model_with_disabled_extruders = True has_model_with_disabled_extruders = True
@ -275,8 +275,8 @@ class StartSliceJob(Job):
if has_model_with_disabled_extruders: if has_model_with_disabled_extruders:
self.setResult(StartJobResult.ObjectsWithDisabledExtruder) self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
associated_disabled_extruders = {str(c) for c in sorted([int(p) + 1 for p in associated_disabled_extruders])} associated_disabled_extruders = {p + 1 for p in associated_disabled_extruders}
self.setMessage(", ".join(associated_disabled_extruders)) self.setMessage(", ".join(map(str, sorted(associated_disabled_extruders))))
return return
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being

View file

@ -60,16 +60,12 @@ UM.Dialog
CheckBox CheckBox
{ {
id: toggleShowAll id: toggleShowAll
anchors anchors
{ {
top: parent.top top: parent.top
right: parent.right right: parent.right
} }
text: catalog.i18nc("@label:checkbox", "Show all") text: catalog.i18nc("@label:checkbox", "Show all")
checked: listview.model.showAll
onClicked: listview.model.showAll = checked
} }
ScrollView ScrollView
@ -85,7 +81,7 @@ UM.Dialog
} }
ListView ListView
{ {
id:listview id: listview
model: UM.SettingDefinitionsModel model: UM.SettingDefinitionsModel
{ {
id: definitionsModel id: definitionsModel
@ -98,6 +94,7 @@ UM.Dialog
excluded_settings = excluded_settings.concat(settingPickDialog.additional_excluded_settings) excluded_settings = excluded_settings.concat(settingPickDialog.additional_excluded_settings)
return excluded_settings return excluded_settings
} }
showAll: toggleShowAll.checked || filterInput.text !== ""
} }
delegate:Loader delegate:Loader
{ {

View file

@ -1,45 +0,0 @@
from ..Script import Script
class BQ_PauseAtHeight(Script):
def __init__(self):
super().__init__()
def getSettingDataString(self):
return """{
"name":"Pause at height (BQ Printers)",
"key": "BQ_PauseAtHeight",
"metadata":{},
"version": 2,
"settings":
{
"pause_height":
{
"label": "Pause height",
"description": "At what height should the pause occur",
"unit": "mm",
"type": "float",
"default_value": 5.0
}
}
}"""
def execute(self, data):
pause_z = self.getSettingValueByKey("pause_height")
for layer in data:
lines = layer.split("\n")
for line in lines:
if self.getValue(line, 'G') == 1 or self.getValue(line, 'G') == 0:
current_z = self.getValue(line, 'Z')
if current_z is not None:
if current_z >= pause_z:
prepend_gcode = ";TYPE:CUSTOM\n"
prepend_gcode += "; -- Pause at height (%.2f mm) --\n" % pause_z
# Insert Pause gcode
prepend_gcode += "M25 ; Pauses the print and waits for the user to resume it\n"
index = data.index(layer)
layer = prepend_gcode + layer
data[index] = layer # Override the data of this layer with the modified data
return data
break
return data

View file

@ -25,7 +25,7 @@ class PauseAtHeight(Script):
"label": "Pause at", "label": "Pause at",
"description": "Whether to pause at a certain height or at a certain layer.", "description": "Whether to pause at a certain height or at a certain layer.",
"type": "enum", "type": "enum",
"options": {"height": "Height", "layer_no": "Layer No."}, "options": {"height": "Height", "layer_no": "Layer Number"},
"default_value": "height" "default_value": "height"
}, },
"pause_height": "pause_height":
@ -49,6 +49,15 @@ class PauseAtHeight(Script):
"minimum_value_warning": "1", "minimum_value_warning": "1",
"enabled": "pause_at == 'layer_no'" "enabled": "pause_at == 'layer_no'"
}, },
"pause_method":
{
"label": "Method",
"description": "The method or gcode command to use for pausing.",
"type": "enum",
"options": {"marlin": "Marlin (M0)", "griffin": "Griffin (M0, firmware retract)", "bq": "BQ (M25)", "reprap": "RepRap (M226)", "repetier": "Repetier (@pause)"},
"default_value": "marlin",
"value": "\\\"griffin\\\" if machine_gcode_flavor==\\\"Griffin\\\" else \\\"reprap\\\" if machine_gcode_flavor==\\\"RepRap (RepRap)\\\" else \\\"repetier\\\" if machine_gcode_flavor==\\\"Repetier\\\" else \\\"bq\\\" if \\\"BQ\\\" in machine_name else \\\"marlin\\\""
},
"disarm_timeout": "disarm_timeout":
{ {
"label": "Disarm timeout", "label": "Disarm timeout",
@ -66,7 +75,8 @@ class PauseAtHeight(Script):
"description": "What X location does the head move to when pausing.", "description": "What X location does the head move to when pausing.",
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 190 "default_value": 190,
"enabled": "pause_method != \\\"griffin\\\""
}, },
"head_park_y": "head_park_y":
{ {
@ -74,7 +84,17 @@ class PauseAtHeight(Script):
"description": "What Y location does the head move to when pausing.", "description": "What Y location does the head move to when pausing.",
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 190 "default_value": 190,
"enabled": "pause_method != \\\"griffin\\\""
},
"head_move_z":
{
"label": "Head move Z",
"description": "The Height of Z-axis retraction before parking.",
"unit": "mm",
"type": "float",
"default_value": 15.0,
"enabled": "pause_method == \\\"repetier\\\""
}, },
"retraction_amount": "retraction_amount":
{ {
@ -82,7 +102,8 @@ class PauseAtHeight(Script):
"description": "How much filament must be retracted at pause.", "description": "How much filament must be retracted at pause.",
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 0 "default_value": 0,
"enabled": "pause_method != \\\"griffin\\\""
}, },
"retraction_speed": "retraction_speed":
{ {
@ -90,7 +111,8 @@ class PauseAtHeight(Script):
"description": "How fast to retract the filament.", "description": "How fast to retract the filament.",
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"default_value": 25 "default_value": 25,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
}, },
"extrude_amount": "extrude_amount":
{ {
@ -98,7 +120,8 @@ class PauseAtHeight(Script):
"description": "How much filament should be extruded after pause. This is needed when doing a material change on Ultimaker2's to compensate for the retraction after the change. In that case 128+ is recommended.", "description": "How much filament should be extruded after pause. This is needed when doing a material change on Ultimaker2's to compensate for the retraction after the change. In that case 128+ is recommended.",
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 0 "default_value": 0,
"enabled": "pause_method != \\\"griffin\\\""
}, },
"extrude_speed": "extrude_speed":
{ {
@ -106,7 +129,8 @@ class PauseAtHeight(Script):
"description": "How fast to extrude the material after pause.", "description": "How fast to extrude the material after pause.",
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"default_value": 3.3333 "default_value": 3.3333,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
}, },
"redo_layer": "redo_layer":
{ {
@ -121,18 +145,61 @@ class PauseAtHeight(Script):
"description": "Change the temperature during the pause.", "description": "Change the temperature during the pause.",
"unit": "°C", "unit": "°C",
"type": "int", "type": "int",
"default_value": 0 "default_value": 0,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
}, },
"display_text": "display_text":
{ {
"label": "Display Text", "label": "Display Text",
"description": "Text that should appear on the display while paused. If left empty, there will not be any message.", "description": "Text that should appear on the display while paused. If left empty, there will not be any message.",
"type": "str", "type": "str",
"default_value": "" "default_value": "",
"enabled": "pause_method != \\\"repetier\\\""
},
"machine_name":
{
"label": "Machine Type",
"description": "The name of your 3D printer model. This setting is controlled by the script and will not be visible.",
"default_value": "Unknown",
"type": "str",
"enabled": false
},
"machine_gcode_flavor":
{
"label": "G-code flavor",
"description": "The type of g-code to be generated. This setting is controlled by the script and will not be visible.",
"type": "enum",
"options":
{
"RepRap (Marlin/Sprinter)": "Marlin",
"RepRap (Volumetric)": "Marlin (Volumetric)",
"RepRap (RepRap)": "RepRap",
"UltiGCode": "Ultimaker 2",
"Griffin": "Griffin",
"Makerbot": "Makerbot",
"BFB": "Bits from Bytes",
"MACH3": "Mach3",
"Repetier": "Repetier"
},
"default_value": "RepRap (Marlin/Sprinter)",
"enabled": false
} }
} }
}""" }"""
## Copy machine name and gcode flavor from global stack so we can use their value in the script stack
def initialize(self) -> None:
super().initialize()
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack is None or self._instance is None:
return
for key in ["machine_name", "machine_gcode_flavor"]:
self._instance.setProperty(key, "value", global_container_stack.getProperty(key, "value"))
## Get the X and Y values for a layer (will be used to get X and Y of the
# layer after the pause).
def getNextXY(self, layer: str) -> Tuple[float, float]: def getNextXY(self, layer: str) -> Tuple[float, float]:
"""Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause).""" """Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause)."""
lines = layer.split("\n") lines = layer.split("\n")
@ -159,6 +226,7 @@ class PauseAtHeight(Script):
extrude_speed = self.getSettingValueByKey("extrude_speed") extrude_speed = self.getSettingValueByKey("extrude_speed")
park_x = self.getSettingValueByKey("head_park_x") park_x = self.getSettingValueByKey("head_park_x")
park_y = self.getSettingValueByKey("head_park_y") park_y = self.getSettingValueByKey("head_park_y")
move_z = self.getSettingValueByKey("head_move_z")
layers_started = False layers_started = False
redo_layer = self.getSettingValueByKey("redo_layer") redo_layer = self.getSettingValueByKey("redo_layer")
standby_temperature = self.getSettingValueByKey("standby_temperature") standby_temperature = self.getSettingValueByKey("standby_temperature")
@ -167,7 +235,14 @@ class PauseAtHeight(Script):
initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value") initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")
display_text = self.getSettingValueByKey("display_text") display_text = self.getSettingValueByKey("display_text")
is_griffin = False pause_method = self.getSettingValueByKey("pause_method")
pause_command = {
"marlin": self.putValue(M = 0),
"griffin": self.putValue(M = 0),
"bq": self.putValue(M = 25),
"reprap": self.putValue(M = 226),
"repetier": self.putValue("@pause now change filament and press continue printing")
}[pause_method]
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value") # T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
@ -188,8 +263,6 @@ class PauseAtHeight(Script):
# Scroll each line of instruction for each layer in the G-code # Scroll each line of instruction for each layer in the G-code
for line in lines: for line in lines:
if ";FLAVOR:Griffin" in line:
is_griffin = True
# Fist positive layer reached # Fist positive layer reached
if ";LAYER:0" in line: if ";LAYER:0" in line:
layers_started = True layers_started = True
@ -291,7 +364,22 @@ class PauseAtHeight(Script):
else: else:
prepend_gcode += ";current layer: {layer}\n".format(layer = current_layer) prepend_gcode += ";current layer: {layer}\n".format(layer = current_layer)
if not is_griffin: if pause_method == "repetier":
#Retraction
prepend_gcode += self.putValue(M = 83) + " ; switch to relative E values for any needed retraction\n"
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = 6000) + "\n"
#Move the head away
prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + " ; move up a millimeter to get out of the way\n"
prepend_gcode += self.putValue(G = 1, X = park_x, Y = park_y, F = 9000) + "\n"
if current_z < move_z:
prepend_gcode += self.putValue(G = 1, Z = current_z + move_z, F = 300) + "\n"
#Disable the E steppers
prepend_gcode += self.putValue(M = 84, E = 0) + "\n"
elif pause_method != "griffin":
# Retraction # Retraction
prepend_gcode += self.putValue(M = 83) + " ; switch to relative E values for any needed retraction\n" prepend_gcode += self.putValue(M = 83) + " ; switch to relative E values for any needed retraction\n"
if retraction_amount != 0: if retraction_amount != 0:
@ -323,9 +411,40 @@ class PauseAtHeight(Script):
prepend_gcode += self.putValue(M = 18, S = disarm_timeout) + " ; Set the disarm timeout\n" prepend_gcode += self.putValue(M = 18, S = disarm_timeout) + " ; Set the disarm timeout\n"
# Wait till the user continues printing # Wait till the user continues printing
prepend_gcode += self.putValue(M = 0) + " ; Do the actual pause\n" prepend_gcode += pause_command + " ; Do the actual pause\n"
if not is_griffin: if pause_method == "repetier":
#Push the filament back,
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = 6000) + "\n"
# Optionally extrude material
if extrude_amount != 0:
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = 200) + "\n"
prepend_gcode += self.putValue("@info wait for cleaning nozzle from previous filament") + "\n"
prepend_gcode += self.putValue("@pause remove the waste filament from parking area and press continue printing") + "\n"
# and retract again, the properly primes the nozzle when changing filament.
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = 6000) + "\n"
#Move the head back
prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
prepend_gcode += self.putValue(G = 1, X = x, Y = y, F = 9000) + "\n"
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = 6000) + "\n"
if current_extrusion_f != 0:
prepend_gcode += self.putValue(G = 1, F = current_extrusion_f) + " ; restore extrusion feedrate\n"
else:
Logger.log("w", "No previous feedrate found in gcode, feedrate for next layer(s) might be incorrect")
prepend_gcode += self.putValue(M = 82) + "\n"
# reset extrude value to pre pause value
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
elif pause_method != "griffin":
if control_temperatures: if control_temperatures:
# Set extruder resume temperature # Set extruder resume temperature
prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, 0))) + " ; resume temperature\n" prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, 0))) + " ; resume temperature\n"

View file

@ -1,51 +0,0 @@
from ..Script import Script
class PauseAtHeightRepRapFirmwareDuet(Script):
def getSettingDataString(self):
return """{
"name": "Pause at height for RepRapFirmware DuetWifi / Duet Ethernet / Duet Maestro",
"key": "PauseAtHeightRepRapFirmwareDuet",
"metadata": {},
"version": 2,
"settings":
{
"pause_height":
{
"label": "Pause height",
"description": "At what height should the pause occur",
"unit": "mm",
"type": "float",
"default_value": 5.0
}
}
}"""
def execute(self, data):
current_z = 0.
pause_z = self.getSettingValueByKey("pause_height")
layers_started = False
for layer_number, layer in enumerate(data):
lines = layer.split("\n")
for line in lines:
if ";LAYER:0" in line:
layers_started = True
continue
if not layers_started:
continue
if self.getValue(line, 'G') == 1 or self.getValue(line, 'G') == 0:
current_z = self.getValue(line, 'Z')
if current_z != None:
if current_z >= pause_z:
prepend_gcode = ";TYPE:CUSTOM\n"
prepend_gcode += "; -- Pause at height (%.2f mm) --\n" % pause_z
prepend_gcode += self.putValue(M = 226) + "\n"
layer = prepend_gcode + layer
data[layer_number] = layer # Override the data of this layer with the modified data
return data
break
return data

View file

@ -1,178 +0,0 @@
from UM.Logger import Logger
from ..Script import Script
class PauseAtHeightforRepetier(Script):
def __init__(self):
super().__init__()
def getSettingDataString(self):
return """{
"name":"Pause at height for repetier",
"key": "PauseAtHeightforRepetier",
"metadata": {},
"version": 2,
"settings":
{
"pause_height":
{
"label": "Pause height",
"description": "At what height should the pause occur",
"unit": "mm",
"type": "float",
"default_value": 5.0
},
"head_park_x":
{
"label": "Park print head X",
"description": "What x location does the head move to when pausing.",
"unit": "mm",
"type": "float",
"default_value": 5.0
},
"head_park_y":
{
"label": "Park print head Y",
"description": "What y location does the head move to when pausing.",
"unit": "mm",
"type": "float",
"default_value": 5.0
},
"head_move_Z":
{
"label": "Head move Z",
"description": "The Hieght of Z-axis retraction before parking.",
"unit": "mm",
"type": "float",
"default_value": 15.0
},
"retraction_amount":
{
"label": "Retraction",
"description": "How much fillament must be retracted at pause.",
"unit": "mm",
"type": "float",
"default_value": 5.0
},
"extrude_amount":
{
"label": "Extrude amount",
"description": "How much filament should be extruded after pause. This is needed when doing a material change on Ultimaker2's to compensate for the retraction after the change. In that case 128+ is recommended.",
"unit": "mm",
"type": "float",
"default_value": 90.0
},
"redo_layers":
{
"label": "Redo layers",
"description": "Redo a number of previous layers after a pause to increases adhesion.",
"unit": "layers",
"type": "int",
"default_value": 0
}
}
}"""
def execute(self, data):
x = 0.
y = 0.
current_extrusion_f = 0
current_z = 0.
pause_z = self.getSettingValueByKey("pause_height")
retraction_amount = self.getSettingValueByKey("retraction_amount")
extrude_amount = self.getSettingValueByKey("extrude_amount")
park_x = self.getSettingValueByKey("head_park_x")
park_y = self.getSettingValueByKey("head_park_y")
move_Z = self.getSettingValueByKey("head_move_Z")
layers_started = False
redo_layers = self.getSettingValueByKey("redo_layers")
for layer in data:
lines = layer.split("\n")
for line in lines:
if ";LAYER:0" in line:
layers_started = True
continue
if not layers_started:
continue
if self.getValue(line, 'G') == 1 or self.getValue(line, 'G') == 0:
current_z = self.getValue(line, 'Z')
if self.getValue(line, 'F') is not None and self.getValue(line, 'E') is not None:
current_extrusion_f = self.getValue(line, 'F', current_extrusion_f)
x = self.getValue(line, 'X', x)
y = self.getValue(line, 'Y', y)
if current_z is not None:
if current_z >= pause_z:
index = data.index(layer)
prevLayer = data[index-1]
prevLines = prevLayer.split("\n")
current_e = 0.
for prevLine in reversed(prevLines):
current_e = self.getValue(prevLine, 'E', -1)
if current_e >= 0:
break
prepend_gcode = ";TYPE:CUSTOM\n"
prepend_gcode += ";added code by post processing\n"
prepend_gcode += ";script: PauseAtHeightforRepetier.py\n"
prepend_gcode += ";current z: %f \n" % (current_z)
prepend_gcode += ";current X: %f \n" % (x)
prepend_gcode += ";current Y: %f \n" % (y)
#Retraction
prepend_gcode += "M83\n"
if retraction_amount != 0:
prepend_gcode += "G1 E-%f F6000\n" % (retraction_amount)
#Move the head away
prepend_gcode += "G1 Z%f F300\n" % (1 + current_z)
prepend_gcode += "G1 X%f Y%f F9000\n" % (park_x, park_y)
if current_z < move_Z:
prepend_gcode += "G1 Z%f F300\n" % (current_z + move_Z)
#Disable the E steppers
prepend_gcode += "M84 E0\n"
#Wait till the user continues printing
prepend_gcode += "@pause now change filament and press continue printing ;Do the actual pause\n"
#Push the filament back,
if retraction_amount != 0:
prepend_gcode += "G1 E%f F6000\n" % (retraction_amount)
# Optionally extrude material
if extrude_amount != 0:
prepend_gcode += "G1 E%f F200\n" % (extrude_amount)
prepend_gcode += "@info wait for cleaning nozzle from previous filament\n"
prepend_gcode += "@pause remove the waste filament from parking area and press continue printing\n"
# and retract again, the properly primes the nozzle when changing filament.
if retraction_amount != 0:
prepend_gcode += "G1 E-%f F6000\n" % (retraction_amount)
#Move the head back
prepend_gcode += "G1 Z%f F300\n" % (1 + current_z)
prepend_gcode +="G1 X%f Y%f F9000\n" % (x, y)
if retraction_amount != 0:
prepend_gcode +="G1 E%f F6000\n" % (retraction_amount)
if current_extrusion_f != 0:
prepend_gcode += self.putValue(G=1, F=current_extrusion_f) + " ; restore extrusion feedrate\n"
else:
Logger.log("w", "No previous feedrate found in gcode, feedrate for next layer(s) might be incorrect")
prepend_gcode +="M82\n"
# reset extrude value to pre pause value
prepend_gcode +="G92 E%f\n" % (current_e)
layer = prepend_gcode + layer
# include a number of previous layers
for i in range(1, redo_layers + 1):
prevLayer = data[index-i]
layer = prevLayer + layer
data[index] = layer #Override the data of this layer with the modified data
return data
break
return data

View file

@ -67,6 +67,7 @@ class CloudPackageChecker(QObject):
self._application.getHttpRequestManager().get(url, self._application.getHttpRequestManager().get(url,
callback = self._onUserPackagesRequestFinished, callback = self._onUserPackagesRequestFinished,
error_callback = self._onUserPackagesRequestFinished, error_callback = self._onUserPackagesRequestFinished,
timeout=10,
scope = self._scope) scope = self._scope)
def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None: def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None:

View file

@ -6,11 +6,15 @@ from time import time
from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from UM.Logger import Logger from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from cura.API import Account from cura.API import Account
from cura.CuraApplication import CuraApplication
from cura.UltimakerCloud import UltimakerCloudAuthentication from cura.UltimakerCloud import UltimakerCloudAuthentication
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from .ToolPathUploader import ToolPathUploader from .ToolPathUploader import ToolPathUploader
from ..Models.BaseModel import BaseModel from ..Models.BaseModel import BaseModel
from ..Models.Http.CloudClusterResponse import CloudClusterResponse from ..Models.Http.CloudClusterResponse import CloudClusterResponse
@ -35,18 +39,23 @@ class CloudApiClient:
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
# In order to avoid garbage collection we keep the callbacks in this list. DEFAULT_REQUEST_TIMEOUT = 10 # seconds
_anti_gc_callbacks = [] # type: List[Callable[[], None]]
def __init__(self, account: Account, on_error: Callable[[List[CloudError]], None]) -> None: # In order to avoid garbage collection we keep the callbacks in this list.
_anti_gc_callbacks = [] # type: List[Callable[[Any], None]]
def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None:
"""Initializes a new cloud API client. """Initializes a new cloud API client.
:param app:
:param account: The user's account object :param account: The user's account object
:param on_error: The callback to be called whenever we receive errors from the server. :param on_error: The callback to be called whenever we receive errors from the server.
""" """
super().__init__() super().__init__()
self._manager = QNetworkAccessManager() self._app = app
self._account = account self._account = app.getCuraAPI().account
self._scope = JsonDecoratorScope(UltimakerCloudScope(app))
self._http = HttpRequestManager.getInstance()
self._on_error = on_error self._on_error = on_error
self._upload = None # type: Optional[ToolPathUploader] self._upload = None # type: Optional[ToolPathUploader]
@ -63,8 +72,11 @@ class CloudApiClient:
""" """
url = "{}/clusters?status=active".format(self.CLUSTER_API_ROOT) url = "{}/clusters?status=active".format(self.CLUSTER_API_ROOT)
reply = self._manager.get(self._createEmptyRequest(url)) self._http.get(url,
self._addCallback(reply, on_finished, CloudClusterResponse, failed) scope = self._scope,
callback = self._parseCallback(on_finished, CloudClusterResponse, failed),
error_callback = failed,
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None: def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None:
"""Retrieves the status of the given cluster. """Retrieves the status of the given cluster.
@ -74,8 +86,10 @@ class CloudApiClient:
""" """
url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id)
reply = self._manager.get(self._createEmptyRequest(url)) self._http.get(url,
self._addCallback(reply, on_finished, CloudClusterStatus) scope = self._scope,
callback = self._parseCallback(on_finished, CloudClusterStatus),
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def requestUpload(self, request: CloudPrintJobUploadRequest, def requestUpload(self, request: CloudPrintJobUploadRequest,
on_finished: Callable[[CloudPrintJobResponse], Any]) -> None: on_finished: Callable[[CloudPrintJobResponse], Any]) -> None:
@ -87,9 +101,13 @@ class CloudApiClient:
""" """
url = "{}/jobs/upload".format(self.CURA_API_ROOT) url = "{}/jobs/upload".format(self.CURA_API_ROOT)
body = json.dumps({"data": request.toDict()}) data = json.dumps({"data": request.toDict()}).encode()
reply = self._manager.put(self._createEmptyRequest(url), body.encode())
self._addCallback(reply, on_finished, CloudPrintJobResponse) self._http.put(url,
scope = self._scope,
data = data,
callback = self._parseCallback(on_finished, CloudPrintJobResponse),
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any],
on_progress: Callable[[int], Any], on_error: Callable[[], Any]): on_progress: Callable[[int], Any], on_error: Callable[[], Any]):
@ -102,7 +120,7 @@ class CloudApiClient:
:param on_error: A function to be called if the upload fails. :param on_error: A function to be called if the upload fails.
""" """
self._upload = ToolPathUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) self._upload = ToolPathUploader(self._http, print_job, mesh, on_finished, on_progress, on_error)
self._upload.start() self._upload.start()
# Requests a cluster to print the given print job. # Requests a cluster to print the given print job.
@ -111,8 +129,11 @@ class CloudApiClient:
# \param on_finished: The function to be called after the result is parsed. # \param on_finished: The function to be called after the result is parsed.
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None: def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None:
url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id)
reply = self._manager.post(self._createEmptyRequest(url), b"") self._http.post(url,
self._addCallback(reply, on_finished, CloudPrintResponse) scope = self._scope,
data = b"",
callback = self._parseCallback(on_finished, CloudPrintResponse),
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str, def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str,
data: Optional[Dict[str, Any]] = None) -> None: data: Optional[Dict[str, Any]] = None) -> None:
@ -126,7 +147,10 @@ class CloudApiClient:
body = json.dumps({"data": data}).encode() if data else b"" body = json.dumps({"data": data}).encode() if data else b""
url = "{}/clusters/{}/print_jobs/{}/action/{}".format(self.CLUSTER_API_ROOT, cluster_id, cluster_job_id, action) url = "{}/clusters/{}/print_jobs/{}/action/{}".format(self.CLUSTER_API_ROOT, cluster_id, cluster_job_id, action)
self._manager.post(self._createEmptyRequest(url), body) self._http.post(url,
scope = self._scope,
data = body,
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
"""We override _createEmptyRequest in order to add the user credentials. """We override _createEmptyRequest in order to add the user credentials.
@ -185,12 +209,12 @@ class CloudApiClient:
else: else:
Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) Logger.log("e", "Cannot find data or errors in the cloud response: %s", response)
def _addCallback(self, def _parseCallback(self,
reply: QNetworkReply,
on_finished: Union[Callable[[CloudApiClientModel], Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]], Callable[[List[CloudApiClientModel]], Any]],
model: Type[CloudApiClientModel], model: Type[CloudApiClientModel],
on_error: Optional[Callable] = None) -> None: on_error: Optional[Callable] = None) -> Callable[[QNetworkReply], None]:
"""Creates a callback function so that it includes the parsing of the response into the correct model. """Creates a callback function so that it includes the parsing of the response into the correct model.
The callback is added to the 'finished' signal of the reply. The callback is added to the 'finished' signal of the reply.
@ -200,7 +224,8 @@ class CloudApiClient:
:param model: The type of the model to convert the response to. :param model: The type of the model to convert the response to.
""" """
def parse() -> None: def parse(reply: QNetworkReply) -> None:
self._anti_gc_callbacks.remove(parse) self._anti_gc_callbacks.remove(parse)
# Don't try to parse the reply if we didn't get one # Don't try to parse the reply if we didn't get one
@ -216,6 +241,4 @@ class CloudApiClient:
self._parseModels(response, on_finished, model) self._parseModels(response, on_finished, model)
self._anti_gc_callbacks.append(parse) self._anti_gc_callbacks.append(parse)
reply.finished.connect(parse) return parse
if on_error is not None:
reply.error.connect(on_error)

View file

@ -4,6 +4,7 @@ import os
from typing import Dict, List, Optional from typing import Dict, List, Optional
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
from PyQt5.QtNetwork import QNetworkReply
from UM import i18nCatalog from UM import i18nCatalog
from UM.Logger import Logger # To log errors talking to the API. from UM.Logger import Logger # To log errors talking to the API.
@ -40,7 +41,7 @@ class CloudOutputDeviceManager:
# Persistent dict containing the remote clusters for the authenticated user. # Persistent dict containing the remote clusters for the authenticated user.
self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] self._remote_clusters = {} # type: Dict[str, CloudOutputDevice]
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
self._api = CloudApiClient(self._account, on_error = lambda error: Logger.log("e", str(error))) self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
self._account.loginStateChanged.connect(self._onLoginStateChanged) self._account.loginStateChanged.connect(self._onLoginStateChanged)
# Ensure we don't start twice. # Ensure we don't start twice.
@ -118,7 +119,7 @@ class CloudOutputDeviceManager:
self._syncing = False self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS) self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
def _onGetRemoteClusterFailed(self): def _onGetRemoteClusterFailed(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
self._syncing = False self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR) self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
@ -265,6 +266,13 @@ class CloudOutputDeviceManager:
machine.setName(device.name) machine.setName(device.name)
machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
machine.setMetaDataEntry("group_name", device.name) machine.setMetaDataEntry("group_name", device.name)
machine.setMetaDataEntry("removal_warning", self.I18N_CATALOG.i18nc(
"@label ({} is printer name)",
"{} will be removed until the next account sync. <br> To remove {} permanently, "
"visit <a href='https://mycloud.ultimaker.com/'>Ultimaker Digital Factory</a>. "
"<br><br>Are you sure you want to remove {} temporarily?",
device.name, device.name, device.name
))
machine.addConfiguredConnectionType(device.connectionType.value) machine.addConfiguredConnectionType(device.connectionType.value)
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None: def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:

View file

@ -1,11 +1,11 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# !/usr/bin/env python # !/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from typing import Callable, Any, Tuple, cast, Dict, Optional
from typing import Optional, Callable, Any, Tuple, cast
from UM.Logger import Logger from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse
@ -23,7 +23,7 @@ class ToolPathUploader:
# The amount of bytes to send per request # The amount of bytes to send per request
BYTES_PER_REQUEST = 256 * 1024 BYTES_PER_REQUEST = 256 * 1024
def __init__(self, manager: QNetworkAccessManager, print_job: CloudPrintJobResponse, data: bytes, def __init__(self, http: HttpRequestManager, print_job: CloudPrintJobResponse, data: bytes,
on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any] on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]
) -> None: ) -> None:
"""Creates a mesh upload object. """Creates a mesh upload object.
@ -36,7 +36,7 @@ class ToolPathUploader:
:param on_error: The method to be called when an error occurs. :param on_error: The method to be called when an error occurs.
""" """
self._manager = manager self._http = http
self._print_job = print_job self._print_job = print_job
self._data = data self._data = data
@ -47,7 +47,6 @@ class ToolPathUploader:
self._sent_bytes = 0 self._sent_bytes = 0
self._retries = 0 self._retries = 0
self._finished = False self._finished = False
self._reply = None # type: Optional[QNetworkReply]
@property @property
def printJob(self): def printJob(self):
@ -55,19 +54,6 @@ class ToolPathUploader:
return self._print_job return self._print_job
def _createRequest(self) -> QNetworkRequest:
"""Creates a network request to the print job upload URL, adding the needed content range header."""
request = QNetworkRequest(QUrl(self._print_job.upload_url))
request.setHeader(QNetworkRequest.ContentTypeHeader, self._print_job.content_type)
first_byte, last_byte = self._chunkRange()
content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data))
request.setRawHeader(b"Content-Range", content_range.encode())
Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url)
return request
def _chunkRange(self) -> Tuple[int, int]: def _chunkRange(self) -> Tuple[int, int]:
"""Determines the bytes that should be uploaded next. """Determines the bytes that should be uploaded next.
@ -99,13 +85,23 @@ class ToolPathUploader:
raise ValueError("The upload is already finished") raise ValueError("The upload is already finished")
first_byte, last_byte = self._chunkRange() first_byte, last_byte = self._chunkRange()
request = self._createRequest() content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data))
# now send the reply and subscribe to the results headers = {
self._reply = self._manager.put(request, self._data[first_byte:last_byte]) "Content-Type": cast(str, self._print_job.content_type),
self._reply.finished.connect(self._finishedCallback) "Content-Range": content_range
self._reply.uploadProgress.connect(self._progressCallback) } # type: Dict[str, str]
self._reply.error.connect(self._errorCallback)
Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url)
self._http.put(
url = cast(str, self._print_job.upload_url),
headers_dict = headers,
data = self._data[first_byte:last_byte],
callback = self._finishedCallback,
error_callback = self._errorCallback,
upload_progress_callback = self._progressCallback
)
def _progressCallback(self, bytes_sent: int, bytes_total: int) -> None: def _progressCallback(self, bytes_sent: int, bytes_total: int) -> None:
"""Handles an update to the upload progress """Handles an update to the upload progress
@ -118,7 +114,8 @@ class ToolPathUploader:
total_sent = self._sent_bytes + bytes_sent total_sent = self._sent_bytes + bytes_sent
self._on_progress(int(total_sent / len(self._data) * 100)) self._on_progress(int(total_sent / len(self._data) * 100))
def _errorCallback(self) -> None: ## Handles an error uploading.
def _errorCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
"""Handles an error uploading.""" """Handles an error uploading."""
reply = cast(QNetworkReply, self._reply) reply = cast(QNetworkReply, self._reply)
@ -127,7 +124,7 @@ class ToolPathUploader:
self.stop() self.stop()
self._on_error() self._on_error()
def _finishedCallback(self) -> None: def _finishedCallback(self, reply: QNetworkReply) -> None:
"""Checks whether a chunk of data was uploaded successfully, starting the next chunk if needed.""" """Checks whether a chunk of data was uploaded successfully, starting the next chunk if needed."""
reply = cast(QNetworkReply, self._reply) reply = cast(QNetworkReply, self._reply)
@ -148,7 +145,7 @@ class ToolPathUploader:
# Http codes that are not to be retried are assumed to be errors. # Http codes that are not to be retried are assumed to be errors.
if status_code > 308: if status_code > 308:
self._errorCallback() self._errorCallback(reply, None)
return return
Logger.log("d", "status_code: %s, Headers: %s, body: %s", status_code, Logger.log("d", "status_code: %s, Headers: %s, body: %s", status_code,

View file

@ -286,7 +286,7 @@ class LocalClusterOutputDeviceManager:
def _showCloudFlowMessage(device: LocalClusterOutputDevice) -> None: def _showCloudFlowMessage(device: LocalClusterOutputDevice) -> None:
"""Nudge the user to start using Ultimaker Cloud.""" """Nudge the user to start using Ultimaker Cloud."""
if CuraApplication.getInstance().getMachineManager().activeMachineIsUsingCloudConnection: if CuraApplication.getInstance().getMachineManager().activeMachineHasCloudRegistration:
# This printer is already cloud connected, so we do not bother the user anymore. # This printer is already cloud connected, so we do not bother the user anymore.
return return
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:

View file

@ -2,11 +2,44 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import configparser import configparser
import copy # To split up files.
from typing import Tuple, List from typing import Tuple, List
import io import io
from UM.VersionUpgrade import VersionUpgrade from UM.VersionUpgrade import VersionUpgrade
renamed_nozzles = {
"deltacomb_025_e3d": "deltacomb_dc20_fbe025",
"deltacomb_040_e3d": "deltacomb_dc20_fbe040",
"deltacomb_080_e3d": "deltacomb_dc20_vfbe080"
}
default_qualities_per_nozzle_and_material = {
# Can't define defaults for user-defined materials, since we only have the material ID. Those will get reset to empty quality :(
"deltacomb_dc20_fbe025": {
"generic_pla_175": "deltacomb_FBE0.25_PLA_C",
"generic_abs_175": "deltacomb_FBE0.25_ABS_C"
},
"deltacomb_dc20_fbe040": {
"generic_pla_175": "deltacomb_FBE0.40_PLA_C",
"generic_abs_175": "deltacomb_FBE0.40_ABS_C",
"generic_petg_175": "deltacomb_FBE0.40_PETG_C",
"generic_tpu_175": "deltacomb_FBE0.40_TPU_C"
},
"deltacomb_dc20_vfbe080": {
"generic_pla_175": "deltacomb_VFBE0.80_PLA_D",
"generic_abs_175": "deltacomb_VFBE0.80_ABS_D"
}
}
class VersionUpgrade460to462(VersionUpgrade): class VersionUpgrade460to462(VersionUpgrade):
def getCfgVersion(self, serialised: str) -> int:
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised)
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
return format_version * 1000000 + setting_version
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
""" """
Upgrades preferences to have the new version number. Upgrades preferences to have the new version number.
@ -25,6 +58,66 @@ class VersionUpgrade460to462(VersionUpgrade):
parser.write(result) parser.write(result)
return [filename], [result.getvalue()] return [filename], [result.getvalue()]
def upgradeExtruderInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades per-extruder instance containers to the new version number.
This applies all of the changes that are applied in other instance
containers as well.
In the case of Deltacomb printers, it splits the 2 extruders into 4 and
changes the definition.
:param serialized: The original contents of the instance container.
:param filename: The original file name of the instance container.
:return: A list of new file names, and a list of the new contents for
those files.
"""
parser = configparser.ConfigParser(interpolation = None, comment_prefixes = ())
parser.read_string(serialized)
results = [(parser, filename)]
if "general" in parser and "definition" in parser["general"]:
if parser["general"]["definition"] == "deltacomb_extruder_0":
parser["general"]["definition"] = "deltacomb_base_extruder_0"
elif parser["general"]["definition"] == "deltacomb_extruder_1": # Split up the second Deltacomb extruder into 3, creating an extra two extruders.
parser_e2 = configparser.ConfigParser(interpolation = None)
parser_e3 = configparser.ConfigParser(interpolation = None)
parser_e2.read_dict(parser)
parser_e3.read_dict(parser)
parser["general"]["definition"] = "deltacomb_base_extruder_1"
parser_e2["general"]["definition"] = "deltacomb_base_extruder_2"
parser_e3["general"]["definition"] = "deltacomb_base_extruder_3"
results.append((parser_e2, filename + "_e2_upgrade")) # Hopefully not already taken.
results.append((parser_e3, filename + "_e3_upgrade"))
elif parser["general"]["definition"] == "deltacomb": # On the global stack, the per-extruder user container OR the per-extruder quality changes container.
parser["general"]["definition"] = "deltacomb_dc20"
if "metadata" in parser and ("extruder" in parser["metadata"] or "position" in parser["metadata"]): # Per-extruder user container or quality changes container.
parser_e2 = configparser.ConfigParser(interpolation = None)
parser_e3 = configparser.ConfigParser(interpolation = None)
parser_e2.read_dict(parser)
parser_e3.read_dict(parser)
if "extruder" in parser["metadata"]:
parser_e2["metadata"]["extruder"] += "_e2_upgrade"
parser_e3["metadata"]["extruder"] += "_e3_upgrade"
results.append((parser_e2, filename + "_e2_upgrade"))
results.append((parser_e3, filename + "_e3_upgrade"))
# Now go upgrade with the generic instance container method.
final_serialized = [] # type: List[str]
final_filenames = [] # type: List[str]
for result_parser, result_filename in results:
result_ss = io.StringIO()
result_parser.write(result_ss)
result_serialized = result_ss.getvalue()
# The upgrade function itself might also return multiple files, so we need to append all of those into the final list.
this_filenames_upgraded, this_serialized_upgraded = self.upgradeInstanceContainer(result_serialized, result_filename)
final_serialized += this_serialized_upgraded
final_filenames += this_filenames_upgraded
return final_filenames, final_serialized
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
""" """
Upgrades instance containers to have the new version number. Upgrades instance containers to have the new version number.
@ -62,6 +155,9 @@ class VersionUpgrade460to462(VersionUpgrade):
def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
""" """
Upgrades stacks to have the new version number. Upgrades stacks to have the new version number.
This upgrades Deltacomb printers to their new profile structure, and
gives them 4 extruders.
:param serialized: The original contents of the stack. :param serialized: The original contents of the stack.
:param filename: The original file name of the stack. :param filename: The original file name of the stack.
:return: A list of new file names, and a list of the new contents for :return: A list of new file names, and a list of the new contents for
@ -69,12 +165,56 @@ class VersionUpgrade460to462(VersionUpgrade):
""" """
parser = configparser.ConfigParser(interpolation = None) parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized) parser.read_string(serialized)
results = [(parser, filename)]
# Update version number. # Update version number.
if "metadata" not in parser: if "metadata" not in parser:
parser["metadata"] = {} parser["metadata"] = {}
parser["metadata"]["setting_version"] = "14" parser["metadata"]["setting_version"] = "14"
result = io.StringIO() if "containers" in parser and "7" in parser["containers"]:
parser.write(result) if parser["containers"]["7"] == "deltacomb_extruder_0" or parser["containers"]["7"] == "deltacomb_extruder_1": # Extruder stack.
return [filename], [result.getvalue()] if "5" in parser["containers"]:
parser["containers"]["5"] = renamed_nozzles.get(parser["containers"]["5"], parser["containers"]["5"])
if "3" in parser["containers"] and "4" in parser["containers"] and parser["containers"]["3"] == "empty_quality":
parser["containers"]["3"] = default_qualities_per_nozzle_and_material[parser["containers"]["5"]].get(parser["containers"]["4"], "empty_quality")
if parser["containers"]["7"] == "deltacomb_extruder_0":
parser["containers"]["7"] = "deltacomb_base_extruder_0"
else:
parser["containers"]["7"] = "deltacomb_base_extruder_1"
# Copy this extruder to extruder 3 and 4.
extruder3 = configparser.ConfigParser(interpolation=None)
extruder4 = configparser.ConfigParser(interpolation=None)
extruder3.read_dict(parser)
extruder4.read_dict(parser)
extruder3["general"]["id"] += "_e2_upgrade"
extruder3["metadata"]["position"] = "2"
extruder3["containers"]["0"] += "_e2_upgrade"
if extruder3["containers"]["1"] != "empty_quality_changes":
extruder3["containers"]["1"] += "_e2_upgrade"
extruder3["containers"]["6"] += "_e2_upgrade"
extruder3["containers"]["7"] = "deltacomb_base_extruder_2"
results.append((extruder3, filename + "_e2_upgrade"))
extruder4["general"]["id"] += "_e3_upgrade"
extruder4["metadata"]["position"] = "3"
extruder4["containers"]["0"] += "_e3_upgrade"
if extruder4["containers"]["1"] != "empty_quality_changes":
extruder4["containers"]["1"] += "_e3_upgrade"
extruder4["containers"]["6"] += "_e3_upgrade"
extruder4["containers"]["7"] = "deltacomb_base_extruder_3"
results.append((extruder4, filename + "_e3_upgrade"))
elif parser["containers"]["7"] == "deltacomb": # Global stack.
parser["containers"]["7"] = "deltacomb_dc20"
parser["containers"]["3"] = "deltacomb_global_C"
result_serialized = []
result_filenames = []
for result_parser, result_filename in results:
result_ss = io.StringIO()
result_parser.write(result_ss)
result_serialized.append(result_ss.getvalue())
result_filenames.append(result_filename)
return result_filenames, result_serialized

View file

@ -17,10 +17,10 @@ def getMetaData() -> Dict[str, Any]:
("preferences", 6000013): ("preferences", 6000014, upgrade.upgradePreferences), ("preferences", 6000013): ("preferences", 6000014, upgrade.upgradePreferences),
("machine_stack", 4000013): ("machine_stack", 4000014, upgrade.upgradeStack), ("machine_stack", 4000013): ("machine_stack", 4000014, upgrade.upgradeStack),
("extruder_train", 4000013): ("extruder_train", 4000014, upgrade.upgradeStack), ("extruder_train", 4000013): ("extruder_train", 4000014, upgrade.upgradeStack),
("definition_changes", 4000013): ("definition_changes", 4000014, upgrade.upgradeInstanceContainer), ("definition_changes", 4000013): ("definition_changes", 4000014, upgrade.upgradeExtruderInstanceContainer),
("quality_changes", 4000013): ("quality_changes", 4000014, upgrade.upgradeInstanceContainer), ("quality_changes", 4000013): ("quality_changes", 4000014, upgrade.upgradeExtruderInstanceContainer),
("quality", 4000013): ("quality", 4000014, upgrade.upgradeInstanceContainer), ("quality", 4000013): ("quality", 4000014, upgrade.upgradeExtruderInstanceContainer),
("user", 4000013): ("user", 4000014, upgrade.upgradeInstanceContainer), ("user", 4000013): ("user", 4000014, upgrade.upgradeExtruderInstanceContainer),
}, },
"sources": { "sources": {
"preferences": { "preferences": {

View file

@ -58,6 +58,19 @@ class VersionUpgrade462to47(VersionUpgrade):
maximum_deviation = "=(" + maximum_deviation + ") / 2" maximum_deviation = "=(" + maximum_deviation + ") / 2"
parser["values"]["meshfix_maximum_deviation"] = maximum_deviation parser["values"]["meshfix_maximum_deviation"] = maximum_deviation
# Ironing inset is now based on the flow-compensated line width to make the setting have a more logical UX.
# Adjust so that the actual print result remains the same.
if "ironing_inset" in parser["values"]:
ironing_inset = parser["values"]["ironing_inset"]
if ironing_inset.startswith("="):
ironing_inset = ironing_inset[1:]
if "ironing_pattern" in parser["values"] and parser["values"]["ironing_pattern"] == "concentric":
correction = " + ironing_line_spacing - skin_line_width * (1.0 + ironing_flow / 100) / 2"
else: # If ironing_pattern doesn't exist, it means the default (zigzag) is selected
correction = " + skin_line_width * (1.0 - ironing_flow / 100) / 2"
ironing_inset = "=(" + ironing_inset + ")" + correction
parser["values"]["ironing_inset"] = ironing_inset
result = io.StringIO() result = io.StringIO()
parser.write(result) parser.write(result)
return [filename], [result.getvalue()] return [filename], [result.getvalue()]
@ -88,6 +101,25 @@ class VersionUpgrade462to47(VersionUpgrade):
script_parser = configparser.ConfigParser(interpolation=None) script_parser = configparser.ConfigParser(interpolation=None)
script_parser.optionxform = str # type: ignore # Don't transform the setting keys as they are case-sensitive. script_parser.optionxform = str # type: ignore # Don't transform the setting keys as they are case-sensitive.
script_parser.read_string(script_str) script_parser.read_string(script_str)
# Unify all Pause at Height
script_id = script_parser.sections()[0]
if script_id in ["BQ_PauseAtHeight", "PauseAtHeightRepRapFirmwareDuet", "PauseAtHeightforRepetier"]:
script_settings = script_parser.items(script_id)
script_settings.append(("pause_method", {
"BQ_PauseAtHeight": "bq",
"PauseAtHeightforRepetier": "repetier",
"PauseAtHeightRepRapFirmwareDuet": "reprap"
}[script_id]))
# Since we cannot rename a section, we remove the original section and create a new section with the new script id.
script_parser.remove_section(script_id)
script_id = "PauseAtHeight"
script_parser.add_section(script_id)
for setting_tuple in script_settings:
script_parser.set(script_id, setting_tuple[0], setting_tuple[1])
# Update redo_layers to redo_layer
if "PauseAtHeight" in script_parser: if "PauseAtHeight" in script_parser:
if "redo_layers" in script_parser["PauseAtHeight"]: if "redo_layers" in script_parser["PauseAtHeight"]:
script_parser["PauseAtHeight"]["redo_layer"] = str(int(script_parser["PauseAtHeight"]["redo_layers"]) > 0) script_parser["PauseAtHeight"]["redo_layer"] = str(int(script_parser["PauseAtHeight"]["redo_layers"]) > 0)

View file

@ -0,0 +1,33 @@
{
"version": 2,
"name": "I3 Metal Motion",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Peter Felecan",
"manufacturer": "eMotionTech",
"file_formats": "text/x-gcode",
"has_materials": true,
"preferred_material": "emotiontech_pla",
"machine_extruder_trains":
{
"0": "I3MetalMotion_extruder_0"
}
},
"overrides": {
"machine_name": { "default_value": "I3MetalMotion" },
"machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 200 },
"machine_height": { "default_value": 200 },
"machine_depth": { "default_value": 200 },
"machine_center_is_zero": { "default_value": false },
"machine_gcode_flavor": { "default_value": "RepRap (RepRap)" },
"machine_start_gcode": {
"default_value": "G21 ; set units to millimeters\nG90 ; use absolute positioning\nM82 ; absolute extrusion mode\nM104 S{material_print_temperature_layer_0} ; set extruder temp\nM140 S{material_bed_temperature_layer_0} ; set bed temp\nM190 S{material_bed_temperature_layer_0} ; wait for bed temp\nM109 S{material_print_temperature_layer_0} ; wait for extruder temp\nG28 W ; home all\nG92 E0.0 ; reset extruder distance position\nG1 Y-3.0 F1000.0 ; go outside print area\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E21.5 F1000.0 ; intro line\nG92 E0.0 ; reset extruder distance position"
},
"machine_end_gcode": {
"default_value": "G28 Z\nG28 X\nG28 Y\nM107 ; Turn off the fan\nG91; Relative positioning\nG1 E-1 ; reduce filament pressure\nM104 T0 S0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn the steppers off"
}
}
}

View file

@ -1,66 +0,0 @@
{
"version": 2,
"name": "Deltacomb 3D",
"inherits": "fdmprinter",
"metadata": {
"author": "Gabriele Rossetti",
"visible": true,
"manufacturer": "Deltacomb 3D",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"platform": "deltacomb.3mf",
"has_machine_quality": true,
"has_materials": true,
"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_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": { "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": { "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": { "value": "True" },
"jerk_infill" : { "value": "5" },
"jerk_support" : { "value": "5" },
"acceleration_enabled": { "value": "1" },
"acceleration_travel" : { "value": 5000 },
"machine_max_feedrate_z" : { "default_value": 300 }
}
}

View file

@ -0,0 +1,92 @@
{
"version": 2,
"name": "Deltacomb Base Printer",
"inherits": "fdmprinter",
"metadata": {
"author": "Gabriele Rossetti",
"visible": false,
"manufacturer": "Deltacomb 3D Printers",
"file_formats": "text/x-gcode",
"has_machine_quality": true,
"has_materials": true,
"has_variants": true,
"variants_name": "Head",
"preferred_quality_type": "d",
"preferred_material": "generic_pla",
"machine_extruder_trains": {
"0": "deltacomb_base_extruder_0",
"1": "deltacomb_base_extruder_1",
"2": "deltacomb_base_extruder_2",
"3": "deltacomb_base_extruder_3"
}
},
"overrides": {
"machine_extruder_count": { "default_value": 1, "maximum_value": "4" },
"machine_heated_bed": { "default_value": true },
"machine_center_is_zero": { "default_value": true },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": { "default_value": ";---------------------------------------\n;Deltacomb start script\n;---------------------------------------\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 ;Home all axes (max endstops)\nG92 E0 ;zero the extruded length\nG1 Z15.0 F9000 ;move to the platform down 15mm\nG1 F9000\n\n;Put printing message on LCD screen\nM117 Printing...\n;---------------------------------------"},
"machine_end_gcode": { "default_value": ";---------------------------------------\n;Deltacomb end script\n;---------------------------------------\nG91 ;relative positioning\nG1 F15000 X8.0 E-4.5 ;Wipe filament+material retraction\nG1 F15000 E4.0 Z1 ;Retraction compensation\nG28 ;Home all axes (max endstops)\nM84 ;steppers off\n" },
"machine_shape": { "default_value": "elliptic" },
"machine_max_feedrate_z" : { "default_value": 300 },
"speed_print" : { "default_value": 80 },
"speed_topbottom": { "value": "speed_print * 0.90" },
"speed_wall_0": { "value": "35 if speed_print > 35 else speed_print" },
"speed_layer_0": { "value": "speed_print * 0.55" },
"speed_travel": { "value": "170" },
"speed_travel_layer_0": { "value": "speed_travel * 0.70" },
"speed_z_hop": { "value": "speed_travel" },
"acceleration_enabled": { "value": "True" },
"acceleration_travel" : { "value": "9000" },
"acceleration_print": { "value": "4000" },
"acceleration_wall": { "value": "acceleration_print * 0.5" },
"acceleration_wall_0": { "value": "acceleration_wall * 0.5" },
"acceleration_topbottom": { "value": "acceleration_wall_0" },
"acceleration_layer_0": { "value": "acceleration_wall_0" },
"acceleration_prime_tower": { "value": "acceleration_wall" },
"acceleration_support": { "value": "acceleration_wall" },
"acceleration_support_interface": { "value": "acceleration_wall_0" },
"jerk_enabled": { "value": "True" },
"jerk_print": { "value": "25" },
"jerk_infill": { "value": "10" },
"jerk_travel": { "value": "10" },
"retraction_hop_enabled": { "default_value": true },
"retraction_hop": { "default_value": 0.5 },
"retraction_amount" : { "default_value": 3.5 },
"retraction_speed" : { "default_value": 70 },
"retraction_combing" : { "default_value": "noskin" },
"travel_avoid_distance": { "value": "1" },
"top_bottom_thickness": { "default_value": 0.8 },
"roofing_layer_count": { "value": "1" },
"roofing_line_width": { "value": "line_width * 0.75" },
"infill_sparse_density": { "default_value": 30 },
"infill_pattern": { "value": "'cubic'" },
"infill_before_walls": { "default_value": false },
"support_z_distance": { "value": "layer_height * 2" },
"support_bottom_distance": { "value": "layer_height" },
"support_use_towers" : { "default_value": false },
"support_bottom_enable" : { "value": "0" },
"skirt_brim_speed": { "value": "speed_layer_0" },
"skirt_line_count": { "default_value": 3 },
"skirt_brim_minimal_length": { "default_value": 150 },
"brim_width": { "value": "3" },
"prime_tower_size": { "value": "math.sqrt(extruders_enabled_count * prime_tower_min_volume / layer_height / math.pi) * 2"},
"prime_tower_position_x": { "value": "prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') 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')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0)" },
"prime_tower_position_y": { "value": "machine_depth / 2 - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') 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')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1" }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Deltacomb DC-20",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm"
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-20" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 250 },
"machine_extruder_count": { "default_value": 1, "maximum_value": "4" }
}
}

View file

@ -0,0 +1,24 @@
{
"version": 2,
"name": "Deltacomb DC-20 DUAL",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "DBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc20dual_extruder_0",
"1": "deltacomb_dc20dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-20 DUAL" },
"machine_extruder_count": { "default_value": 2, "maximum_value": "2" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 250 }
}
}

View file

@ -0,0 +1,29 @@
{
"version": 2,
"name": "Deltacomb DC-20 FLUX",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc20flux_extruder_0",
"1": "deltacomb_dc20flux_extruder_1",
"2": "deltacomb_dc20flux_extruder_2",
"3": "deltacomb_dc20flux_extruder_3"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-20 FLUX" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 250 },
"machine_extruder_count": { "default_value": 2, "maximum_value": "4" },
"switch_extruder_retraction_amount": { "value": "0" },
"prime_tower_min_volume": { "value": "45" },
"prime_tower_enable": { "value": "1" }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Deltacomb DC-21",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm"
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-21" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 400 },
"machine_extruder_count": { "default_value": 1, "maximum_value": "4" }
}
}

View file

@ -0,0 +1,24 @@
{
"version": 2,
"name": "Deltacomb DC-21 DUAL",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "DBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc20dual_extruder_0",
"1": "deltacomb_dc20dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-20 DUAL" },
"machine_extruder_count": { "default_value": 2, "maximum_value": "2" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 400 }
}
}

View file

@ -0,0 +1,29 @@
{
"version": 2,
"name": "Deltacomb DC-21 FLUX",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc20.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc20flux_extruder_0",
"1": "deltacomb_dc20flux_extruder_1",
"2": "deltacomb_dc20flux_extruder_2",
"3": "deltacomb_dc20flux_extruder_3"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-20 FLUX" },
"machine_width": { "default_value": 190 },
"machine_depth": { "default_value": 190 },
"machine_height": { "default_value": 400 },
"machine_extruder_count": { "default_value": 2, "maximum_value": "4" },
"switch_extruder_retraction_amount": { "value": "0" },
"prime_tower_min_volume": { "value": "45" },
"prime_tower_enable": { "value": "1" }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Deltacomb DC-30",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc30.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm"
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-30" },
"machine_width": { "default_value": 290 },
"machine_depth": { "default_value": 290 },
"machine_height": { "default_value": 300 },
"machine_extruder_count": { "default_value": 1, "maximum_value": "4" }
}
}

View file

@ -0,0 +1,24 @@
{
"version": 2,
"name": "Deltacomb DC-30 DUAL",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc30.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "DBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc30dual_extruder_0",
"1": "deltacomb_dc30dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-30 DUAL" },
"machine_width": { "default_value": 290 },
"machine_depth": { "default_value": 290 },
"machine_height": { "default_value": 300 },
"machine_extruder_count": { "default_value": 2, "maximum_value": "2" }
}
}

View file

@ -0,0 +1,29 @@
{
"version": 2,
"name": "Deltacomb DC-30 FLUX",
"inherits": "deltacomb_base",
"metadata": {
"visible": true,
"platform": "deltacomb_dc30.stl",
"quality_definition": "deltacomb_base",
"preferred_variant_name": "FBE 0.40mm",
"machine_extruder_trains": {
"0": "deltacomb_dc30flux_extruder_0",
"1": "deltacomb_dc30flux_extruder_1",
"2": "deltacomb_dc30flux_extruder_2",
"3": "deltacomb_dc30flux_extruder_3"
}
},
"overrides": {
"machine_name": { "default_value": "Deltacomb DC-30 FLUX" },
"machine_width": { "default_value": 290 },
"machine_depth": { "default_value": 290 },
"machine_height": { "default_value": 300 },
"machine_extruder_count": { "default_value": 2, "maximum_value": "4" },
"switch_extruder_retraction_amount": { "value": "0" },
"prime_tower_min_volume": { "value": "45" },
"prime_tower_enable": { "value": "1" }
}
}

View file

@ -1575,7 +1575,7 @@
"type": "float", "type": "float",
"unit": "mm", "unit": "mm",
"default_value": 0.35, "default_value": 0.35,
"value": "wall_line_width_0 / 2", "value": "wall_line_width_0 / 2 + (ironing_line_spacing - skin_line_width * (1.0 + ironing_flow / 100) / 2 if ironing_pattern == 'concentric' else skin_line_width * (1.0 - ironing_flow / 100) / 2)",
"minimum_value_warning": "0", "minimum_value_warning": "0",
"maximum_value_warning": "wall_line_width_0", "maximum_value_warning": "wall_line_width_0",
"enabled": "ironing_enabled", "enabled": "ironing_enabled",
@ -1761,7 +1761,7 @@
"description": "A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees for the lines and zig zag patterns and 45 degrees for all other patterns).", "description": "A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees for the lines and zig zag patterns and 45 degrees for all other patterns).",
"type": "[int]", "type": "[int]",
"default_value": "[ ]", "default_value": "[ ]",
"enabled": "infill_pattern != 'concentric' and infill_pattern != 'cubicsubdiv' and infill_sparse_density > 0", "enabled": "infill_pattern != 'concentric' and infill_sparse_density > 0",
"limit_to_extruder": "infill_extruder_nr", "limit_to_extruder": "infill_extruder_nr",
"settable_per_mesh": true "settable_per_mesh": true
}, },

View file

@ -21,11 +21,12 @@
"default_value": 220 "default_value": 220
}, },
"machine_height": { "machine_height": {
"default_value": 220 "default_value": 260
}, },
"machine_depth": { "machine_depth": {
"default_value": 260 "default_value": 220
}, "machine_center_is_zero": { },
"machine_center_is_zero": {
"default_value": false "default_value": false
}, },
"layer_height": { "default_value": 0.1 }, "layer_height": { "default_value": 0.1 },

View file

@ -27,7 +27,8 @@
}, },
"machine_depth": { "machine_depth": {
"default_value": 260 "default_value": 260
}, "machine_center_is_zero": { },
"machine_center_is_zero": {
"default_value": false "default_value": false
}, },
"layer_height": { "default_value": 0.1 }, "layer_height": { "default_value": 0.1 },

View file

@ -70,7 +70,7 @@
"material_bed_temp_wait": {"default_value": false }, "material_bed_temp_wait": {"default_value": false },
"machine_max_feedrate_z": {"default_value": 10 }, "machine_max_feedrate_z": {"default_value": 10 },
"machine_acceleration": {"default_value": 180 }, "machine_acceleration": {"default_value": 180 },
"machine_start_gcode": {"default_value": "\n;Neither Hybrid AM Systems nor any of Hybrid AM Systems representatives has any liabilities or gives any warranties on this .gcode file, or on any or all objects made with this .gcode file.\n\nM140 S{material_bed_temperature_layer_0}\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\n\nG1 Z10 F900\nG1 X-25 Y20 F12000\n\nM190 S{material_bed_temperature_layer_0}\nM117 HMS434 Printing ...\n\nM42 P10 S255 ; chamberfans on" }, "machine_start_gcode": {"default_value": "\n;Neither Hybrid AM Systems nor any of Hybrid AM Systems representatives has any liabilities or gives any warranties on this .gcode file, or on any or all objects made with this .gcode file.\n\nM114\n\nM140 S{material_bed_temperature_layer_0}\nM118 // action:chamber_fan_on\nM141 S{build_volume_temperature}\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\n\nG1 Z10 F900\nG1 X-25 Y20 F12000\n\nM190 S{material_bed_temperature_layer_0}\n\nM117 HMS434 Printing ..." },
"machine_end_gcode": {"default_value": "" }, "machine_end_gcode": {"default_value": "" },
"retraction_extra_prime_amount": {"minimum_value_warning": "-2.0" }, "retraction_extra_prime_amount": {"minimum_value_warning": "-2.0" },
@ -104,7 +104,7 @@
"skin_outline_count": {"value": "0"}, "skin_outline_count": {"value": "0"},
"ironing_line_spacing": {"value": "line_width / 4 * 3"}, "ironing_line_spacing": {"value": "line_width / 4 * 3"},
"ironing_flow": {"value": "0"}, "ironing_flow": {"value": "0"},
"ironing_inset": {"value": "ironing_line_spacing"}, "ironing_inset": {"value": "ironing_line_spacing + (ironing_line_spacing - skin_line_width * (1.0 + ironing_flow / 100) / 2 if ironing_pattern == 'concentric' else skin_line_width * (1.0 - ironing_flow / 100) / 2)"},
"speed_ironing": {"value": "150"}, "speed_ironing": {"value": "150"},
"infill_sparse_density": {"value": 30}, "infill_sparse_density": {"value": 30},

View file

@ -0,0 +1,72 @@
{
"name": "Lotmaxx Shark",
"version": 2,
"inherits": "fdmprinter",
"overrides": {
"machine_name": { "default_value": "Lotmaxx Shark" },
"machine_width": { "default_value": 235 },
"machine_depth": { "default_value": 235 },
"machine_height": { "default_value": 265 },
"machine_head_with_fans_polygon": { "default_value": [
[-50.7,16.8],
[-50.7,-29.5],
[46.9,-29.5],
[49.9,16.8]
]
},
"gantry_height": { "value": 29 },
"machine_heated_bed": {"value": true},
"machine_start_gcode":{
"default_value":"G28 ;Home\nG92 E0 ;Reset Extruder\nG1 Z4.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n"
},
"machine_end_gcode":{
"default_value":"G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n"
},
"acceleration_print":{"value":1000},
"acceleration_travel":{"value":1000},
"acceleration_travel_layer_0":{"value":1000.0},
"expand_skins_expand_distance":{"value":0.8},
"fill_outline_gaps":{"default_value":false},
"infill_sparse_density":{"value":15},
"meshfix_maximum_resolution":{"value":0.05},
"optimize_wall_printing_order":{"value":true},
"retract_at_layer_change":{"value":false},
"retraction_amount":{"value":4.5},
"roofing_layer_count":{"value":1},
"skin_preshrink":{"value":0.8},
"speed_layer_0":{"value":30},
"speed_print":{"value":45},
"speed_roofing":{"value":35},
"speed_topbottom":{"value":35},
"speed_travel":{"value":80},
"speed_wall_0":{"value":32},
"speed_wall_x":{"value":32},
"support_infill_rate":{"value":5},
"support_pattern":{"default_value":"lines"},
"support_use_towers":{"value":false},
"wall_overhang_speed_factor":{"value":50},
"z_seam_corner":{"default_value":"z_seam_corner_any"},
"z_seam_relative":{"value":true},
"z_seam_type":{"default_value":"sharpest_corner"},
"zig_zaggify_infill":{"value":true},
"adhesion_type":{"default_value":"skirt"},
"prime_tower_enable":{"value":true},
"prime_tower_position_x":{"value": 50},
"prime_tower_position_y":{"value": 50},
"prime_tower_min_volume":{"value": 30},
"switch_extruder_retraction_amount": {"value": 100},
"switch_extruder_retraction_speeds": {"value": 60}
},
"metadata": {
"visible": true,
"author": "lotmaxx.com",
"manufacturer": "Lotmaxx",
"platform": "lotmaxx_sc_10_20_platform.3mf",
"machine_extruder_trains": {
"0": "lotmaxx_sc60_extruder_left",
"1": "lotmaxx_sc60_extruder_right"
},
"has_materials": true,
"preferred_quality_type": "normal"
}
}

View file

@ -345,7 +345,7 @@
"value": "0.8" "value": "0.8"
}, },
"ironing_inset": { "ironing_inset": {
"value": "0.2" "value": "0.2 + (ironing_line_spacing - skin_line_width * (1.0 + ironing_flow / 100) / 2 if ironing_pattern == 'concentric' else skin_line_width * (1.0 - ironing_flow / 100) / 2)"
}, },
"jerk_travel": { "jerk_travel": {
"value": "10" "value": "10"

View file

@ -11,7 +11,8 @@
"platform": "prusai3_platform.3mf", "platform": "prusai3_platform.3mf",
"machine_extruder_trains": "machine_extruder_trains":
{ {
"0": "tevo_tarantula_extruder_0" "0": "tevo_tarantula_extruder_0",
"1": "tevo_tarantula_extruder_1"
} }
}, },

View file

@ -11,7 +11,8 @@
"has_materials": true, "has_materials": true,
"machine_extruder_trains": "machine_extruder_trains":
{ {
"0": "tevo_tarantula_pro_extruder_0" "0": "tevo_tarantula_pro_extruder_0",
"1": "tevo_tarantula_pro_extruder_1"
} }
}, },

View file

@ -0,0 +1,17 @@
{
"version": 2,
"name": "I3MetalMotion extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "I3MetalMotion",
"position": "0"
},
"overrides": {
"extruder_nr": { "default_value": 0 },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_offset_x": { "default_value": 0 },
"machine_nozzle_offset_y": { "default_value": 0 }
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_base",
"position": "0"
},
"overrides": {
"extruder_nr": { "default_value": 0, "maximum_value": "3" },
"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 },
"machine_extruder_start_code": { "default_value": ";Deltacomb Extruder 1\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";Deltacomb Extruder 1\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_base",
"position": "1"
},
"overrides": {
"extruder_nr": { "default_value": 1, "maximum_value": "3" },
"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 },
"machine_extruder_start_code": { "default_value": ";Deltacomb Extruder 2\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";Deltacomb Extruder 2\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 3",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_base",
"position": "2"
},
"overrides": {
"extruder_nr": { "default_value": 2, "maximum_value": "3" },
"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 },
"machine_extruder_start_code": { "default_value": ";Deltacomb Extruder 3\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";Deltacomb Extruder 3\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 4",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_base",
"position": "3"
},
"overrides": {
"extruder_nr": { "default_value": 3, "maximum_value": "3" },
"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 },
"machine_extruder_start_code": { "default_value": ";Deltacomb Extruder 4\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";Deltacomb Extruder 4\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20dual",
"position": "0"
},
"overrides": {
"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 },
"machine_extruder_start_code": { "default_value": ";DC20 Dual Extruder 1\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";DC20 Dual Extruder 1\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20dual",
"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": 19 },
"machine_nozzle_offset_y": { "default_value": 0 },
"machine_extruder_start_code": { "default_value": ";DC20 Dual Extruder 2\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";DC20 Dual Extruder 2\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20flux",
"position": "0"
},
"overrides": {
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 1 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 1 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"}
}
}

View file

@ -0,0 +1,21 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20flux",
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 2 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 2 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -0,0 +1,21 @@
{
"version": 2,
"name": "Extruder 3",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20flux",
"position": "2"
},
"overrides": {
"extruder_nr": { "default_value": 2 },
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 3 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 3 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -0,0 +1,21 @@
{
"version": 2,
"name": "Extruder 4",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc20flux",
"position": "3"
},
"overrides": {
"extruder_nr": { "default_value": 3 },
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 4 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC20 Flux Extruder 4 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30dual",
"position": "0"
},
"overrides": {
"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 },
"machine_extruder_start_code": { "default_value": ";DC30 Dual Extruder 1\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";DC30 Dual Extruder 1\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30dual",
"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": 19 },
"machine_nozzle_offset_y": { "default_value": 0 },
"machine_extruder_start_code": { "default_value": ";DC30 Dual Extruder 2\n;Put your custom code here"},
"machine_extruder_end_code": { "default_value": ";DC30 Dual Extruder 2\n;Put your custom code here"}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30flux",
"position": "0"
},
"overrides": {
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 1 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 1 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"}
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30flux",
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 2 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 2 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 3",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30flux",
"position": "2"
},
"overrides": {
"extruder_nr": { "default_value": 2 },
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 3 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 3 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 4",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb_dc30flux",
"position": "3"
},
"overrides": {
"extruder_nr": { "default_value": 3 },
"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 },
"machine_extruder_start_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 4 Start\n;---------------------------------------\nG92 E0 ;zero the extruded length\nG91 ;use relative coordinates\nG1 E68 F10000 ; fast insert\nG92 E0 ;zero the extruded length\nG90 ;absolute positioning\n;---------------------------------------\n;---------------------------------------"},
"machine_extruder_end_code": { "default_value": ";---------------------------------------\n;DC30 Flux Extruder 4 End\n;---------------------------------------\nG91 ;use relative coordinates\nG0 E-15 F10000\nG1 Z2 ;lift head\nG4 P3000\nG0 E14.7 F10000\nG1 E-69.7 F10000\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\n;---------------------------------------\n;---------------------------------------"},
"prime_tower_flow": { "value": "200" }
}
}

View file

@ -1,17 +0,0 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "deltacomb",
"position": "0"
},
"overrides": {
"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 }
}
}

View file

@ -1,17 +0,0 @@
{
"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 }
}
}

View file

@ -16,7 +16,7 @@
"machine_nozzle_offset_y": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 },
"material_diameter": { "default_value": 1.75 }, "material_diameter": { "default_value": 1.75 },
"machine_extruder_start_code": { "machine_extruder_start_code": {
"default_value": "\n;changing to tool1\nM83\nM109 T0 S{material_print_temperature}\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" "default_value": "\n;changing to tool1\nM83\nM109 T0 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n"
}, },
"machine_extruder_end_code": { "machine_extruder_end_code": {
"default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T0 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool1\n\n" "default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T0 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool1\n\n"

View file

@ -16,7 +16,7 @@
"machine_nozzle_offset_y": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 },
"material_diameter": { "default_value": 1.75 }, "material_diameter": { "default_value": 1.75 },
"machine_extruder_start_code": { "machine_extruder_start_code": {
"default_value": "\n;changing to tool2\nM83\nM109 T1 S{material_print_temperature}\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" "default_value": "\n;changing to tool2\nM83\nM109 T1 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n"
}, },
"machine_extruder_end_code": { "machine_extruder_end_code": {
"default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T1 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool2\n\n" "default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T1 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool2\n\n"

View file

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

View file

@ -0,0 +1,17 @@
{
"version": 2,
"name": "Extruder 2",
"inherits": "fdmextruder",
"metadata": {
"machine": "lotmaxx_sc60",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

View file

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

View file

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

Binary file not shown.

Binary file not shown.

View file

@ -108,7 +108,15 @@ Item
} }
} }
onClicked: popup.opened ? popup.close() : popup.open() onClicked: {
if (popup.opened)
{
popup.close()
} else {
Cura.API.account.popupOpened()
popup.open()
}
}
} }
Popup Popup
@ -119,6 +127,7 @@ Item
x: parent.width - width x: parent.width - width
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
onOpened: Cura.API.account.popupOpened()
opacity: opened ? 1 : 0 opacity: opened ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } } Behavior on opacity { NumberAnimation { duration: 100 } }

View file

@ -7,11 +7,7 @@ import Cura 1.1 as Cura
Row // sync state icon + message Row // sync state icon + message
{ {
property alias iconSource: icon.source id: syncRow
property alias labelText: stateLabel.text
property alias syncButtonVisible: accountSyncButton.visible
property alias animateIconRotation: updateAnimator.running
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -23,7 +19,7 @@ Row // sync state icon + message
width: 20 * screenScaleFactor width: 20 * screenScaleFactor
height: width height: width
source: UM.Theme.getIcon("update") source: Cura.API.account.manualSyncEnabled ? UM.Theme.getIcon("update") : UM.Theme.getIcon("checked")
color: palette.text color: palette.text
RotationAnimator RotationAnimator
@ -54,10 +50,11 @@ Row // sync state icon + message
Label Label
{ {
id: stateLabel id: stateLabel
text: catalog.i18nc("@state", "Checking...") text: catalog.i18nc("@state", catalog.i18nc("@label", "You are in sync with your account"))
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering renderType: Text.NativeRendering
visible: !Cura.API.account.manualSyncEnabled
} }
Label Label
@ -67,11 +64,13 @@ Row // sync state icon + message
color: UM.Theme.getColor("secondary_button_text") color: UM.Theme.getColor("secondary_button_text")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering renderType: Text.NativeRendering
visible: Cura.API.account.manualSyncEnabled
height: visible ? accountSyncButton.intrinsicHeight : 0
MouseArea MouseArea
{ {
anchors.fill: parent anchors.fill: parent
onClicked: Cura.API.account.sync() onClicked: Cura.API.account.sync(true)
hoverEnabled: true hoverEnabled: true
onEntered: accountSyncButton.font.underline = true onEntered: accountSyncButton.font.underline = true
onExited: accountSyncButton.font.underline = false onExited: accountSyncButton.font.underline = false
@ -82,25 +81,25 @@ Row // sync state icon + message
signal syncStateChanged(string newState) signal syncStateChanged(string newState)
onSyncStateChanged: { onSyncStateChanged: {
if(newState == Cura.AccountSyncState.SYNCING){ if(newState == Cura.AccountSyncState.IDLE){
syncRow.iconSource = UM.Theme.getIcon("update") icon.source = UM.Theme.getIcon("update")
syncRow.labelText = catalog.i18nc("@label", "Checking...") } else if(newState == Cura.AccountSyncState.SYNCING){
icon.source = UM.Theme.getIcon("update")
stateLabel.text = catalog.i18nc("@label", "Checking...")
} else if (newState == Cura.AccountSyncState.SUCCESS) { } else if (newState == Cura.AccountSyncState.SUCCESS) {
syncRow.iconSource = UM.Theme.getIcon("checked") icon.source = UM.Theme.getIcon("checked")
syncRow.labelText = catalog.i18nc("@label", "You are up to date") stateLabel.text = catalog.i18nc("@label", "You are in sync with your account")
} else if (newState == Cura.AccountSyncState.ERROR) { } else if (newState == Cura.AccountSyncState.ERROR) {
syncRow.iconSource = UM.Theme.getIcon("warning_light") icon.source = UM.Theme.getIcon("warning_light")
syncRow.labelText = catalog.i18nc("@label", "Something went wrong...") stateLabel.text = catalog.i18nc("@label", "Something went wrong...")
} else { } else {
print("Error: unexpected sync state: " + newState) print("Error: unexpected sync state: " + newState)
} }
if(newState == Cura.AccountSyncState.SYNCING){ if(newState == Cura.AccountSyncState.SYNCING){
syncRow.animateIconRotation = true updateAnimator.running = true
syncRow.syncButtonVisible = false
} else { } else {
syncRow.animateIconRotation = false updateAnimator.running = false
syncRow.syncButtonVisible = true
} }
} }

View file

@ -9,7 +9,10 @@ import Cura 1.1 as Cura
Column Column
{ {
width: Math.max(title.width, accountButton.width) + 2 * UM.Theme.getSize("default_margin").width width: Math.max(
Math.max(title.width, accountButton.width) + 2 * UM.Theme.getSize("default_margin").width,
syncRow.width
)
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
@ -29,13 +32,10 @@ Column
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
} }
SyncState SyncState {
{
id: syncRow id: syncRow
} }
Label Label
{ {
id: lastSyncLabel id: lastSyncLabel

View file

@ -35,7 +35,8 @@ Item
property color headerActiveColor: UM.Theme.getColor("secondary") property color headerActiveColor: UM.Theme.getColor("secondary")
property color headerHoverColor: UM.Theme.getColor("action_button_hovered") property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
property alias enabled: mouseArea.enabled property alias mouseArea: headerMouseArea
property alias enabled: headerMouseArea.enabled
// Text to show when this component is disabled // Text to show when this component is disabled
property alias disabledText: disabledLabel.text property alias disabledText: disabledLabel.text
@ -139,6 +140,16 @@ Item
anchors.fill: parent anchors.fill: parent
visible: base.enabled visible: base.enabled
MouseArea
{
id: headerMouseArea
anchors.fill: parent
onClicked: toggleContent()
hoverEnabled: true
onEntered: background.color = headerHoverColor
onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
}
Loader Loader
{ {
id: headerItemLoader id: headerItemLoader
@ -180,15 +191,6 @@ Item
} }
} }
MouseArea
{
id: mouseArea
anchors.fill: parent
onClicked: toggleContent()
hoverEnabled: true
onEntered: background.color = headerHoverColor
onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
}
} }
DropShadow DropShadow

View file

@ -24,7 +24,7 @@ Menu
{ {
text: text:
{ {
var path = modelData.toString() var path = decodeURIComponent(modelData.toString())
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
} }
onTriggered: onTriggered:

View file

@ -154,7 +154,7 @@ UM.PreferencesPage
Component.onCompleted: { Component.onCompleted: {
append({ text: "English", code: "en_US" }) append({ text: "English", code: "en_US" })
append({ text: "Czech", code: "cs_CZ" }) append({ text: "Čeština", code: "cs_CZ" })
append({ text: "Deutsch", code: "de_DE" }) append({ text: "Deutsch", code: "de_DE" })
append({ text: "Español", code: "es_ES" }) append({ text: "Español", code: "es_ES" })
//Finnish is disabled for being incomplete: append({ text: "Suomi", code: "fi_FI" }) //Finnish is disabled for being incomplete: append({ text: "Suomi", code: "fi_FI" })

View file

@ -136,6 +136,7 @@ UM.ManagementPage
{ {
id: confirmDialog id: confirmDialog
object: base.currentItem && base.currentItem.name ? base.currentItem.name : "" object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""
text: base.currentItem ? base.currentItem.removalWarning : "";
onYes: onYes:
{ {
Cura.MachineManager.removeMachine(base.currentItem.id) Cura.MachineManager.removeMachine(base.currentItem.id)

View file

@ -5,16 +5,49 @@ import QtQuick 2.7
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.0 as Cura import Cura 1.1 as Cura
Cura.ExpandablePopup Cura.ExpandablePopup
{ {
id: machineSelector id: machineSelector
property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasNetworkConnection property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasNetworkConnection
property bool isCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection property bool isConnectedCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection
property bool isCloudRegistered: Cura.MachineManager.activeMachineHasCloudRegistration
property bool isGroup: Cura.MachineManager.activeMachineIsGroup property bool isGroup: Cura.MachineManager.activeMachineIsGroup
readonly property string connectionStatus: {
if (isNetworkPrinter)
{
return "printer_connected"
}
else if (isConnectedCloudPrinter && Cura.API.connectionStatus.isInternetReachable)
{
return "printer_cloud_connected"
}
else if (isCloudRegistered)
{
return "printer_cloud_not_available"
}
else
{
return ""
}
}
readonly property string connectionStatusMessage: {
if (connectionStatus == "printer_cloud_not_available")
{
if(Cura.API.connectionStatus.isInternetReachable){
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection and sign in to connect to the cloud printer.")
} else {
return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection.")
}
} else {
return ""
}
}
contentPadding: UM.Theme.getSize("default_lining").width contentPadding: UM.Theme.getSize("default_lining").width
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
@ -44,7 +77,7 @@ Cura.ExpandablePopup
{ {
return UM.Theme.getIcon("printer_group") return UM.Theme.getIcon("printer_group")
} }
else if (isNetworkPrinter || isCloudPrinter) else if (isNetworkPrinter || isCloudRegistered)
{ {
return UM.Theme.getIcon("printer_single") return UM.Theme.getIcon("printer_single")
} }
@ -59,6 +92,7 @@ Cura.ExpandablePopup
UM.RecolorImage UM.RecolorImage
{ {
id: connectionStatusImage
anchors anchors
{ {
bottom: parent.bottom bottom: parent.bottom
@ -66,27 +100,14 @@ Cura.ExpandablePopup
leftMargin: UM.Theme.getSize("thick_margin").width leftMargin: UM.Theme.getSize("thick_margin").width
} }
source: source: UM.Theme.getIcon(connectionStatus)
{
if (isNetworkPrinter)
{
return UM.Theme.getIcon("printer_connected")
}
else if (isCloudPrinter)
{
return UM.Theme.getIcon("printer_cloud_connected")
}
else
{
return ""
}
}
width: UM.Theme.getSize("printer_status_icon").width width: UM.Theme.getSize("printer_status_icon").width
height: UM.Theme.getSize("printer_status_icon").height height: UM.Theme.getSize("printer_status_icon").height
color: UM.Theme.getColor("primary") color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary")
visible: isNetworkPrinter || isCloudPrinter
visible: isNetworkPrinter || isCloudRegistered
// Make a themable circle in the background so we can change it in other themes // Make a themable circle in the background so we can change it in other themes
Rectangle Rectangle
@ -100,6 +121,38 @@ Cura.ExpandablePopup
color: UM.Theme.getColor("main_background") color: UM.Theme.getColor("main_background")
z: parent.z - 1 z: parent.z - 1
} }
}
MouseArea // Connection status tooltip hover area
{
id: connectionStatusTooltipHoverArea
anchors.fill: parent
hoverEnabled: connectionStatusMessage !== ""
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.show()
}
onExited: { tooltip.hide() }
}
Cura.ToolTip
{
id: tooltip
width: 250 * screenScaleFactor
tooltipText: connectionStatusMessage
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
z: popup.z + 1
targetPoint: Qt.point(
connectionStatusImage.x + Math.round(connectionStatusImage.width / 2),
connectionStatusImage.y
)
} }
} }

View file

@ -19,12 +19,20 @@ ToolTip
property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight
property alias tooltipText: tooltip.text property alias tooltipText: tooltip.text
property alias arrowSize: backgroundRect.arrowSize
property var targetPoint: Qt.point(parent.x, y + Math.round(height/2)) property var targetPoint: Qt.point(parent.x, y + Math.round(height/2))
id: tooltip id: tooltip
text: "" text: ""
delay: 500 delay: 500
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
visible: opacity != 0.0
opacity: 0.0 // initially hidden
Behavior on opacity
{
NumberAnimation { duration: 100; }
}
// If the text is not set, just set the height to 0 to prevent it from showing // If the text is not set, just set the height to 0 to prevent it from showing
height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0 height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0
@ -60,4 +68,12 @@ ToolTip
color: UM.Theme.getColor("tooltip_text") color: UM.Theme.getColor("tooltip_text")
renderType: Text.NativeRendering renderType: Text.NativeRendering
} }
function show() {
opacity = 1
}
function hide() {
opacity = 0
}
} }

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = A
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = a
material = generic_abs
variant = DBE 0.25mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 0.5
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 80

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = B
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = b
material = generic_abs
variant = DBE 0.25mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 0.5
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 55

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = C
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = c
material = generic_abs
variant = DBE 0.25mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 0.5
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 32

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = A
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = a
material = generic_abs
variant = DBE 0.40mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = B
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = b
material = generic_abs
variant = DBE 0.40mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = C
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = c
material = generic_abs
variant = DBE 0.40mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = D
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = d
material = generic_abs
variant = FBE 0.40mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = E
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = e
material = generic_abs
variant = DBE 0.40mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = C
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = c
material = generic_abs
variant = DBE 0.60mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,21 @@
[general]
version = 4
name = D
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = d
material = generic_abs
variant = FBE 0.60mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = E
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = e
material = generic_abs
variant = DBE 0.60mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 65

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = A
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = f
material = generic_abs
variant = DBE 0.60mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 1
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 45

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = A
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = a
material = generic_abs
variant = FBE 0.25mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 0.5
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 80

View file

@ -0,0 +1,22 @@
[general]
version = 4
name = B
definition = deltacomb_base
[metadata]
setting_version = 15
type = quality
quality_type = b
material = generic_abs
variant = FBE 0.25mm
[values]
adhesion_type = raft
cool_fan_full_at_height = 0.5
cool_fan_speed = 50
cool_fan_speed_max = 50
cool_fan_speed_min = 0
material_bed_temperature = 100
material_bed_temperature_layer_0 = 80
material_print_temperature = 240
speed_print = 55

Some files were not shown because too many files have changed in this diff Show more