From 73c99f1214cceaad6a59daa3eac4a1cab2431941 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Mon, 26 Jun 2023 15:03:14 +0200 Subject: [PATCH] fix: bqplot stuck after the first frame of the movie We have to run the loop outside the main thread; otherwise, the processing of messages from the frontend is blocked, causing the message with the first image to never be received. The "save_image" method can only save the next image after the previous image is received. --- .../plugins/export_plot/export_plot.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/jdaviz/configs/default/plugins/export_plot/export_plot.py b/jdaviz/configs/default/plugins/export_plot/export_plot.py index 4b4418ab28..98cb704e30 100644 --- a/jdaviz/configs/default/plugins/export_plot/export_plot.py +++ b/jdaviz/configs/default/plugins/export_plot/export_plot.py @@ -9,6 +9,7 @@ except ImportError: HAS_OPENCV = False else: + import threading import time HAS_OPENCV = True @@ -86,15 +87,7 @@ def vue_save_figure(self, filetype): """ self.save_figure(filetype=filetype) - def save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files=True): - """``i`` start/end control the frames being written out. - ``i_end`` is inclusive. This method creates a bunch of - PNG files (one per frame) and then deletes them after stitching - the video. - """ - if not HAS_OPENCV: - raise ImportError("Please install opencv-python to save cube as movie.") - + def _save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files=True): if self.config != "cubeviz": raise NotImplementedError(f"save_movie is not available for config={self.config}") @@ -143,7 +136,6 @@ def save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files slice_plg._on_slider_updated({'new': i}) cur_pngfile = f"._cubeviz_movie_frame_{i}.png" - # FIXME: bqplot stuck after first frame! # If we can fix this, maybe we can have callback to video.write and don't need PNG files. self.save_figure(filename=cur_pngfile, filetype="png") @@ -151,6 +143,11 @@ def save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files i += i_step time.sleep(ts) # Avoid giving user epilepsy + # Wait for the roundtrip to the frontend to complete in case the epilepsy + # mitigating sleep wasn't long enough + while self.viewer.figure._upload_png_callback is not None: + time.sleep(0.1) + for cur_pngfile in temp_png_files: video.write(cv2.imread(cur_pngfile)) @@ -164,6 +161,18 @@ def save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files for cur_pngfile in temp_png_files: os.remove(cur_pngfile) + def save_movie(self, i_start, i_end, filename=None, filetype=None, rm_temp_files=True): + """``i`` start/end control the frames being written out. + ``i_end`` is inclusive. This method creates a bunch of + PNG files (one per frame) and then deletes them after stitching + the video. + """ + if not HAS_OPENCV: + raise ImportError("Please install opencv-python to save cube as movie.") + threading.Thread( + target=lambda: self._save_movie(i_start, i_end, filename, filetype, rm_temp_files) + ).start() + def vue_save_movie(self, filetype): """ Callback for save movie events in the front end viewer toolbars. Uses