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
@ -14,7 +15,7 @@ if TYPE_CHECKING:
class CuraAPI(QObject):
"""The official Cura API that plug-ins can use to interact with Cura.
Python does not technically prevent talking to other classes as well, but this API provides a version-safe
interface with proper deprecation warnings etc. Usage of any other methods than the ones provided in this API can
cause plug-ins to be unstable.
@ -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

@ -47,9 +47,9 @@ class CuraContainerRegistry(ContainerRegistry):
@override(ContainerRegistry)
def addContainer(self, container: ContainerInterface) -> None:
"""Overridden from ContainerRegistry
Adds a container to the registry.
This will also try to convert a ContainerStack to either Extruder or
Global stack based on metadata information.
"""
@ -70,7 +70,7 @@ class CuraContainerRegistry(ContainerRegistry):
def createUniqueName(self, container_type: str, current_name: str, new_name: str, fallback_name: str) -> str:
"""Create a name that is not empty and unique
:param container_type: :type{string} Type of the container (machine, quality, ...)
:param current_name: :type{} Current name of the container, which may be an acceptable option
:param new_name: :type{string} Base name, which may not be unique
@ -95,7 +95,7 @@ class CuraContainerRegistry(ContainerRegistry):
def _containerExists(self, container_type: str, container_name: str):
"""Check if a container with of a certain type and a certain name or id exists
Both the id and the name are checked, because they may not be the same and it is better if they are both unique
:param container_type: :type{string} Type of the container (machine, quality, ...)
:param container_name: :type{string} Name to check
@ -107,7 +107,7 @@ class CuraContainerRegistry(ContainerRegistry):
def exportQualityProfile(self, container_list: List[InstanceContainer], file_name: str, file_type: str) -> bool:
"""Exports an profile to a file
:param container_list: :type{list} the containers to export. This is not
necessarily in any order!
:param file_name: :type{str} the full path and filename to export to.
@ -160,7 +160,7 @@ class CuraContainerRegistry(ContainerRegistry):
def _findProfileWriter(self, extension: str, description: str) -> Optional[ProfileWriter]:
"""Gets the plugin object matching the criteria
:param extension:
:param description:
:return: The plugin object matching the given extension and description.
@ -177,7 +177,7 @@ class CuraContainerRegistry(ContainerRegistry):
def importProfile(self, file_name: str) -> Dict[str, str]:
"""Imports a profile from a file
:param file_name: The full path and filename of the profile to import.
:return: Dict with a 'status' key containing the string 'ok' or 'error',
and a 'message' key containing a message for the user.
@ -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)
@ -353,7 +351,7 @@ class CuraContainerRegistry(ContainerRegistry):
@override(ContainerRegistry)
def _isMetadataValid(self, metadata: Optional[Dict[str, Any]]) -> bool:
"""Check if the metadata for a container is okay before adding it.
This overrides the one from UM.Settings.ContainerRegistry because we
also require that the setting_version is correct.
"""
@ -371,11 +369,11 @@ class CuraContainerRegistry(ContainerRegistry):
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]:
"""Update an imported profile to match the current machine configuration.
:param profile: The profile to configure.
:param id_seed: The base ID for the profile. May be changed so it does not conflict with existing containers.
:param new_name: The new name for the profile.
:return: None if configuring was successful or an error message if an error occurred.
"""
@ -438,7 +436,7 @@ class CuraContainerRegistry(ContainerRegistry):
def _getIOPlugins(self, io_type):
"""Gets a list of profile writer plugins
:return: List of tuples of (plugin_id, meta_data).
"""
plugin_registry = PluginRegistry.getInstance()

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

@ -21,7 +21,7 @@ if TYPE_CHECKING:
class ExtruderManager(QObject):
"""Manages all existing extruder stacks.
This keeps a list of extruder stacks for each machine.
"""
@ -54,10 +54,10 @@ class ExtruderManager(QObject):
@pyqtProperty(str, notify = activeExtruderChanged)
def activeExtruderStackId(self) -> Optional[str]:
"""Gets the unique identifier of the currently active extruder stack.
The currently active extruder stack is the stack that is currently being
edited.
:return: The unique ID of the currently active extruder stack.
"""
@ -83,7 +83,7 @@ class ExtruderManager(QObject):
@pyqtSlot(int)
def setActiveExtruderIndex(self, index: int) -> None:
"""Changes the active extruder by index.
:param index: The index of the new active extruder.
"""
@ -132,7 +132,7 @@ class ExtruderManager(QObject):
def resetSelectedObjectExtruders(self) -> None:
"""Reset the internal list used for the selectedObjectExtruders property
This will trigger a recalculation of the extruders used for the
selection.
"""
@ -154,28 +154,9 @@ 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.
:param setting_key: :type{str} The setting to get the property of.
:param prop: :type{str} The property to get.
:return: :type{List} the list of results
@ -197,14 +178,14 @@ class ExtruderManager(QObject):
def getUsedExtruderStacks(self) -> List["ExtruderStack"]:
"""Gets the extruder stacks that are actually being used at the moment.
An extruder stack is being used if it is the extruder to print any mesh
with, or if it is the support infill extruder, the support interface
extruder, or the bed adhesion extruder.
If there are no extruders, this returns the global stack as a singleton
list.
:return: A list of extruder stacks.
"""
@ -294,7 +275,7 @@ class ExtruderManager(QObject):
def getInitialExtruderNr(self) -> int:
"""Get the extruder that the print will start with.
This should mirror the implementation in CuraEngine of
``FffGcodeWriter::getStartExtruder()``.
"""
@ -315,7 +296,7 @@ class ExtruderManager(QObject):
def removeMachineExtruders(self, machine_id: str) -> None:
"""Removes the container stack and user profile for the extruders for a specific machine.
:param machine_id: The machine to remove the extruders for.
"""
@ -327,7 +308,7 @@ class ExtruderManager(QObject):
def getMachineExtruders(self, machine_id: str) -> List["ExtruderStack"]:
"""Returns extruders for a specific machine.
:param machine_id: The machine to get the extruders of.
"""
@ -337,7 +318,7 @@ class ExtruderManager(QObject):
def getActiveExtruderStacks(self) -> List["ExtruderStack"]:
"""Returns the list of active extruder stacks, taking into account the machine extruder count.
:return: :type{List[ContainerStack]} a list of
"""
@ -423,11 +404,11 @@ class ExtruderManager(QObject):
@pyqtSlot(str, result="QVariant")
def getInstanceExtruderValues(self, key: str) -> List:
"""Get all extruder values for a certain setting.
This is exposed to qml for display purposes
:param key: The key of the setting to retrieve values for.
:return: String representing the extruder values
"""
@ -436,11 +417,11 @@ class ExtruderManager(QObject):
@staticmethod
def getResolveOrValue(key: str) -> Any:
"""Get the resolve value or value for a given key
This is the effective value for a given key, it is used for values in the global stack.
This is exposed to SettingFunction to use in value functions.
:param key: The key of the setting to get the value of.
:return: The effective value
"""

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

@ -499,6 +499,10 @@ class MachineManager(QObject):
def activeMachineHasCloudConnection(self) -> bool:
# 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:
@ -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")
@ -145,7 +212,7 @@ class PauseAtHeight(Script):
def execute(self, data: List[str]) -> List[str]:
"""Inserts the pause commands.
:param data: List of layers.
:return: New list of layers.
"""
@ -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
@ -26,7 +30,7 @@ CloudApiClientModel = TypeVar("CloudApiClientModel", bound=BaseModel)
class CloudApiClient:
"""The cloud API client is responsible for handling the requests and responses from the cloud.
Each method should only handle models instead of exposing Any HTTP details.
"""
@ -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]
@ -58,43 +67,52 @@ class CloudApiClient:
def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any], failed: Callable) -> None:
"""Retrieves all the clusters for the user that is currently logged in.
:param on_finished: The function to be called after the result is parsed.
"""
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.
:param cluster_id: The ID of the cluster.
:param on_finished: The function to be called after the result is parsed.
"""
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:
"""Requests the cloud to register the upload of a print job mesh.
:param request: The request object.
:param on_finished: The function to be called after the result is parsed.
"""
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]):
"""Uploads a print job tool path to the cloud.
:param print_job: The object received after requesting an upload with `self.requestUpload`.
:param mesh: The tool path data to be uploaded.
:param on_finished: The function to be called after the upload is successful.
@ -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,14 +129,17 @@ 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:
"""Send a print job action to the cluster for the given print job.
:param cluster_id: The ID of the cluster.
:param cluster_job_id: The ID of the print job within the cluster.
:param action: The name of the action to execute.
@ -126,11 +147,14 @@ 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.
:param url: The URL to request
:param content_type: The type of the body contents.
"""
@ -146,7 +170,7 @@ class CloudApiClient:
@staticmethod
def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, Any]]:
"""Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well.
:param reply: The reply from the server.
:return: A tuple with a status code and a dictionary.
"""
@ -164,7 +188,7 @@ class CloudApiClient:
def _parseModels(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None:
"""Parses the given models and calls the correct callback depending on the result.
:param response: The response from the server, after being converted to a dict.
:param on_finished: The callback in case the response is successful.
:param model_class: The type of the model to convert the response to. It may either be a single record or a list.
@ -185,14 +209,14 @@ 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.
:param reply: The reply that should be listened to.
:param on_finished: The callback in case the response is successful. Depending on the endpoint it will be either
@ -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:
@ -277,4 +285,4 @@ class CloudOutputDeviceManager:
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
if device.key not in output_device_manager.getOutputDeviceIds():
output_device_manager.addOutputDevice(device)
output_device_manager.addOutputDevice(device)

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,11 +23,11 @@ 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.
:param manager: The network access manager that will handle the HTTP requests.
:param print_job: The print job response that was returned by the cloud after registering the upload.
:param data: The mesh bytes to be uploaded.
@ -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,22 +54,9 @@ 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.
:return: A tuple with the first and the last byte to upload.
"""
last_byte = min(len(self._data), self._sent_bytes + self.BYTES_PER_REQUEST)
@ -99,17 +85,27 @@ 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
:param bytes_sent: The amount of bytes sent in the current request.
:param bytes_total: The amount of bytes to send in the current request.
"""
@ -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

@ -151,7 +151,7 @@ class LocalClusterOutputDeviceManager:
@staticmethod
def _getPrinterTypeIdentifiers() -> Dict[str, str]:
"""Returns a dict of printer BOM numbers to machine types.
These numbers are available in the machine definition already so we just search for them here.
"""
@ -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