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
SUCCESS = 1
ERROR = 2
IDLE = 3
class Account(QObject):
"""The account API provides a version-proof bridge to use Ultimaker Accounts
@ -54,6 +55,7 @@ class Account(QObject):
"""
lastSyncDateTimeChanged = pyqtSignal()
syncStateChanged = pyqtSignal(int) # because SyncState is an int Enum
manualSyncEnabledChanged = pyqtSignal(bool)
def __init__(self, application: "CuraApplication", parent = None) -> None:
super().__init__(parent)
@ -62,7 +64,8 @@ class Account(QObject):
self._error_message = None # type: Optional[Message]
self._logged_in = False
self._sync_state = SyncState.SUCCESS
self._sync_state = SyncState.IDLE
self._manual_sync_enabled = False
self._last_sync_str = "-"
self._callback_port = 32118
@ -110,16 +113,21 @@ class Account(QObject):
:param state: One of SyncState
"""
Logger.info("Service {service} enters sync state {state}", service = service_name, state = state)
prev_state = self._sync_state
self._sync_services[service_name] = state
if any(val == SyncState.SYNCING for val in self._sync_services.values()):
self._sync_state = SyncState.SYNCING
self._setManualSyncEnabled(False)
elif any(val == SyncState.ERROR for val in self._sync_services.values()):
self._sync_state = SyncState.ERROR
self._setManualSyncEnabled(True)
else:
self._sync_state = SyncState.SUCCESS
self._setManualSyncEnabled(False)
if self._sync_state != prev_state:
self.syncStateChanged.emit(self._sync_state)
@ -162,11 +170,31 @@ class Account(QObject):
self._logged_in = logged_in
self.loginStateChanged.emit(logged_in)
if logged_in:
self.sync()
self._setManualSyncEnabled(False)
self._sync()
else:
if self._update_timer.isActive():
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(bool)
def login(self, force_logout_before_login: bool = False) -> None:
@ -217,20 +245,23 @@ class Account(QObject):
def lastSyncDateTime(self) -> str:
return self._last_sync_str
@pyqtProperty(bool, notify=manualSyncEnabledChanged)
def manualSyncEnabled(self) -> bool:
return self._manual_sync_enabled
@pyqtSlot()
def sync(self) -> None:
"""Signals all sync services to start syncing
@pyqtSlot(bool)
def sync(self, user_initiated: bool = False) -> None:
if user_initiated:
self._setManualSyncEnabled(False)
This can be considered a forced sync: even when a
sync is currently running, a sync will be requested.
"""
self._sync()
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()
@pyqtSlot()
def popupOpened(self) -> None:
self._setManualSyncEnabled(True)
self._sync_state = SyncState.IDLE
self.syncStateChanged.emit(self._sync_state)
@pyqtSlot()
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 cura.API.Backups import Backups
from cura.API.ConnectionStatus import ConnectionStatus
from cura.API.Interface import Interface
from cura.API.Account import Account
@ -44,6 +45,9 @@ class CuraAPI(QObject):
self._backups = Backups(self._application)
self._connectionStatus = ConnectionStatus()
# Interface API
self._interface = Interface(self._application)
def initialize(self) -> None:
@ -55,6 +59,10 @@ class CuraAPI(QObject):
return self._account
@pyqtProperty(QObject, constant = True)
def connectionStatus(self) -> "ConnectionStatus":
return self._connectionStatus
@property
def backups(self) -> "Backups":
"""Backups API"""

View file

@ -40,10 +40,10 @@ class CuraPackageManager(PackageManager):
machine_with_qualities = []
for container_id in ids:
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")):
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():
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

View file

@ -156,9 +156,10 @@ class BaseMaterialsModel(ListModel):
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack.hasMaterials:
return # There are no materials for this machine, so nothing to do.
extruder_stack = global_stack.extruders.get(str(self._extruder_position))
if not extruder_stack:
extruder_list = global_stack.extruderList
if self._extruder_position > len(extruder_list):
return
extruder_stack = extruder_list[self._extruder_position]
nozzle_name = extruder_stack.variant.getName()
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
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._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin]
self._network_plugin_queue = [] # type: List[OutputDevicePlugin]
self._manual_device_address = ""
self._manual_device_request_timeout_in_seconds = 5 # timeout for adding a manual device in seconds
@ -152,20 +153,25 @@ class DiscoveredPrintersModel(QObject):
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,
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)
return
plugin = max(can_add_manual_plugins, key = lambda p: priority_order.index(p.canAddManualDevice(address)))
self._plugin_for_manual_device = plugin
self._plugin_for_manual_device.addManualDevice(address, callback = self._onManualDeviceRequestFinished)
self._manual_device_address = address
self._manual_device_request_timer.start()
self.hasManualDeviceRequestInProgressChanged.emit()
self._attemptToAddManualDevice(address)
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_request_timer.start()
self.hasManualDeviceRequestInProgressChanged.emit()
@pyqtSlot()
def cancelCurrentManualDeviceRequest(self) -> None:
@ -180,8 +186,11 @@ class DiscoveredPrintersModel(QObject):
self.manualDeviceRequestFinished.emit(False)
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()
if self._network_plugin_queue:
self._attemptToAddManualDevice(address)
hasManualDeviceRequestInProgressChanged = pyqtSignal()
@ -197,6 +206,8 @@ class DiscoveredPrintersModel(QObject):
self._manual_device_address = ""
self.hasManualDeviceRequestInProgressChanged.emit()
self.manualDeviceRequestFinished.emit(success)
if not success and self._network_plugin_queue:
self._attemptToAddManualDevice(address)
@pyqtProperty("QVariantMap", notify = discoveredPrintersChanged)
def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]:

View file

@ -19,6 +19,7 @@ class GlobalStacksModel(ListModel):
ConnectionTypeRole = Qt.UserRole + 4
MetaDataRole = Qt.UserRole + 5
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:
super().__init__(parent)
@ -66,13 +67,21 @@ class GlobalStacksModel(ListModel):
if parseBool(container_stack.getMetaDataEntry("hidden", False)):
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)
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(),
"hasRemoteConnection": has_remote_connection,
"metadata": container_stack.getMetaData().copy(),
"discoverySource": section_name})
items.sort(key = lambda i: (not i["hasRemoteConnection"], i["name"]))
"discoverySource": section_name,
"removalWarning": removal_warning})
items.sort(key=lambda i: (not i["hasRemoteConnection"], i["name"]))
self.setItems(items)

View file

@ -151,7 +151,7 @@ class QualitySettingsModel(ListModel):
if self._selected_position == self.GLOBAL_STACK_POSITION:
user_value = global_container_stack.userChanges.getProperty(definition.key, "value")
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")
if profile_value is None and user_value is None:

View file

@ -241,7 +241,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._node is None:
return None
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():
child_hull = child.callDecoration("_compute2DConvexHull")
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.
# 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
# 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.

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)}
container_tree = ContainerTree.getInstance()
machine_extruders = []
for position in sorted(global_stack.extruders):
machine_extruders.append(global_stack.extruders[position])
machine_extruders = global_stack.extruderList
plugin_registry = PluginRegistry.getInstance()
extension = file_name.split(".")[-1]
@ -275,7 +273,7 @@ class CuraContainerRegistry(ContainerRegistry):
if len(profile_or_list) == 1:
global_profile = profile_or_list[0]
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 = InstanceContainer(profile_id)
profile.setName(quality_name)

View file

@ -65,7 +65,7 @@ class CuraStackBuilder:
except IndexError:
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)
# 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 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]:
"""Gets a property of a setting for all extruders.

View file

@ -45,9 +45,6 @@ class ExtruderStack(CuraContainerStack):
stack.addExtruder(self)
self.setMetaDataEntry("machine", stack.id)
# For backward compatibility: Register the extruder with the Extruder Manager
ExtruderManager.getInstance().registerExtruder(self, stack.id)
@override(ContainerStack)
def getNextStack(self) -> Optional["GlobalStack"]:
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.
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)
def activeMachineIsUsingCloudConnection(self) -> bool:
return self.activeMachineHasCloudConnection and not self.activeMachineHasNetworkConnection
@ -943,7 +947,7 @@ class MachineManager(QObject):
@pyqtSlot(int, bool)
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)
return
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.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
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_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:
containers = container_registry.findContainers(id = quality_changes_metadata["id"])
if containers:
@ -1132,7 +1136,7 @@ class MachineManager(QObject):
def _setVariantNode(self, position: str, variant_node: "VariantNode") -> None:
if self._global_container_stack is None:
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()
def _setGlobalVariant(self, container_node: "ContainerNode") -> None:
@ -1147,7 +1151,7 @@ class MachineManager(QObject):
return
if material_node and 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)
else:
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:
return
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:
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
# of the new machine after the new_machine becomes active.
global_user_changes = self._global_container_stack.userChanges
per_extruder_user_changes = {}
for extruder_name, extruder_stack in self._global_container_stack.extruders.items():
per_extruder_user_changes[extruder_name] = extruder_stack.userChanges
per_extruder_user_changes = [extruder_stack.userChanges for extruder_stack in self._global_container_stack.extruderList]
self.setActiveMachine(new_machine.getId())
# Apply the global and per-extruder userChanges to the new_machine (which is of different type than the
# previous one).
self._global_container_stack.setUserChanges(global_user_changes)
for extruder_name in self._global_container_stack.extruders.keys():
self._global_container_stack.extruders[extruder_name].setUserChanges(per_extruder_user_changes[extruder_name])
for i, user_changes in enumerate(per_extruder_user_changes):
self._global_container_stack.extruderList[i].setUserChanges(per_extruder_user_changes[i])
@pyqtSlot(QObject)
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)
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.updateDefaultExtruder()
@ -1446,7 +1448,7 @@ class MachineManager(QObject):
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:
global_stack.extruders[position].material = container_node.container
global_stack.extruderList[int(position)].material = container_node.container
return
position = str(position)
self.blurSettings.emit()
@ -1639,7 +1641,7 @@ class MachineManager(QObject):
return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
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()
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)

View file

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

View file

@ -598,7 +598,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
"""
application = CuraApplication.getInstance()
archive = zipfile.ZipFile(file_name, "r")
try:
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/")]
@ -632,8 +640,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
machine_name = self._container_registry.uniqueName(self._machine_info.name)
global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id)
if global_stack: #Only switch if creating the machine was successful.
extruder_stack_dict = global_stack.extruders
if global_stack: # Only switch if creating the machine was successful.
extruder_stack_dict = {str(position): extruder for position, extruder in enumerate(global_stack.extruderList)}
self._container_registry.addContainer(global_stack)
else:

View file

@ -255,7 +255,7 @@ class StartSliceJob(Job):
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
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 = []
has_model_with_disabled_extruders = False
associated_disabled_extruders = set()
@ -265,7 +265,7 @@ class StartSliceJob(Job):
for node in group:
# Only check if the printing extruder is enabled for printing meshes
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]:
skip_group = True
has_model_with_disabled_extruders = True
@ -275,8 +275,8 @@ class StartSliceJob(Job):
if has_model_with_disabled_extruders:
self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
associated_disabled_extruders = {str(c) for c in sorted([int(p) + 1 for p in associated_disabled_extruders])}
self.setMessage(", ".join(associated_disabled_extruders))
associated_disabled_extruders = {p + 1 for p in associated_disabled_extruders}
self.setMessage(", ".join(map(str, sorted(associated_disabled_extruders))))
return
# 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
{
id: toggleShowAll
anchors
{
top: parent.top
right: parent.right
}
text: catalog.i18nc("@label:checkbox", "Show all")
checked: listview.model.showAll
onClicked: listview.model.showAll = checked
}
ScrollView
@ -85,7 +81,7 @@ UM.Dialog
}
ListView
{
id:listview
id: listview
model: UM.SettingDefinitionsModel
{
id: definitionsModel
@ -98,6 +94,7 @@ UM.Dialog
excluded_settings = excluded_settings.concat(settingPickDialog.additional_excluded_settings)
return excluded_settings
}
showAll: toggleShowAll.checked || filterInput.text !== ""
}
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",
"description": "Whether to pause at a certain height or at a certain layer.",
"type": "enum",
"options": {"height": "Height", "layer_no": "Layer No."},
"options": {"height": "Height", "layer_no": "Layer Number"},
"default_value": "height"
},
"pause_height":
@ -49,6 +49,15 @@ class PauseAtHeight(Script):
"minimum_value_warning": "1",
"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":
{
"label": "Disarm timeout",
@ -66,7 +75,8 @@ class PauseAtHeight(Script):
"description": "What X location does the head move to when pausing.",
"unit": "mm",
"type": "float",
"default_value": 190
"default_value": 190,
"enabled": "pause_method != \\\"griffin\\\""
},
"head_park_y":
{
@ -74,7 +84,17 @@ class PauseAtHeight(Script):
"description": "What Y location does the head move to when pausing.",
"unit": "mm",
"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":
{
@ -82,7 +102,8 @@ class PauseAtHeight(Script):
"description": "How much filament must be retracted at pause.",
"unit": "mm",
"type": "float",
"default_value": 0
"default_value": 0,
"enabled": "pause_method != \\\"griffin\\\""
},
"retraction_speed":
{
@ -90,7 +111,8 @@ class PauseAtHeight(Script):
"description": "How fast to retract the filament.",
"unit": "mm/s",
"type": "float",
"default_value": 25
"default_value": 25,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
},
"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.",
"unit": "mm",
"type": "float",
"default_value": 0
"default_value": 0,
"enabled": "pause_method != \\\"griffin\\\""
},
"extrude_speed":
{
@ -106,7 +129,8 @@ class PauseAtHeight(Script):
"description": "How fast to extrude the material after pause.",
"unit": "mm/s",
"type": "float",
"default_value": 3.3333
"default_value": 3.3333,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
},
"redo_layer":
{
@ -121,18 +145,61 @@ class PauseAtHeight(Script):
"description": "Change the temperature during the pause.",
"unit": "°C",
"type": "int",
"default_value": 0
"default_value": 0,
"enabled": "pause_method not in [\\\"griffin\\\", \\\"repetier\\\"]"
},
"display_text":
{
"label": "Display Text",
"description": "Text that should appear on the display while paused. If left empty, there will not be any message.",
"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]:
"""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")
@ -159,6 +226,7 @@ class PauseAtHeight(Script):
extrude_speed = self.getSettingValueByKey("extrude_speed")
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_layer = self.getSettingValueByKey("redo_layer")
standby_temperature = self.getSettingValueByKey("standby_temperature")
@ -167,7 +235,14 @@ class PauseAtHeight(Script):
initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")
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")
@ -188,8 +263,6 @@ class PauseAtHeight(Script):
# Scroll each line of instruction for each layer in the G-code
for line in lines:
if ";FLAVOR:Griffin" in line:
is_griffin = True
# Fist positive layer reached
if ";LAYER:0" in line:
layers_started = True
@ -291,7 +364,22 @@ class PauseAtHeight(Script):
else:
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
prepend_gcode += self.putValue(M = 83) + " ; switch to relative E values for any needed retraction\n"
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"
# 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:
# Set extruder resume temperature
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,
callback = self._onUserPackagesRequestFinished,
error_callback = self._onUserPackagesRequestFinished,
timeout=10,
scope = self._scope)
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 PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from cura.API import Account
from cura.CuraApplication import CuraApplication
from cura.UltimakerCloud import UltimakerCloudAuthentication
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
from .ToolPathUploader import ToolPathUploader
from ..Models.BaseModel import BaseModel
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
@ -35,18 +39,23 @@ class CloudApiClient:
CLUSTER_API_ROOT = "{}/connect/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.
_anti_gc_callbacks = [] # type: List[Callable[[], None]]
DEFAULT_REQUEST_TIMEOUT = 10 # seconds
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.
:param app:
:param account: The user's account object
:param on_error: The callback to be called whenever we receive errors from the server.
"""
super().__init__()
self._manager = QNetworkAccessManager()
self._account = account
self._app = app
self._account = app.getCuraAPI().account
self._scope = JsonDecoratorScope(UltimakerCloudScope(app))
self._http = HttpRequestManager.getInstance()
self._on_error = on_error
self._upload = None # type: Optional[ToolPathUploader]
@ -63,8 +72,11 @@ class CloudApiClient:
"""
url = "{}/clusters?status=active".format(self.CLUSTER_API_ROOT)
reply = self._manager.get(self._createEmptyRequest(url))
self._addCallback(reply, on_finished, CloudClusterResponse, failed)
self._http.get(url,
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:
"""Retrieves the status of the given cluster.
@ -74,8 +86,10 @@ class CloudApiClient:
"""
url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id)
reply = self._manager.get(self._createEmptyRequest(url))
self._addCallback(reply, on_finished, CloudClusterStatus)
self._http.get(url,
scope = self._scope,
callback = self._parseCallback(on_finished, CloudClusterStatus),
timeout = self.DEFAULT_REQUEST_TIMEOUT)
def requestUpload(self, request: CloudPrintJobUploadRequest,
on_finished: Callable[[CloudPrintJobResponse], Any]) -> None:
@ -87,9 +101,13 @@ class CloudApiClient:
"""
url = "{}/jobs/upload".format(self.CURA_API_ROOT)
body = json.dumps({"data": request.toDict()})
reply = self._manager.put(self._createEmptyRequest(url), body.encode())
self._addCallback(reply, on_finished, CloudPrintJobResponse)
data = json.dumps({"data": request.toDict()}).encode()
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],
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.
"""
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()
# 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.
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)
reply = self._manager.post(self._createEmptyRequest(url), b"")
self._addCallback(reply, on_finished, CloudPrintResponse)
self._http.post(url,
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,
data: Optional[Dict[str, Any]] = None) -> None:
@ -126,7 +147,10 @@ class CloudApiClient:
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)
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:
"""We override _createEmptyRequest in order to add the user credentials.
@ -185,12 +209,12 @@ class CloudApiClient:
else:
Logger.log("e", "Cannot find data or errors in the cloud response: %s", response)
def _addCallback(self,
reply: QNetworkReply,
on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]],
model: Type[CloudApiClientModel],
on_error: Optional[Callable] = None) -> None:
def _parseCallback(self,
on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]],
model: Type[CloudApiClientModel],
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.
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.
"""
def parse() -> None:
def parse(reply: QNetworkReply) -> None:
self._anti_gc_callbacks.remove(parse)
# 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._anti_gc_callbacks.append(parse)
reply.finished.connect(parse)
if on_error is not None:
reply.error.connect(on_error)
return parse

View file

@ -4,6 +4,7 @@ import os
from typing import Dict, List, Optional
from PyQt5.QtCore import QTimer
from PyQt5.QtNetwork import QNetworkReply
from UM import i18nCatalog
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.
self._remote_clusters = {} # type: Dict[str, CloudOutputDevice]
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)
# Ensure we don't start twice.
@ -118,7 +119,7 @@ class CloudOutputDeviceManager:
self._syncing = False
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._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
@ -265,6 +266,13 @@ class CloudOutputDeviceManager:
machine.setName(device.name)
machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
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)
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:

View file

@ -1,11 +1,11 @@
# Copyright (c) 2019 Ultimaker B.V.
# !/usr/bin/env python
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
from typing import Optional, Callable, Any, Tuple, cast
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from typing import Callable, Any, Tuple, cast, Dict, Optional
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse
@ -23,7 +23,7 @@ class ToolPathUploader:
# The amount of bytes to send per request
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]
) -> None:
"""Creates a mesh upload object.
@ -36,7 +36,7 @@ class ToolPathUploader:
:param on_error: The method to be called when an error occurs.
"""
self._manager = manager
self._http = http
self._print_job = print_job
self._data = data
@ -47,7 +47,6 @@ class ToolPathUploader:
self._sent_bytes = 0
self._retries = 0
self._finished = False
self._reply = None # type: Optional[QNetworkReply]
@property
def printJob(self):
@ -55,19 +54,6 @@ class ToolPathUploader:
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]:
"""Determines the bytes that should be uploaded next.
@ -99,13 +85,23 @@ class ToolPathUploader:
raise ValueError("The upload is already finished")
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
self._reply = self._manager.put(request, self._data[first_byte:last_byte])
self._reply.finished.connect(self._finishedCallback)
self._reply.uploadProgress.connect(self._progressCallback)
self._reply.error.connect(self._errorCallback)
headers = {
"Content-Type": cast(str, self._print_job.content_type),
"Content-Range": content_range
} # type: Dict[str, str]
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:
"""Handles an update to the upload progress
@ -118,7 +114,8 @@ class ToolPathUploader:
total_sent = self._sent_bytes + bytes_sent
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."""
reply = cast(QNetworkReply, self._reply)
@ -127,7 +124,7 @@ class ToolPathUploader:
self.stop()
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."""
reply = cast(QNetworkReply, self._reply)
@ -148,7 +145,7 @@ class ToolPathUploader:
# Http codes that are not to be retried are assumed to be errors.
if status_code > 308:
self._errorCallback()
self._errorCallback(reply, None)
return
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:
"""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.
return
if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:

View file

@ -2,11 +2,44 @@
# Cura is released under the terms of the LGPLv3 or higher.
import configparser
import copy # To split up files.
from typing import Tuple, List
import io
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):
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]]:
"""
Upgrades preferences to have the new version number.
@ -25,6 +58,66 @@ class VersionUpgrade460to462(VersionUpgrade):
parser.write(result)
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]]:
"""
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]]:
"""
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 filename: The original file name of the stack.
: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.read_string(serialized)
results = [(parser, filename)]
# Update version number.
if "metadata" not in parser:
parser["metadata"] = {}
parser["metadata"]["setting_version"] = "14"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
if "containers" in parser and "7" in parser["containers"]:
if parser["containers"]["7"] == "deltacomb_extruder_0" or parser["containers"]["7"] == "deltacomb_extruder_1": # Extruder stack.
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),
("machine_stack", 4000013): ("machine_stack", 4000014, upgrade.upgradeStack),
("extruder_train", 4000013): ("extruder_train", 4000014, upgrade.upgradeStack),
("definition_changes", 4000013): ("definition_changes", 4000014, upgrade.upgradeInstanceContainer),
("quality_changes", 4000013): ("quality_changes", 4000014, upgrade.upgradeInstanceContainer),
("quality", 4000013): ("quality", 4000014, upgrade.upgradeInstanceContainer),
("user", 4000013): ("user", 4000014, upgrade.upgradeInstanceContainer),
("definition_changes", 4000013): ("definition_changes", 4000014, upgrade.upgradeExtruderInstanceContainer),
("quality_changes", 4000013): ("quality_changes", 4000014, upgrade.upgradeExtruderInstanceContainer),
("quality", 4000013): ("quality", 4000014, upgrade.upgradeExtruderInstanceContainer),
("user", 4000013): ("user", 4000014, upgrade.upgradeExtruderInstanceContainer),
},
"sources": {
"preferences": {

View file

@ -58,6 +58,19 @@ class VersionUpgrade462to47(VersionUpgrade):
maximum_deviation = "=(" + maximum_deviation + ") / 2"
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()
parser.write(result)
return [filename], [result.getvalue()]
@ -88,6 +101,25 @@ class VersionUpgrade462to47(VersionUpgrade):
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.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 "redo_layers" in script_parser["PauseAtHeight"]:
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",
"unit": "mm",
"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",
"maximum_value_warning": "wall_line_width_0",
"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).",
"type": "[int]",
"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",
"settable_per_mesh": true
},

View file

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

View file

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

View file

@ -70,7 +70,7 @@
"material_bed_temp_wait": {"default_value": false },
"machine_max_feedrate_z": {"default_value": 10 },
"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": "" },
"retraction_extra_prime_amount": {"minimum_value_warning": "-2.0" },
@ -104,7 +104,7 @@
"skin_outline_count": {"value": "0"},
"ironing_line_spacing": {"value": "line_width / 4 * 3"},
"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"},
"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"
},
"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": {
"value": "10"

View file

@ -11,7 +11,8 @@
"platform": "prusai3_platform.3mf",
"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,
"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 },
"material_diameter": { "default_value": 1.75 },
"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": {
"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 },
"material_diameter": { "default_value": 1.75 },
"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": {
"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
@ -119,6 +127,7 @@ Item
x: parent.width - width
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
onOpened: Cura.API.account.popupOpened()
opacity: opened ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }

View file

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

View file

@ -9,7 +9,10 @@ import Cura 1.1 as Cura
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
@ -29,13 +32,10 @@ Column
color: UM.Theme.getColor("text")
}
SyncState
{
SyncState {
id: syncRow
}
Label
{
id: lastSyncLabel

View file

@ -35,7 +35,8 @@ Item
property color headerActiveColor: UM.Theme.getColor("secondary")
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
property alias disabledText: disabledLabel.text
@ -139,6 +140,16 @@ Item
anchors.fill: parent
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
{
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

View file

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

View file

@ -154,7 +154,7 @@ UM.PreferencesPage
Component.onCompleted: {
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: "Español", code: "es_ES" })
//Finnish is disabled for being incomplete: append({ text: "Suomi", code: "fi_FI" })

View file

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

View file

@ -5,16 +5,49 @@ import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
import Cura 1.1 as Cura
Cura.ExpandablePopup
{
id: machineSelector
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
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
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
@ -44,7 +77,7 @@ Cura.ExpandablePopup
{
return UM.Theme.getIcon("printer_group")
}
else if (isNetworkPrinter || isCloudPrinter)
else if (isNetworkPrinter || isCloudRegistered)
{
return UM.Theme.getIcon("printer_single")
}
@ -59,6 +92,7 @@ Cura.ExpandablePopup
UM.RecolorImage
{
id: connectionStatusImage
anchors
{
bottom: parent.bottom
@ -66,27 +100,14 @@ Cura.ExpandablePopup
leftMargin: UM.Theme.getSize("thick_margin").width
}
source:
{
if (isNetworkPrinter)
{
return UM.Theme.getIcon("printer_connected")
}
else if (isCloudPrinter)
{
return UM.Theme.getIcon("printer_cloud_connected")
}
else
{
return ""
}
}
source: UM.Theme.getIcon(connectionStatus)
width: UM.Theme.getSize("printer_status_icon").width
height: UM.Theme.getSize("printer_status_icon").height
color: UM.Theme.getColor("primary")
visible: isNetworkPrinter || isCloudPrinter
color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary")
visible: isNetworkPrinter || isCloudRegistered
// Make a themable circle in the background so we can change it in other themes
Rectangle
@ -100,6 +121,38 @@ Cura.ExpandablePopup
color: UM.Theme.getColor("main_background")
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 alias tooltipText: tooltip.text
property alias arrowSize: backgroundRect.arrowSize
property var targetPoint: Qt.point(parent.x, y + Math.round(height/2))
id: tooltip
text: ""
delay: 500
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
height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0
@ -60,4 +68,12 @@ ToolTip
color: UM.Theme.getColor("tooltip_text")
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