Make a snapshot on slice instead of write.

In some cases, UFP-writing is going to be done when the OpenGL-context is off the main window. This doesn't work. That unfortunately also goes for this commit, but it's a work in progress.
This commit is contained in:
Remco Burema 2021-01-20 20:15:33 +01:00
parent cdedb56a9a
commit 4fc0612806
No known key found for this signature in database
GPG key ID: 215C49431D43F98C
2 changed files with 45 additions and 34 deletions

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. # 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. 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 time import time
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING 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.Backend.Backend import Backend, BackendState
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Signal import Signal from UM.Signal import Signal
@ -24,6 +26,8 @@ from UM.Tool import Tool #For typing.
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager 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 .ProcessSlicedLayersJob import ProcessSlicedLayersJob
from .StartSliceJob import StartSliceJob, StartJobResult from .StartSliceJob import StartSliceJob, StartJobResult
@ -153,6 +157,8 @@ class CuraEngineBackend(QObject, Backend):
self.determineAutoSlicing() self.determineAutoSlicing()
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged) application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
self._snapshot = None #type: Optional[QImage]
application.initializationFinished.connect(self.initialize) application.initializationFinished.connect(self.initialize)
def initialize(self) -> None: def initialize(self) -> None:
@ -241,9 +247,24 @@ class CuraEngineBackend(QObject, Backend):
self.markSliceAll() self.markSliceAll()
self.slice() 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: def slice(self) -> None:
"""Perform a slice of the scene.""" """Perform a slice of the scene."""
self._createSnapshot()
Logger.log("i", "Starting to slice...") Logger.log("i", "Starting to slice...")
self._slice_start_time = time() self._slice_start_time = time()
if not self._build_plates_to_be_sliced: if not self._build_plates_to_be_sliced:
@ -331,7 +352,6 @@ class CuraEngineBackend(QObject, Backend):
def _onStartSliceCompleted(self, job: StartSliceJob) -> None: def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
"""Event handler to call when the job to initiate the slicing process is """Event handler to call when the job to initiate the slicing process is
completed. completed.
When the start slice job is successfully completed, it will be happily When the start slice job is successfully completed, it will be happily

View file

@ -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 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 io import StringIO # For converting g-code to bytes.
from PyQt5.QtCore import QBuffer
from UM.Logger import Logger from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement. from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.PluginRegistry import PluginRegistry # To get the g-code writer. 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.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread from cura.Utils.Threading import call_on_qt_thread
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from plugins.CuraEngineBackend.CuraEngineBackend import CuraEngineBackend
METADATA_OBJECTS_PATH = "metadata/objects" 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 # 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 # 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 # 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")) 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") 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 snapshot = None
# self._createSnapshot() backend = CuraApplication.getInstance().getBackend()
# if isinstance(backend, CuraEngineBackend):
# # Store the thumbnail. snapshot = backend.getLatestSnapshot()
# if self._snapshot:
# archive.addContentType(extension = "png", mime_type = "image/png") # Store the thumbnail.
# thumbnail = archive.getStream("/Metadata/thumbnail.png") if snapshot:
# archive.addContentType(extension = "png", mime_type = "image/png")
# thumbnail_buffer = QBuffer() thumbnail = archive.getStream("/Metadata/thumbnail.png")
# thumbnail_buffer.open(QBuffer.ReadWrite)
# thumbnail_image = self._snapshot thumbnail_buffer = QBuffer()
# thumbnail_image.save(thumbnail_buffer, "PNG") thumbnail_buffer.open(QBuffer.ReadWrite)
# snapshot.save(thumbnail_buffer, "PNG")
# thumbnail.write(thumbnail_buffer.data())
# archive.addRelation(virtual_path = "/Metadata/thumbnail.png", thumbnail.write(thumbnail_buffer.data())
# relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", archive.addRelation(virtual_path = "/Metadata/thumbnail.png",
# origin = "/3D/model.gcode") relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail",
# else: origin = "/3D/model.gcode")
# Logger.log("d", "Thumbnail not created, cannot save it") else:
Logger.log("w", "Thumbnail not created, cannot save it")
# Store the material. # Store the material.
application = CuraApplication.getInstance() application = CuraApplication.getInstance()