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

This commit is contained in:
Jelle Spijker 2021-02-05 08:48:57 +01:00
commit 55e3b858d6
No known key found for this signature in database
GPG key ID: 6662DC033BE6B99A
207 changed files with 3493 additions and 183 deletions

View file

@ -51,6 +51,10 @@ class ThreeMFReader(MeshReader):
self._root = None
self._base_name = ""
self._unit = None
self._empty_project = False
def emptyFileHintSet(self) -> bool:
return self._empty_project
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
if transformation == "":
@ -193,6 +197,7 @@ class ThreeMFReader(MeshReader):
return um_node
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
self._empty_project = False
result = []
# The base object of 3mf is a zipped archive.
try:
@ -257,6 +262,9 @@ class ThreeMFReader(MeshReader):
result.append(um_node)
if len(result) == 0:
self._empty_project = True
except Exception:
Logger.logException("e", "An exception occurred in 3mf reader.")
return []

View file

@ -43,6 +43,10 @@ class DriveApiService:
return
backup_list_response = HttpRequestManager.readJSON(reply)
if backup_list_response is None:
Logger.error("List of back-ups can't be parsed.")
changed([])
return
if "data" not in backup_list_response:
Logger.log("w", "Could not get backups from remote, actual response body was: %s",
str(backup_list_response))

View file

@ -69,7 +69,7 @@ Item
BackupListFooter
{
id: backupListFooter
showInfoButton: backupList.model.length > 4
}
}
}

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import argparse #To run the engine in debug mode if the front-end is in debug mode.
@ -9,6 +9,8 @@ import sys
from time import time
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
from PyQt5.QtGui import QImage
from UM.Backend.Backend import Backend, BackendState
from UM.Scene.SceneNode import SceneNode
from UM.Signal import Signal
@ -24,6 +26,8 @@ from UM.Tool import Tool #For typing.
from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread
from .ProcessSlicedLayersJob import ProcessSlicedLayersJob
from .StartSliceJob import StartSliceJob, StartJobResult
@ -153,6 +157,8 @@ class CuraEngineBackend(QObject, Backend):
self.determineAutoSlicing()
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
self._snapshot = None #type: Optional[QImage]
application.initializationFinished.connect(self.initialize)
def initialize(self) -> None:
@ -241,9 +247,27 @@ class CuraEngineBackend(QObject, Backend):
self.markSliceAll()
self.slice()
@call_on_qt_thread # must be called from the main thread because of OpenGL
def _createSnapshot(self) -> None:
self._snapshot = None
if not CuraApplication.getInstance().isVisible:
Logger.log("w", "Can't create snapshot when renderer not initialized.")
return
Logger.log("i", "Creating thumbnail image (just before slice)...")
try:
self._snapshot = Snapshot.snapshot(width = 300, height = 300)
except:
Logger.logException("w", "Failed to create snapshot image")
self._snapshot = None # Failing to create thumbnail should not fail creation of UFP
def getLatestSnapshot(self) -> Optional[QImage]:
return self._snapshot
def slice(self) -> None:
"""Perform a slice of the scene."""
self._createSnapshot()
Logger.log("i", "Starting to slice...")
self._slice_start_time = time()
if not self._build_plates_to_be_sliced:
@ -331,7 +355,6 @@ class CuraEngineBackend(QObject, Backend):
def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
"""Event handler to call when the job to initiate the slicing process is
completed.
When the start slice job is successfully completed, it will be happily

View file

@ -43,7 +43,7 @@ UM.Dialog
TextField {
id: peak_height
objectName: "Peak_Height"
validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/}
validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onPeakHeightChanged(text) }
}
@ -66,7 +66,7 @@ UM.Dialog
TextField {
id: base_height
objectName: "Base_Height"
validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/}
validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onBaseHeightChanged(text) }
}

View file

@ -52,11 +52,8 @@ class ImageReader(MeshReader):
def _generateSceneNode(self, file_name, xz_size, height_from_base, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm):
scene_node = SceneNode()
mesh = MeshBuilder()
img = QImage(file_name)
if img.isNull():
Logger.log("e", "Image is corrupt.")
return None
@ -70,11 +67,10 @@ class ImageReader(MeshReader):
height_from_base = max(height_from_base, 0)
base_height = max(base_height, 0)
peak_height = base_height + height_from_base
xz_size = max(xz_size, 1)
scale_vector = Vector(xz_size, peak_height, xz_size)
scale_vector = Vector(xz_size, height_from_base, xz_size)
if width > height:
scale_vector = scale_vector.set(z=scale_vector.z * aspect)
@ -132,7 +128,7 @@ class ImageReader(MeshReader):
if use_transparency_model:
divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel.
min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height)
min_luminance = (transmittance_1mm / 100.0) ** height_from_base
for (y, x) in numpy.ndindex(height_data.shape):
mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x]
height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this

View file

@ -137,6 +137,7 @@ Item
labelWidth: base.labelWidth
controlWidth: base.controlWidth
unitText: ""
decimals: 0
forceUpdateOnChangeFunction: forceUpdateFunction
}
}

View file

@ -330,6 +330,25 @@ Item
}
}
/*
- Fix for this issue: https://github.com/Ultimaker/Cura/issues/9167
- Allows user to toggle if GCODE coordinates are affected by the extruder offset.
- Machine wide setting. CuraEngine/src/gcodeExport.cpp is not set up to evaluate per extruder currently.
- If it is moved to per-extruder (unlikely), then this should be moved to the extruder tab.
*/
Cura.SimpleCheckBox // "GCode Affected By Extruder Offsets"
{
id: applyExtruderOffsetsCheckbox
containerStackId: machineStackId
settingKey: "machine_use_extruder_offset_to_offset_coords"
settingStoreIndex: propertyStoreIndex
labelText: catalog.i18nc("@label", "Apply Extruder offsets to GCode")
labelFont: base.labelFont
labelWidth: base.labelWidth
forceUpdateOnChangeFunction: forceUpdateFunction
}
/* The "Shared Heater" feature is temporarily disabled because its
implementation is incomplete. Printers with multiple filaments going
into one nozzle will keep the inactive filaments retracted at the

View file

@ -237,7 +237,7 @@ Item
id: settingLoader
width: UM.Theme.getSize("setting").width
height: UM.Theme.getSize("section").height
enabled: provider.properties.enabled === "True"
property var definition: model
property var settingDefinitionsModel: addedSettingsModel
property var propertyProvider: provider

View file

@ -774,15 +774,15 @@ class ChangeAtZProcessor:
# looking for wait for bed temp
if "bedTemp" in values:
codes.append("BedTemp: " + str(values["bedTemp"]))
codes.append("BedTemp: " + str(round(values["bedTemp"])))
# set our extruder one temp (if specified)
if "extruderOne" in values:
codes.append("Extruder 1 Temp: " + str(values["extruderOne"]))
codes.append("Extruder 1 Temp: " + str(round(values["extruderOne"])))
# set our extruder two temp (if specified)
if "extruderTwo" in values:
codes.append("Extruder 2 Temp: " + str(values["extruderTwo"]))
codes.append("Extruder 2 Temp: " + str(round(values["extruderTwo"])))
# set global flow rate
if "flowrate" in values:

View file

@ -0,0 +1,109 @@
import base64
from UM.Logger import Logger
from cura.Snapshot import Snapshot
from PyQt5.QtCore import QByteArray, QIODevice, QBuffer
from ..Script import Script
class CreateThumbnail(Script):
def __init__(self):
super().__init__()
def _createSnapshot(self, width, height):
Logger.log("d", "Creating thumbnail image...")
try:
return Snapshot.snapshot(width, height)
except Exception:
Logger.logException("w", "Failed to create snapshot image")
def _encodeSnapshot(self, snapshot):
Logger.log("d", "Encoding thumbnail image...")
try:
thumbnail_buffer = QBuffer()
thumbnail_buffer.open(QBuffer.ReadWrite)
thumbnail_image = snapshot
thumbnail_image.save(thumbnail_buffer, "PNG")
base64_bytes = base64.b64encode(thumbnail_buffer.data())
base64_message = base64_bytes.decode('ascii')
thumbnail_buffer.close()
return base64_message
except Exception:
Logger.logException("w", "Failed to encode snapshot image")
def _convertSnapshotToGcode(self, encoded_snapshot, width, height, chunk_size=78):
gcode = []
encoded_snapshot_length = len(encoded_snapshot)
gcode.append(";")
gcode.append("; thumbnail begin {} {} {}".format(
width, height, encoded_snapshot_length))
chunks = ["; {}".format(encoded_snapshot[i:i+chunk_size])
for i in range(0, len(encoded_snapshot), chunk_size)]
gcode.extend(chunks)
gcode.append("; thumbnail end")
gcode.append(";")
gcode.append("")
return gcode
def getSettingDataString(self):
return """{
"name": "Create Thumbnail",
"key": "CreateThumbnail",
"metadata": {},
"version": 2,
"settings":
{
"width":
{
"label": "Width",
"description": "Width of the generated thumbnail",
"unit": "px",
"type": "int",
"default_value": 32,
"minimum_value": "0",
"minimum_value_warning": "12",
"maximum_value_warning": "800"
},
"height":
{
"label": "Height",
"description": "Height of the generated thumbnail",
"unit": "px",
"type": "int",
"default_value": 32,
"minimum_value": "0",
"minimum_value_warning": "12",
"maximum_value_warning": "600"
}
}
}"""
def execute(self, data):
width = self.getSettingValueByKey("width")
height = self.getSettingValueByKey("height")
snapshot = self._createSnapshot(width, height)
if snapshot:
encoded_snapshot = self._encodeSnapshot(snapshot)
snapshot_gcode = self._convertSnapshotToGcode(
encoded_snapshot, width, height)
for layer in data:
layer_index = data.index(layer)
lines = data[layer_index].split("\n")
for line in lines:
if line.startswith(";Generated with Cura"):
line_index = lines.index(line)
insert_index = line_index + 1
lines[insert_index:insert_index] = snapshot_gcode
break
final_lines = "\n".join(lines)
data[layer_index] = final_lines
return data

View file

@ -182,8 +182,7 @@ class PauseAtHeight(Script):
"Repetier": "Repetier"
},
"default_value": "RepRap (Marlin/Sprinter)",
"enabled": false,
"default_value": ""
"enabled": false
},
"custom_gcode_before_pause":
{

View file

@ -15,9 +15,10 @@ from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from cura.Settings.ExtruderManager import ExtruderManager
from cura.LayerPolygon import LayerPolygon
import os.path
import numpy
## RenderPass used to display g-code paths.
from .NozzleNode import NozzleNode
@ -60,6 +61,9 @@ class SimulationPass(RenderPass):
self._current_shader = self._layer_shader
# Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
if not self._compatibility_mode:
self._layer_shader.setUniformValue("u_starts_color", Color(*Application.getInstance().getTheme().getColor("layerview_starts").getRgb()))
if self._layer_view:
self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
@ -71,6 +75,7 @@ class SimulationPass(RenderPass):
self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
self._layer_shader.setUniformValue("u_show_starts", self._layer_view.getShowStarts())
else:
#defaults
self._layer_shader.setUniformValue("u_max_feedrate", 1)
@ -83,6 +88,7 @@ class SimulationPass(RenderPass):
self._layer_shader.setUniformValue("u_show_helpers", 1)
self._layer_shader.setUniformValue("u_show_skin", 1)
self._layer_shader.setUniformValue("u_show_infill", 1)
self._layer_shader.setUniformValue("u_show_starts", 1)
if not self._tool_handle_shader:
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
@ -161,6 +167,13 @@ class SimulationPass(RenderPass):
self._current_shader = self._layer_shader
self._switching_layers = True
# The first line does not have a previous line: add a MoveCombingType in front for start detection
# this way the first start of the layer can also be drawn
prev_line_types = numpy.concatenate([numpy.asarray([LayerPolygon.MoveCombingType], dtype = numpy.float32), layer_data._attributes["line_types"]["value"]])
# Remove the last element
prev_line_types = prev_line_types[0:layer_data._attributes["line_types"]["value"].size]
layer_data._attributes["prev_line_types"] = {'opengl_type': 'float', 'value': prev_line_types, 'opengl_name': 'a_prev_line_type'}
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end), backface_cull = True)
layers_batch.addItem(node.getWorldTransformation(), layer_data)
layers_batch.render(self._scene.getActiveCamera())

View file

@ -104,13 +104,14 @@ class SimulationView(CuraView):
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
Application.getInstance().getPreferences().addPreference("view/force_layer_view_compatibility_mode", False)
Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 0)
Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 1) # Default to "Line Type".
Application.getInstance().getPreferences().addPreference("layerview/extruder_opacities", "")
Application.getInstance().getPreferences().addPreference("layerview/show_travel_moves", False)
Application.getInstance().getPreferences().addPreference("layerview/show_helpers", True)
Application.getInstance().getPreferences().addPreference("layerview/show_skin", True)
Application.getInstance().getPreferences().addPreference("layerview/show_infill", True)
Application.getInstance().getPreferences().addPreference("layerview/show_starts", True)
self._updateWithPreferences()
@ -146,6 +147,7 @@ class SimulationView(CuraView):
self._show_helpers = True
self._show_skin = True
self._show_infill = True
self._show_starts = True
self.resetLayerData()
def getActivity(self) -> bool:
@ -355,6 +357,13 @@ class SimulationView(CuraView):
def getShowInfill(self) -> bool:
return self._show_infill
def setShowStarts(self, show: bool) -> None:
self._show_starts = show
self.currentLayerNumChanged.emit()
def getShowStarts(self) -> bool:
return self._show_starts
def getCompatibilityMode(self) -> bool:
return self._compatibility_mode
@ -638,6 +647,7 @@ class SimulationView(CuraView):
self.setShowHelpers(bool(Application.getInstance().getPreferences().getValue("layerview/show_helpers")))
self.setShowSkin(bool(Application.getInstance().getPreferences().getValue("layerview/show_skin")))
self.setShowInfill(bool(Application.getInstance().getPreferences().getValue("layerview/show_infill")))
self.setShowStarts(bool(Application.getInstance().getPreferences().getValue("layerview/show_starts")))
self._startUpdateTopLayers()
self.preferencesChanged.emit()
@ -653,6 +663,7 @@ class SimulationView(CuraView):
"layerview/show_helpers",
"layerview/show_skin",
"layerview/show_infill",
"layerview/show_starts",
}:
return

View file

@ -82,6 +82,7 @@ Cura.ExpandableComponent
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
property bool show_starts: UM.Preferences.getValue("layerview/show_starts")
// If we are in compatibility mode, we only show the "line type"
property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
@ -250,6 +251,15 @@ Cura.ExpandableComponent
preference: "layerview/show_infill",
colorId: "layerview_infill"
});
if (! UM.SimulationView.compatibilityMode)
{
typesLegendModel.append({
label: catalog.i18nc("@label", "Starts"),
initialValue: viewSettings.show_starts,
preference: "layerview/show_starts",
colorId: "layerview_starts"
});
}
}
}

View file

@ -21,6 +21,7 @@ vertex41core =
in highp vec4 a_normal;
in highp vec2 a_line_dim; // line width and thickness
in highp float a_extruder;
in highp float a_prev_line_type;
in highp float a_line_type;
in highp float a_feedrate;
in highp float a_thickness;
@ -32,6 +33,7 @@ vertex41core =
out lowp vec2 v_line_dim;
out highp int v_extruder;
out highp mat4 v_extruder_opacity;
out float v_prev_line_type;
out float v_line_type;
out lowp vec4 f_color;
@ -92,6 +94,7 @@ vertex41core =
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
v_line_dim = a_line_dim;
v_extruder = int(a_extruder);
v_prev_line_type = a_prev_line_type;
v_line_type = a_line_type;
v_extruder_opacity = u_extruder_opacity;
@ -108,13 +111,16 @@ geometry41core =
uniform highp mat4 u_viewMatrix;
uniform highp mat4 u_projectionMatrix;
uniform lowp vec4 u_starts_color;
uniform int u_show_travel_moves;
uniform int u_show_helpers;
uniform int u_show_skin;
uniform int u_show_infill;
uniform int u_show_starts;
layout(lines) in;
layout(triangle_strip, max_vertices = 26) out;
layout(triangle_strip, max_vertices = 40) out;
in vec4 v_color[];
in vec3 v_vertex[];
@ -122,6 +128,7 @@ geometry41core =
in vec2 v_line_dim[];
in int v_extruder[];
in mat4 v_extruder_opacity[];
in float v_prev_line_type[];
in float v_line_type[];
out vec4 f_color;
@ -268,6 +275,29 @@ geometry41core =
EndPrimitive();
}
if ((u_show_starts == 1) && (v_prev_line_type[0] != 1) && (v_line_type[0] == 1)) {
float w = v_line_dim[0].x / 2;
float h = v_line_dim[0].y / 2;
myEmitVertex(v_vertex[0] + vec3( w, h, w), u_starts_color, normalize(vec3( 1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, w, 0.0))); // Front-top-left
myEmitVertex(v_vertex[0] + vec3(-w, h, w), u_starts_color, normalize(vec3(-1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, w, 0.0))); // Front-top-right
myEmitVertex(v_vertex[0] + vec3( w, -h, w), u_starts_color, normalize(vec3( 1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, w, 0.0))); // Front-bottom-left
myEmitVertex(v_vertex[0] + vec3(-w, -h, w), u_starts_color, normalize(vec3(-1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, w, 0.0))); // Front-bottom-right
myEmitVertex(v_vertex[0] + vec3(-w, -h, -w), u_starts_color, normalize(vec3(-1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, -w, 0.0))); // Back-bottom-right
myEmitVertex(v_vertex[0] + vec3(-w, h, w), u_starts_color, normalize(vec3(-1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, w, 0.0))); // Front-top-right
myEmitVertex(v_vertex[0] + vec3(-w, h, -w), u_starts_color, normalize(vec3(-1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, -w, 0.0))); // Back-top-right
myEmitVertex(v_vertex[0] + vec3( w, h, w), u_starts_color, normalize(vec3( 1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, w, 0.0))); // Front-top-left
myEmitVertex(v_vertex[0] + vec3( w, h, -w), u_starts_color, normalize(vec3( 1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, -w, 0.0))); // Back-top-left
myEmitVertex(v_vertex[0] + vec3( w, -h, w), u_starts_color, normalize(vec3( 1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, w, 0.0))); // Front-bottom-left
myEmitVertex(v_vertex[0] + vec3( w, -h, -w), u_starts_color, normalize(vec3( 1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, -w, 0.0))); // Back-bottom-left
myEmitVertex(v_vertex[0] + vec3(-w, -h, -w), u_starts_color, normalize(vec3(-1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, -w, 0.0))); // Back-bottom-right
myEmitVertex(v_vertex[0] + vec3( w, h, -w), u_starts_color, normalize(vec3( 1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, -w, 0.0))); // Back-top-left
myEmitVertex(v_vertex[0] + vec3(-w, h, -w), u_starts_color, normalize(vec3(-1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, -w, 0.0))); // Back-top-right
EndPrimitive();
}
}
fragment41core =
@ -312,10 +342,13 @@ u_diffuseColor = [1.0, 0.79, 0.14, 1.0]
u_minimumAlbedo = [0.1, 0.1, 0.1, 1.0]
u_shininess = 20.0
u_starts_color = [1.0, 1.0, 1.0, 1.0]
u_show_travel_moves = 0
u_show_helpers = 1
u_show_skin = 1
u_show_infill = 1
u_show_starts = 1
u_min_feedrate = 0
u_max_feedrate = 1
@ -337,6 +370,7 @@ a_normal = normal
a_line_dim = line_dim
a_extruder = extruder
a_material_color = material_color
a_prev_line_type = prev_line_type
a_line_type = line_type
a_feedrate = feedrate
a_thickness = thickness

View file

@ -1,3 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import List, Dict, Any, cast
@ -96,7 +99,10 @@ class SyncOrchestrator(Extension):
else:
self._cloud_api.unsubscribe(item["package_id"])
# delete temp file
os.remove(item["package_path"])
try:
os.remove(item["package_path"])
except EnvironmentError as e: # File was already removed, no access rights, etc.
Logger.error("Can't delete temporary package file: {err}".format(err = str(e)))
if has_changes:
self._restart_presenter.present()

View file

@ -46,8 +46,6 @@ class Toolbox(QObject, Extension):
self._application = application # type: CuraApplication
self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
# Network:
self._download_request_data = None # type: Optional[HttpRequestData]
self._download_progress = 0 # type: float

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 typing import cast, List, Dict
@ -7,16 +7,16 @@ from Charon.VirtualFile import VirtualFile # To open UFP files.
from Charon.OpenMode import OpenMode # To indicate that we want to write to UFP files.
from io import StringIO # For converting g-code to bytes.
from PyQt5.QtCore import QBuffer
from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.PluginRegistry import PluginRegistry # To get the g-code writer.
from PyQt5.QtCore import QBuffer
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread
from UM.i18n import i18nCatalog
@ -38,17 +38,6 @@ class UFPWriter(MeshWriter):
)
)
self._snapshot = None
def _createSnapshot(self, *args):
# must be called from the main thread because of OpenGL
Logger.log("d", "Creating thumbnail image...")
try:
self._snapshot = Snapshot.snapshot(width = 300, height = 300)
except Exception:
Logger.logException("w", "Failed to create snapshot image")
self._snapshot = None # Failing to create thumbnail should not fail creation of UFP
# This needs to be called on the main thread (Qt thread) because the serialization of material containers can
# trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the
# Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled
@ -72,24 +61,23 @@ class UFPWriter(MeshWriter):
gcode.write(gcode_textio.getvalue().encode("UTF-8"))
archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")
self._createSnapshot()
# Store the thumbnail.
if self._snapshot:
# Attempt to store the thumbnail, if any:
backend = CuraApplication.getInstance().getBackend()
snapshot = None if getattr(backend, "getLatestSnapshot", None) is None else backend.getLatestSnapshot()
if snapshot:
archive.addContentType(extension = "png", mime_type = "image/png")
thumbnail = archive.getStream("/Metadata/thumbnail.png")
thumbnail_buffer = QBuffer()
thumbnail_buffer.open(QBuffer.ReadWrite)
thumbnail_image = self._snapshot
thumbnail_image.save(thumbnail_buffer, "PNG")
snapshot.save(thumbnail_buffer, "PNG")
thumbnail.write(thumbnail_buffer.data())
archive.addRelation(virtual_path = "/Metadata/thumbnail.png",
relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail",
origin = "/3D/model.gcode")
else:
Logger.log("d", "Thumbnail not created, cannot save it")
Logger.log("w", "Thumbnail not created, cannot save it")
# Store the material.
application = CuraApplication.getInstance()

View file

@ -1,3 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional
from UM.FileHandler.FileHandler import FileHandler

View file

@ -151,7 +151,7 @@ class XmlMaterialProfile(InstanceContainer):
"version": self.CurrentFdmMaterialVersion})
## Begin Metadata Block
builder.start("metadata") # type: ignore
builder.start("metadata", {}) # type: ignore
metadata = copy.deepcopy(self.getMetaData())
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
@ -165,21 +165,21 @@ class XmlMaterialProfile(InstanceContainer):
properties = metadata.pop("properties", {})
## Begin Name Block
builder.start("name") # type: ignore
builder.start("name", {}) # type: ignore
builder.start("brand") # type: ignore
builder.start("brand", {}) # type: ignore
builder.data(metadata.pop("brand", ""))
builder.end("brand")
builder.start("material") # type: ignore
builder.start("material", {}) # type: ignore
builder.data(metadata.pop("material", ""))
builder.end("material")
builder.start("color") # type: ignore
builder.start("color", {}) # type: ignore
builder.data(metadata.pop("color_name", ""))
builder.end("color")
builder.start("label") # type: ignore
builder.start("label", {}) # type: ignore
builder.data(self.getName())
builder.end("label")
@ -190,7 +190,7 @@ class XmlMaterialProfile(InstanceContainer):
key_to_use = key
if key in self._metadata_tags_that_have_cura_namespace:
key_to_use = "cura:" + key_to_use
builder.start(key_to_use) # type: ignore
builder.start(key_to_use, {}) # type: ignore
if value is not None: #Nones get handled well by the builder.
#Otherwise the builder always expects a string.
#Deserialize expects the stringified version.
@ -202,10 +202,10 @@ class XmlMaterialProfile(InstanceContainer):
## End Metadata Block
## Begin Properties Block
builder.start("properties") # type: ignore
builder.start("properties", {}) # type: ignore
for key, value in properties.items():
builder.start(key) # type: ignore
builder.start(key, {}) # type: ignore
builder.data(value)
builder.end(key)
@ -213,7 +213,7 @@ class XmlMaterialProfile(InstanceContainer):
## End Properties Block
## Begin Settings Block
builder.start("settings") # type: ignore
builder.start("settings", {}) # type: ignore
if self.getMetaDataEntry("definition") == "fdmprinter":
for instance in self.findInstances():
@ -258,7 +258,7 @@ class XmlMaterialProfile(InstanceContainer):
product = product_name
break
builder.start("machine") # type: ignore
builder.start("machine", {}) # type: ignore
builder.start("machine_identifier", {
"manufacturer": container.getMetaDataEntry("machine_manufacturer",
definition_metadata.get("manufacturer", "Unknown")),