Merge branch 'master' into marketplace_redesign

This commit is contained in:
Ghostkeeper 2021-12-31 14:07:51 +01:00
commit a878cfae7a
No known key found for this signature in database
GPG key ID: D2A8871EE34EC59A
1092 changed files with 109052 additions and 89476 deletions

View file

@ -49,7 +49,9 @@ _ignored_machine_network_metadata = {
"removal_warning",
"group_name",
"group_size",
"connection_type"
"connection_type",
"capabilities",
"octoprint_api_key",
} # type: Set[str]
@ -377,7 +379,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# - the global stack DOESN'T exist but some/all of the extruder stacks exist
# To simplify this, only check if the global stack exists or not
global_stack_id = self._stripFileToId(global_stack_file)
serialized = archive.open(global_stack_file).read().decode("utf-8")
serialized = GlobalStack._updateSerialized(serialized, global_stack_file)
machine_name = self._getMachineNameFromSerializedStack(serialized)
self._machine_info.metadata_dict = self._getMetaDataDictFromSerializedStack(serialized)

View file

@ -32,6 +32,12 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
Logger.error("3MF Writer class is unavailable. Can't write workspace.")
return False
global_stack = machine_manager.activeMachine
if global_stack is None:
self.setInformation(catalog.i18nc("@error", "There is no workspace yet to write. Please add a printer first."))
Logger.error("Tried to write a 3MF workspace before there was a global stack.")
return False
# Indicate that the 3mf mesh writer should not close the archive just yet (we still need to add stuff to it).
mesh_writer.setStoreArchive(True)
mesh_writer.write(stream, nodes, mode)
@ -40,7 +46,6 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
if archive is None: # This happens if there was no mesh data to write.
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
global_stack = machine_manager.activeMachine
try:
# Add global container stack data to the archive.
@ -149,7 +154,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
"group_name",
"group_size",
"connection_type",
"octoprint_api_key"
"capabilities",
"octoprint_api_key",
}
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)

View file

@ -10,6 +10,10 @@ from UM.Application import Application
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.Utils.Threading import call_on_qt_thread
from cura.Snapshot import Snapshot
from PyQt5.QtCore import QBuffer
import Savitar
@ -149,6 +153,22 @@ class ThreeMFWriter(MeshWriter):
relations_element = ET.Element("Relationships", xmlns = self._namespaces["relationships"])
model_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/3D/3dmodel.model", Id = "rel0", Type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")
# Attempt to add a thumbnail
snapshot = self._createSnapshot()
if snapshot:
thumbnail_buffer = QBuffer()
thumbnail_buffer.open(QBuffer.ReadWrite)
snapshot.save(thumbnail_buffer, "PNG")
thumbnail_file = zipfile.ZipInfo("Metadata/thumbnail.png")
# Don't try to compress snapshot file, because the PNG is pretty much as compact as it will get
archive.writestr(thumbnail_file, thumbnail_buffer.data())
# Add PNG to content types file
thumbnail_type = ET.SubElement(content_types, "Default", Extension = "png", ContentType = "image/png")
# Add thumbnail relation to _rels/.rels file
thumbnail_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/Metadata/thumbnail.png", Id = "rel1", Type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
savitar_scene = Savitar.Scene()
metadata_to_store = CuraApplication.getInstance().getController().getScene().getMetaData()
@ -212,3 +232,17 @@ class ThreeMFWriter(MeshWriter):
self._archive = archive
return True
@call_on_qt_thread # must be called from the main thread because of OpenGL
def _createSnapshot(self):
Logger.log("d", "Creating thumbnail image...")
if not CuraApplication.getInstance().isVisible:
Logger.log("w", "Can't create snapshot when renderer not initialized.")
return None
try:
snapshot = Snapshot.snapshot(width = 300, height = 300)
except:
Logger.logException("w", "Failed to create snapshot image")
return None
return snapshot

View file

@ -123,6 +123,9 @@ class StartSliceJob(Job):
Job.yieldThread()
for changed_setting_key in changed_setting_keys:
if not stack.getProperty(changed_setting_key, "enabled"):
continue
validation_state = stack.getProperty(changed_setting_key, "validationState")
if validation_state is None:

View file

@ -261,7 +261,10 @@ class DigitalFactoryController(QObject):
"""
Error function, called whenever the retrieval of the files in a library project fails.
"""
Logger.log("w", "Failed to retrieve the list of files in project '{}' from the Digital Library".format(self._project_model._projects[self._selected_project_idx]))
try:
Logger.warning(f"Failed to retrieve the list of files in project '{self._project_model._projects[self._selected_project_idx]}' from the Digital Library")
except IndexError:
Logger.warning(f"Failed to retrieve the list of files in a project from the Digital Library. And failed to get the project too.")
self.setRetrievingFilesStatus(RetrievalStatus.Failed)
@pyqtSlot()

View file

@ -198,7 +198,7 @@ class FlavorParser:
# Only when extruding we can determine the latest known "layer height" which is the difference in height between extrusions
# Also, 1.5 is a heuristic for any priming or whatsoever, we skip those.
if z > self._previous_z and (z - self._previous_z < 1.5):
if z > self._previous_z and (z - self._previous_z < 1.5) and (params.x is not None or params.y is not None):
self._current_layer_thickness = z - self._previous_z # allow a tiny overlap
self._previous_z = z
elif self._previous_extrusion_value > e[self._extruder_number]:

View file

@ -11,6 +11,8 @@
# Modified by Jaime van Kessel (Ultimaker), j.vankessel@ultimaker.com to make it work for 15.10 / 2.x
# Modified by Ghostkeeper (Ultimaker), rubend@tutanota.com, to debug.
# Modified by Wes Hanney, https://github.com/novamxd, Retract Length + Speed, Clean up
# Modified by Alex Jaxon, https://github.com/legend069, Added option to modify Build Volume Temperature
# history / changelog:
# V3.0.1: TweakAtZ-state default 1 (i.e. the plugin works without any TweakAtZ comment)
@ -33,13 +35,17 @@
# V5.0: Bugfix for fall back after one layer and doubled G0 commands when using print speed tweak, Initial version for Cura 2.x
# V5.0.1: Bugfix for calling unknown property 'bedTemp' of previous settings storage and unknown variable 'speed'
# V5.1: API Changes included for use with Cura 2.2
# V5.2.0: Wes Hanney. Added support for changing Retract Length and Speed. Removed layer spread option. Fixed issue of cumulative ChangeZ
# V5.2.0: Wes Hanney. Added support for changing Retract Length and Speed. Removed layer spread option. Fixed issue of cumulative ChangeAtZ
# mods so they can now properly be stacked on top of each other. Applied code refactoring to clean up various coding styles. Added comments.
# Broke up functions for clarity. Split up class so it can be debugged outside of Cura.
# V5.2.1: Wes Hanney. Added support for firmware based retractions. Fixed issue of properly restoring previous values in single layer option.
# Added support for outputting changes to LCD (untested). Added type hints to most functions and variables. Added more comments. Created GCodeCommand
# class for better detection of G1 vs G10 or G11 commands, and accessing arguments. Moved most GCode methods to GCodeCommand class. Improved wording
# of Single Layer vs Keep Layer to better reflect what was happening.
# V5.3.0 Alex Jaxon, Added option to modify Build Volume Temperature keeping current format
#
# Uses -
# M220 S<factor in percent> - set speed factor override percentage
@ -56,9 +62,9 @@ from ..Script import Script
import re
# this was broken up into a separate class so the main ChangeZ script could be debugged outside of Cura
# this was broken up into a separate class so the main ChangeAtZ script could be debugged outside of Cura
class ChangeAtZ(Script):
version = "5.2.1"
version = "5.3.0"
def getSettingDataString(self):
return """{
@ -69,10 +75,10 @@ class ChangeAtZ(Script):
"settings": {
"caz_enabled": {
"label": "Enabled",
"description": "Allows adding multiple ChangeZ mods and disabling them as needed.",
"description": "Allows adding multiple ChangeAtZ mods and disabling them as needed.",
"type": "bool",
"default_value": true
},
},
"a_trigger": {
"label": "Trigger",
"description": "Trigger at height or at layer no.",
@ -119,7 +125,7 @@ class ChangeAtZ(Script):
"description": "Displays the current changes to the LCD",
"type": "bool",
"default_value": false
},
},
"e1_Change_speed": {
"label": "Change Speed",
"description": "Select if total speed (print and travel) has to be changed",
@ -222,6 +228,23 @@ class ChangeAtZ(Script):
"maximum_value_warning": "120",
"enabled": "h1_Change_bedTemp"
},
"h1_Change_buildVolumeTemperature": {
"label": "Change Build Volume Temperature",
"description": "Select if Build Volume Temperature has to be changed",
"type": "bool",
"default_value": false
},
"h2_buildVolumeTemperature": {
"label": "Build Volume Temperature",
"description": "New Build Volume Temperature",
"unit": "C",
"type": "float",
"default_value": 20,
"minimum_value": "0",
"minimum_value_warning": "10",
"maximum_value_warning": "50",
"enabled": "h1_Change_buildVolumeTemperature"
},
"i1_Change_extruderOne": {
"label": "Change Extruder 1 Temp",
"description": "Select if First Extruder Temperature has to be changed",
@ -278,25 +301,25 @@ class ChangeAtZ(Script):
"description": "Indicates you would like to modify retraction properties.",
"type": "bool",
"default_value": false
},
},
"caz_retractstyle": {
"label": "Retract Style",
"description": "Specify if you're using firmware retraction or linear move based retractions. Check your printer settings to see which you're using.",
"type": "enum",
"options": {
"linear": "Linear Move",
"linear": "Linear Move",
"firmware": "Firmware"
},
"default_value": "linear",
"enabled": "caz_change_retract"
},
},
"caz_change_retractfeedrate": {
"label": "Change Retract Feed Rate",
"description": "Changes the retraction feed rate during print",
"type": "bool",
"default_value": false,
"enabled": "caz_change_retract"
},
},
"caz_retractfeedrate": {
"label": "Retract Feed Rate",
"description": "New Retract Feed Rate (mm/s)",
@ -325,7 +348,7 @@ class ChangeAtZ(Script):
"minimum_value_warning": "0",
"maximum_value_warning": "20",
"enabled": "caz_change_retractlength"
}
}
}
}"""
@ -345,6 +368,7 @@ class ChangeAtZ(Script):
self.setIntSettingIfEnabled(caz_instance, "g3_Change_flowrateOne", "flowrateOne", "g4_flowrateOne")
self.setIntSettingIfEnabled(caz_instance, "g5_Change_flowrateTwo", "flowrateTwo", "g6_flowrateTwo")
self.setFloatSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp")
self.setFloatSettingIfEnabled(caz_instance, "h1_Change_buildVolumeTemperature", "buildVolumeTemperature", "h2_buildVolumeTemperature")
self.setFloatSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne")
self.setFloatSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo")
self.setIntSettingIfEnabled(caz_instance, "j1_Change_fanSpeed", "fanSpeed", "j2_fanSpeed")
@ -776,6 +800,10 @@ class ChangeAtZProcessor:
if "bedTemp" in values:
codes.append("BedTemp: " + str(round(values["bedTemp"])))
# looking for wait for Build Volume Temperature
if "buildVolumeTemperature" in values:
codes.append("buildVolumeTemperature: " + str(round(values["buildVolumeTemperature"])))
# set our extruder one temp (if specified)
if "extruderOne" in values:
codes.append("Extruder 1 Temp: " + str(round(values["extruderOne"])))
@ -858,6 +886,10 @@ class ChangeAtZProcessor:
if "bedTemp" in values:
codes.append("M140 S" + str(values["bedTemp"]))
# looking for wait for Build Volume Temperature
if "buildVolumeTemperature" in values:
codes.append("M141 S" + str(values["buildVolumeTemperature"]))
# set our extruder one temp (if specified)
if "extruderOne" in values:
codes.append("M104 S" + str(values["extruderOne"]) + " T0")
@ -943,7 +975,7 @@ class ChangeAtZProcessor:
# nothing to do
return ""
# Returns the unmodified GCODE line from previous ChangeZ edits
# Returns the unmodified GCODE line from previous ChangeAtZ edits
@staticmethod
def getOriginalLine(line: str) -> str:
@ -990,7 +1022,7 @@ class ChangeAtZProcessor:
else:
return self.currentZ >= self.targetZ
# Marks any current ChangeZ layer defaults in the layer for deletion
# Marks any current ChangeAtZ layer defaults in the layer for deletion
@staticmethod
def markChangesForDeletion(layer: str):
return re.sub(r";\[CAZD:", ";[CAZD:DELETE:", layer)
@ -1288,7 +1320,7 @@ class ChangeAtZProcessor:
# flag that we're inside our target layer
self.insideTargetLayer = True
# Removes all the ChangeZ layer defaults from the given layer
# Removes all the ChangeAtZ layer defaults from the given layer
@staticmethod
def removeMarkedChanges(layer: str) -> str:
return re.sub(r";\[CAZD:DELETE:[\s\S]+?:CAZD\](\n|$)", "", layer)
@ -1364,6 +1396,16 @@ class ChangeAtZProcessor:
# move to the next command
return
# handle Build Volume Temperature changes, really shouldn't want to wait for enclousure temp mid print though.
if command.command == "M141" or command.command == "M191":
# get our bed temp if provided
if "S" in command.arguments:
self.lastValues["buildVolumeTemperature"] = command.getArgumentAsFloat("S")
# move to the next command
return
# handle extruder temp changes
if command.command == "M104" or command.command == "M109":

View file

@ -458,7 +458,7 @@ class PauseAtHeight(Script):
# Optionally extrude material
if extrude_amount != 0:
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = 200) + "\n"
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = 200) + "; Extra extrude after the unpause\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"
@ -479,7 +479,15 @@ class PauseAtHeight(Script):
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"
extrusion_mode_string = "absolute"
extrusion_mode_numeric = 82
relative_extrusion = Application.getInstance().getGlobalContainerStack().getProperty("relative_extrusion", "value")
if relative_extrusion:
extrusion_mode_string = "relative"
extrusion_mode_numeric = 83
prepend_gcode += self.putValue(M = extrusion_mode_numeric) + " ; switch back to " + extrusion_mode_string + " E values\n"
# reset extrude value to pre pause value
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
@ -489,18 +497,17 @@ class PauseAtHeight(Script):
# Set extruder resume temperature
prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, 0))) + " ; resume temperature\n"
# Push the filament back,
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
if extrude_amount != 0: # Need to prime after the pause.
# Push the filament back.
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
# Optionally extrude material
if extrude_amount != 0:
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = extrude_speed * 60) + "\n"
# Prime the material.
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = extrude_speed * 60) + "; Extra extrude after the unpause\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 = retraction_speed * 60) + "\n"
# And retract again to make the movements back to the starting position.
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"
# Move the head back
if park_enabled:

View file

@ -1,4 +1,4 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Math.Color import Color

View file

@ -56,12 +56,12 @@ vertex41core =
value = (abs_value - min_value) / (max_value - min_value);
}
float red = value;
float green = 1-abs(1-4*value);
float green = 1.0 - abs(1.0 - 4.0 * value);
if (value > 0.375)
{
green = 0.5;
}
float blue = max(1-4*value, 0);
float blue = max(1.0 - 4.0 * value, 0.0);
return vec4(red, green, blue, 1.0);
}
@ -76,7 +76,7 @@ vertex41core =
{
value = (abs_value - min_value) / (max_value - min_value);
}
float red = min(max(4*value-2, 0), 1);
float red = min(max(4.0 * value - 2.0, 0.0), 1.0);
float green = min(1.5*value, 0.75);
if (value > 0.75)
{
@ -98,18 +98,18 @@ vertex41core =
value = (abs_value - min_value) / (max_value - min_value);
}
float red = value;
float green = 1 - abs(1 - 4 * value);
float green = 1.0 - abs(1.0 - 4.0 * value);
if(value > 0.375)
{
green = 0.5;
}
float blue = max(1 - 4 * value, 0);
float blue = max(1.0 - 4.0 * value, 0.0);
return vec4(red, green, blue, 1.0);
}
float clamp(float v)
{
float t = v < 0 ? 0 : v;
float t = v < 0.0 ? 0.0 : v;
return t > 1.0 ? 1.0 : t;
}

View file

@ -2,7 +2,7 @@
"name": "Simulation View",
"author": "Ultimaker B.V.",
"version": "1.0.1",
"description": "Provides the Simulation view.",
"description": "Provides the preview of sliced layerdata.",
"api": 7,
"i18n-catalog": "cura"
}

View file

@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QApplication
from UM.Application import Application
from UM.Math.Vector import Vector
from UM.Operations.TranslateOperation import TranslateOperation
from UM.Tool import Tool
from UM.Event import Event, MouseEvent
from UM.Mesh.MeshBuilder import MeshBuilder
@ -120,8 +121,8 @@ class SupportEraser(Tool):
# First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent
op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot()))
op.addOperation(SetParentOperation(node, parent))
op.addOperation(TranslateOperation(node, position, set_position = True))
op.push()
node.setPosition(position, CuraSceneNode.TransformSpace.World)
CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)

View file

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

View file

@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides support for reading Ultimaker Format Packages.",
"supported_sdk_versions": ["7.8.0"],
"supported_sdk_versions": ["7.9.0"],
"i18n-catalog": "cura"
}

View file

@ -73,6 +73,8 @@ Item
switch (printJob.state)
{
case "wait_cleanup":
// This hack was removed previously. Then we found out that we don't get back 'aborted_wait_cleanup'
// for the UM2+C it seems. Will communicate this to other teams, in the mean time, put this back.
if (printJob.timeTotal > printJob.timeElapsed)
{
return catalog.i18nc("@label:status", "Aborted");
@ -88,6 +90,20 @@ Item
return catalog.i18nc("@label:status", "Aborting...");
case "aborted": // NOTE: Unused, see above
return catalog.i18nc("@label:status", "Aborted");
case "aborted_post_print":
return catalog.i18nc("@label:status", "Aborted");
case "aborted_wait_user_action":
return catalog.i18nc("@label:status", "Aborted");
case "aborted_wait_cleanup":
return catalog.i18nc("@label:status", "Aborted");
case "failed":
return catalog.i18nc("@label:status", "Failed");
case "failed_post_print":
return catalog.i18nc("@label:status", "Failed");
case "failed_wait_user_action":
return catalog.i18nc("@label:status", "Failed");
case "failed_wait_cleanup":
return catalog.i18nc("@label:status", "Failed");
case "pausing":
return catalog.i18nc("@label:status", "Pausing...");
case "paused":
@ -97,7 +113,7 @@ Item
case "queued":
return catalog.i18nc("@label:status", "Action required");
default:
return catalog.i18nc("@label:status", "Finishes %1 at %2".arg(OutputDevice.getDateCompleted(printJob.timeRemaining)).arg(OutputDevice.getTimeCompleted(printJob.timeRemaining)));
return catalog.i18nc("@label:status", "Finishes %1 at %2").arg(OutputDevice.getDateCompleted(printJob.timeRemaining)).arg(OutputDevice.getTimeCompleted(printJob.timeRemaining));
}
}
width: contentWidth

View file

@ -1,4 +1,4 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
@ -16,9 +16,10 @@ from UM.Util import parseBool
from cura.API import Account
from cura.API.Account import SyncState
from cura.CuraApplication import CuraApplication
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To update printer metadata with information received about cloud printers.
from cura.Settings.CuraStackBuilder import CuraStackBuilder
from cura.Settings.GlobalStack import GlobalStack
from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT
from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES, META_UM_LINKED_TO_ACCOUNT
from .CloudApiClient import CloudApiClient
from .CloudOutputDevice import CloudOutputDevice
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
@ -127,8 +128,12 @@ class CloudOutputDeviceManager:
# to the current account
if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
if not self._um_cloud_printers[device_id].getMetaDataEntry(META_CAPABILITIES, None):
self._um_cloud_printers[device_id].setMetaDataEntry(META_CAPABILITIES, ",".join(cluster_data.capabilities))
self._onDevicesDiscovered(new_clusters)
self._updateOnlinePrinters(all_clusters)
# Hide the current removed_printers_message, if there is any
if self._removed_printers_message:
self._removed_printers_message.actionTriggered.disconnect(self._onRemovedPrintersMessageActionTriggered)
@ -154,6 +159,8 @@ class CloudOutputDeviceManager:
self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
Logger.debug("Synced cloud printers with account.")
def _onGetRemoteClusterFailed(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
@ -255,6 +262,16 @@ class CloudOutputDeviceManager:
message_text = self.i18n_catalog.i18nc("info:status", "Printers added from Digital Factory:") + "<ul>" + device_names + "</ul>"
message.setText(message_text)
def _updateOnlinePrinters(self, printer_responses: Dict[str, CloudClusterResponse]) -> None:
"""
Update the metadata of the printers to store whether they are online or not.
:param printer_responses: The responses received from the API about the printer statuses.
"""
for container_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "machine"):
cluster_id = container_stack.getMetaDataEntry("um_cloud_cluster_id", "")
if cluster_id in printer_responses:
container_stack.setMetaDataEntry("is_online", printer_responses[cluster_id].is_online)
def _updateOutdatedMachine(self, outdated_machine: GlobalStack, new_cloud_output_device: CloudOutputDevice) -> None:
"""
Update the cloud metadata of a pre-existing machine that is rediscovered (e.g. if the printer was removed and

View file

@ -37,7 +37,7 @@ class CloudClusterResponse(BaseModel):
self.friendly_name = friendly_name
self.printer_type = printer_type
self.printer_count = printer_count
self.capabilities = capabilities
self.capabilities = capabilities if capabilities is not None else []
super().__init__(**kwargs)
# Validates the model, raising an exception if the model is invalid.
@ -45,3 +45,10 @@ class CloudClusterResponse(BaseModel):
super().validate()
if not self.cluster_id:
raise ValueError("cluster_id is required on CloudCluster")
def __repr__(self) -> str:
"""
Convenience function for printing when debugging.
:return: A human-readable representation of the data in this object.
"""
return str({k: v for k, v in self.__dict__.items() if k in {"cluster_id", "host_guid", "host_name", "status", "is_online", "host_version", "host_internal_ip", "friendly_name", "printer_type", "printer_count", "capabilities"}})

View file

@ -40,7 +40,7 @@ class ClusterPrintJobStatus(BaseModel):
configuration_changes_required: List[
Union[Dict[str, Any], ClusterPrintJobConfigurationChange]] = None,
build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None,
compatible_machine_families: List[str] = None,
compatible_machine_families: Optional[List[str]] = None,
impediments_to_printing: List[Union[Dict[str, Any], ClusterPrintJobImpediment]] = None,
preview_url: Optional[str] = None,
**kwargs) -> None:
@ -97,7 +97,7 @@ class ClusterPrintJobStatus(BaseModel):
configuration_changes_required) \
if configuration_changes_required else []
self.build_plate = self.parseModel(ClusterBuildPlate, build_plate) if build_plate else None
self.compatible_machine_families = compatible_machine_families if compatible_machine_families else []
self.compatible_machine_families = compatible_machine_families if compatible_machine_families is not None else []
self.impediments_to_printing = self.parseModels(ClusterPrintJobImpediment, impediments_to_printing) \
if impediments_to_printing else []
@ -130,8 +130,10 @@ class ClusterPrintJobStatus(BaseModel):
model.updateConfiguration(self._createConfigurationModel())
model.updateTimeTotal(self.time_total)
model.updateTimeElapsed(self.time_elapsed)
model.updateOwner(self.owner)
if self.time_elapsed is not None:
model.updateTimeElapsed(self.time_elapsed)
if self.owner is not None:
model.updateOwner(self.owner)
model.updateState(self.status)
model.setCompatibleMachineFamilies(self.compatible_machine_families)

View file

@ -3,6 +3,7 @@
import configparser
import io
import json
import os.path
from typing import List, Tuple
@ -49,6 +50,28 @@ class VersionUpgrade411to412(VersionUpgrade):
# Update version number.
parser["metadata"]["setting_version"] = "19"
# If the account scope in 4.11 is outdated, delete it so that the user is enforced to log in again and get the
# correct permissions.
new_scopes = {"account.user.read",
"drive.backup.read",
"drive.backup.write",
"packages.download",
"packages.rating.read",
"packages.rating.write",
"connect.cluster.read",
"connect.cluster.write",
"library.project.read",
"library.project.write",
"cura.printjob.read",
"cura.printjob.write",
"cura.mesh.read",
"cura.mesh.write",
"cura.material.write"}
if "ultimaker_auth_data" in parser["general"]:
ultimaker_auth_data = json.loads(parser["general"]["ultimaker_auth_data"])
if new_scopes - set(ultimaker_auth_data["scope"].split(" ")):
parser["general"]["ultimaker_auth_data"] = "{}"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View file

@ -0,0 +1,118 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import configparser
from typing import Tuple, List
import io
from UM.VersionUpgrade import VersionUpgrade
_removed_settings = {
"travel_compensate_overlapping_walls_enabled",
"travel_compensate_overlapping_walls_0_enabled",
"travel_compensate_overlapping_walls_x_enabled",
"fill_perimeter_gaps",
"wall_min_flow",
"wall_min_flow_retract",
"speed_equalize_flow_enabled",
"speed_equalize_flow_min"
}
class VersionUpgrade49to50(VersionUpgrade):
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades preferences to remove from the visibility list the settings that were removed in this version.
It also changes the preferences to have the new version number.
This removes any settings that were removed in the new Cura version.
:param serialized: The original contents of the preferences file.
:param filename: The file name of the preferences file.
:return: A list of new file names, and a list of the new contents for
those files.
"""
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
parser["metadata"]["setting_version"] = "18"
# Remove deleted settings from the visible settings list.
if "general" in parser and "visible_settings" in parser["general"]:
visible_settings = set(parser["general"]["visible_settings"].split(";"))
for removed in _removed_settings:
if removed in visible_settings:
visible_settings.remove(removed)
# Replace Outer Before Inner Walls with equivalent.
if "outer_inset_first" in visible_settings:
visible_settings.remove("outer_inset_first")
visible_settings.add("inset_direction")
parser["general"]["visible_settings"] = ";".join(visible_settings)
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades instance containers to remove the settings that were removed in this version.
It also changes the instance containers to have the new version number.
This removes any settings that were removed in the new Cura version and updates settings that need to be updated
with a new value.
: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)
# Update version number.
parser["metadata"]["setting_version"] = "18"
if "values" in parser:
# Remove deleted settings from the instance containers.
for removed in _removed_settings:
if removed in parser["values"]:
del parser["values"][removed]
# Replace Outer Before Inner Walls with equivalent setting.
if "outer_inset_first" in parser["values"]:
old_value = parser["values"]["outer_inset_first"]
if old_value.startswith("="): # Was already a formula.
old_value = old_value[1:]
parser["values"]["inset_direction"] = f"='outside_in' if ({old_value}) else 'inside_out'" # Makes it work both with plain setting values and formulas.
# Disable Fuzzy Skin as it doesn't work with with the libArachne walls
if "magic_fuzzy_skin_enabled" in parser["values"]:
parser["values"]["magic_fuzzy_skin_enabled"] = "False"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades stacks to have the new version number.
: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
those files.
"""
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
if "metadata" not in parser:
parser["metadata"] = {}
parser["general"]["version"] = "5"
parser["metadata"]["setting_version"] = "18"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View file

@ -0,0 +1,61 @@
# Copyright (c) 2020 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, Dict, TYPE_CHECKING
from . import VersionUpgrade49to50
if TYPE_CHECKING:
from UM.Application import Application
upgrade = VersionUpgrade49to50.VersionUpgrade49to50()
def getMetaData() -> Dict[str, Any]:
return { # Since there is no VersionUpgrade from 48 to 49 yet, upgrade the 48 profiles to 50.
"version_upgrade": {
# From To Upgrade function
("preferences", 6000016): ("preferences", 6000018, upgrade.upgradePreferences),
("machine_stack", 5000016): ("machine_stack", 5000018, upgrade.upgradeStack),
("extruder_train", 5000016): ("extruder_train", 5000018, upgrade.upgradeStack),
("machine_stack", 4000018): ("machine_stack", 5000018, upgrade.upgradeStack), # We made a mistake in the arachne beta 1
("extruder_train", 4000018): ("extruder_train", 5000018, upgrade.upgradeStack), # We made a mistake in the arachne beta 1
("definition_changes", 4000016): ("definition_changes", 4000018, upgrade.upgradeInstanceContainer),
("quality_changes", 4000016): ("quality_changes", 4000018, upgrade.upgradeInstanceContainer),
("quality", 4000016): ("quality", 4000018, upgrade.upgradeInstanceContainer),
("user", 4000016): ("user", 4000018, upgrade.upgradeInstanceContainer),
},
"sources": {
"preferences": {
"get_version": upgrade.getCfgVersion,
"location": {"."}
},
"machine_stack": {
"get_version": upgrade.getCfgVersion,
"location": {"./machine_instances"}
},
"extruder_train": {
"get_version": upgrade.getCfgVersion,
"location": {"./extruders"}
},
"definition_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./definition_changes"}
},
"quality_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality_changes"}
},
"quality": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality"}
},
"user": {
"get_version": upgrade.getCfgVersion,
"location": {"./user"}
}
}
}
def register(app: "Application") -> Dict[str, Any]:
return {"version_upgrade": upgrade}

View file

@ -0,0 +1,8 @@
{
"name": "Version Upgrade 4.9 to 5.0",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 4.9 to Cura 5.0.",
"api": "7.4.0",
"i18n-catalog": "cura"
}