diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 4b196f7b5d..7a88618a7e 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -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,24 @@ 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 + 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 +352,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 diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index af208209e4..b7941c425f 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -7,19 +7,20 @@ 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 +from plugins.CuraEngineBackend.CuraEngineBackend import CuraEngineBackend METADATA_OBJECTS_PATH = "metadata/objects" @@ -38,17 +39,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,25 +62,26 @@ 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") - # TODO temporarily commented out, as is causes a crash whenever the UFPWriter is called outside of the main thread - # self._createSnapshot() - # - # # Store the thumbnail. - # if self._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") - # - # 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") + snapshot = None + backend = CuraApplication.getInstance().getBackend() + if isinstance(backend, CuraEngineBackend): + snapshot = backend.getLatestSnapshot() + + # Store the thumbnail. + if snapshot: + archive.addContentType(extension = "png", mime_type = "image/png") + thumbnail = archive.getStream("/Metadata/thumbnail.png") + + thumbnail_buffer = QBuffer() + thumbnail_buffer.open(QBuffer.ReadWrite) + 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("w", "Thumbnail not created, cannot save it") # Store the material. application = CuraApplication.getInstance()