From 9f8d15335e4698b4dbb261417a4bd7d8fb495605 Mon Sep 17 00:00:00 2001 From: diego Date: Tue, 14 Jul 2020 14:55:57 +0200 Subject: [PATCH 01/11] Fixed frame rate display in calibration mode --- lightsheet/gui/camera_gui.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lightsheet/gui/camera_gui.py b/lightsheet/gui/camera_gui.py index 9858e34d..b18c8cc2 100644 --- a/lightsheet/gui/camera_gui.py +++ b/lightsheet/gui/camera_gui.py @@ -146,16 +146,15 @@ def update_roi_info(self, width, height): "Current frame dimensions are:\nHeight: {}\nWidth: {}".format(int(height), int(width))) def update_camera_info(self): - triggered_frame_rate = self.state.get_triggered_frame_rate() - if triggered_frame_rate is not None: + frame_rate = self.state.get_triggered_frame_rate() + if frame_rate is not None: if self.state.global_state == GlobalState.PAUSED: self.lbl_camera_info.hide() else: self.lbl_camera_info.setStyleSheet("color: white") expected_frame_rate = None if self.state.global_state == GlobalState.PREVIEW: - frame_rate = self.state.current_camera_status.internal_frame_rate - self.lbl_camera_info.setText("Internal frame rate: {} Hz".format(round(frame_rate, 2))) + self.lbl_camera_info.setText("Camera frame rate: {} Hz".format(round(frame_rate, 2))) if self.state.global_state == GlobalState.VOLUME_PREVIEW: planes = self.state.volume_setting.n_planes - \ self.state.volume_setting.n_skip_start - self.state.volume_setting.n_skip_end @@ -168,11 +167,11 @@ def update_camera_info(self): self.lbl_camera_info.setText( "\n".join( [ - "Camera frame rate: {} Hz".format(round(triggered_frame_rate, 2)) + "Camera frame rate: {} Hz".format(round(frame_rate, 2)) ] + ( ["Camera is lagging behind. Decrease exposure, number of planes or frequency"] - if expected_frame_rate > (triggered_frame_rate * 1.1) + if expected_frame_rate > (frame_rate * 1.1) else [ "Seems to follow well current speed" ] @@ -180,7 +179,7 @@ def update_camera_info(self): ) ) - if expected_frame_rate > (triggered_frame_rate * 1.1): + if expected_frame_rate > (frame_rate * 1.1): self.lbl_camera_info.setStyleSheet("color: red") else: self.lbl_camera_info.setStyleSheet("color: green") From 1b0f0c9557ddb81c52db83f6682d04dcbe078f96 Mon Sep 17 00:00:00 2001 From: diego Date: Tue, 14 Jul 2020 15:04:41 +0200 Subject: [PATCH 02/11] Highlight frozen plane in waveform plot --- lightsheet/gui/waveform_gui.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lightsheet/gui/waveform_gui.py b/lightsheet/gui/waveform_gui.py index 65a3f3b3..e302119a 100644 --- a/lightsheet/gui/waveform_gui.py +++ b/lightsheet/gui/waveform_gui.py @@ -34,13 +34,22 @@ def update_pulses(self): self.plot_widget.removeItem(self.pulse_regions[region]) self.pulse_regions = [None] * len(pulse_times) for i_pulse, pulse in enumerate(pulse_times): - self.pulse_regions[i_pulse] = pg.LinearRegionItem( - values=(pulse, pulse + self.state.camera_settings.exposure / 1000), - movable=False, - brush=pg.mkBrush( - 166, 196, 240, 100 + if self.state.volume_setting.i_freeze - 1 == i_pulse: + self.pulse_regions[i_pulse] = pg.LinearRegionItem( + values=(pulse, pulse + self.state.camera_settings.exposure / 1000), + movable=False, + brush=pg.mkBrush( + 100, 100, 240, 100 + ) + ) + else: + self.pulse_regions[i_pulse] = pg.LinearRegionItem( + values=(pulse, pulse + self.state.camera_settings.exposure / 1000), + movable=False, + brush=pg.mkBrush( + 166, 196, 240, 100 + ) ) - ) for line in self.pulse_regions[i_pulse].lines: line.hide() self.plot_widget.addItem(self.pulse_regions[i_pulse]) From 1641d7a25acaeae296e672844a24a53965c659fe Mon Sep 17 00:00:00 2001 From: diego Date: Tue, 14 Jul 2020 17:23:05 +0200 Subject: [PATCH 03/11] Popup window with rich text instructions --- lightsheet/gui/save_settings_gui.py | 19 +++++++++++++++---- requirements.txt | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lightsheet/gui/save_settings_gui.py b/lightsheet/gui/save_settings_gui.py index c9fa71c8..33e4efb7 100644 --- a/lightsheet/gui/save_settings_gui.py +++ b/lightsheet/gui/save_settings_gui.py @@ -2,14 +2,17 @@ QVBoxLayout, QWidget, QPushButton, -QFileDialog + QFileDialog, + QDialog, + QTextEdit ) from PyQt5.QtCore import pyqtSignal from lightsheet.state import State from pathlib import Path from datetime import datetime +import markdown -DEFAULT_SETTIGNS_PATH = "C:/Users/portugueslab/lightsheet_settings" +DEFAULT_SETTINGS_PATH = "C:/Users/portugueslab/lightsheet_settings" class SavingSettingsWidget(QWidget): @@ -21,18 +24,26 @@ def __init__(self, st: State): self.setLayout(QVBoxLayout()) self.btn_load = QPushButton("Load settings") self.btn_save = QPushButton("Save settings") + self.btn_instructions = QPushButton("User guide") + f = open(r"../../lightsheet_procedure.md") + self.html_markdown = markdown.markdown(f.read()) + self.instructions = QTextEdit(self.html_markdown) + self.popup_window = QDialog() self.layout().addWidget(self.btn_load) self.layout().addWidget(self.btn_save) + self.layout().addWidget(self.btn_instructions) + self.popup_window.layout().addWidget(self.instructions) self.btn_load.clicked.connect(self.load) self.btn_save.clicked.connect(self.save) + self.btn_instructions.clicked.connect(self.popup_window.show) def load(self): - file, _ = QFileDialog.getOpenFileName(None, "Open settings file", DEFAULT_SETTIGNS_PATH, "*.json") + file, _ = QFileDialog.getOpenFileName(None, "Open settings file", DEFAULT_SETTINGS_PATH, "*.json") if Path(file).is_file(): self.state.restore_tree(file) self.sig_params_loaded.emit() def save(self): - pth = Path(DEFAULT_SETTIGNS_PATH) + pth = Path(DEFAULT_SETTINGS_PATH) filename = datetime.now().strftime("%y%m%d %H%M%S.json") self.state.save_tree(pth/filename) diff --git a/requirements.txt b/requirements.txt index f1dbe505..a4c2e75a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pyvisa==1.10.1 pyzmq==18.1.0 QDarkStyle==2.7 yagmail==0.11.224 +markdown==3.2.2 \ No newline at end of file From dca9c81975b81df8bd3c0d8578f363bb0ce87ebc Mon Sep 17 00:00:00 2001 From: diego Date: Wed, 15 Jul 2020 12:46:21 +0200 Subject: [PATCH 04/11] Removed file from another branch --- lightsheet/boost.py | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 lightsheet/boost.py diff --git a/lightsheet/boost.py b/lightsheet/boost.py deleted file mode 100644 index 81113ab8..00000000 --- a/lightsheet/boost.py +++ /dev/null @@ -1,26 +0,0 @@ -from multiprocessing import Process, Queue, Event -from queue import Empty - - -class FrameDispatcher(Process): - def __init__(self, stop_event: Event): - super().__init__() - self.stop_event = stop_event - - def run(self): - while not self.stop_event.is_set(): - pass - - def get_image(self): - try: - image = self.camera.image_queue.get(timeout=0.001) - if self.calibration_ref is not None: - image = neg_dif(image, self.calibration_ref) - if self.saver.saving_signal.is_set(): - if self.experiment_state == ExperimentPrepareState.EXPERIMENT_STARTED: - self.saver.save_queue.put(image) - return image - except Empty: - return None - - From 777e6af11eff37c9e6c271a4e98e2465509e425e Mon Sep 17 00:00:00 2001 From: diego Date: Wed, 15 Jul 2020 17:39:16 +0200 Subject: [PATCH 05/11] Reflect laser off at the end of experiment --- lightsheet/gui/main_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightsheet/gui/main_gui.py b/lightsheet/gui/main_gui.py index d411be2c..2e2a0eb0 100644 --- a/lightsheet/gui/main_gui.py +++ b/lightsheet/gui/main_gui.py @@ -109,7 +109,7 @@ def check_end_experiment(self): self.st.toggle_experiment_state() if self.st.pause_after: self.wid_status.setCurrentIndex(0) - self.st.laser.set_current(0) + self.wid_laser.toggle() self.refresh_param_values() self.wid_display.experiment_progress.hide() self.st.saver.saver_stopped_signal.clear() From 3997f9b2c941b7e21b19ebd3fdc5177a5bd57da1 Mon Sep 17 00:00:00 2001 From: diego Date: Wed, 15 Jul 2020 18:39:35 +0200 Subject: [PATCH 06/11] Maintain noise subtraction after experiment end --- lightsheet/gui/main_gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lightsheet/gui/main_gui.py b/lightsheet/gui/main_gui.py index 2e2a0eb0..2610ba58 100644 --- a/lightsheet/gui/main_gui.py +++ b/lightsheet/gui/main_gui.py @@ -91,7 +91,7 @@ def closeEvent(self, a0) -> None: self.st.wrap_up() a0.accept() - def refresh_param_values(self): + def refresh_param_values(self, omit_wid_camera=False): # TODO should be possible with lightparam, when it's implemented there remove here self.wid_laser.wid_settings.refresh_widgets() self.wid_scan.wid_planar.refresh_widgets() @@ -99,7 +99,8 @@ def refresh_param_values(self): self.wid_status.wid_calibration.refresh_widgets() self.wid_status.wid_single_plane.wid_singleplane.refresh_widgets() self.wid_display.wid_display_settings.refresh_widgets() - self.wid_camera.wid_camera_settings.refresh_widgets() + if not omit_wid_camera: + self.wid_camera.wid_camera_settings.refresh_widgets() self.wid_save_options.wid_save_options.refresh_widgets() self.wid_camera.set_roi() self.wid_save_options.set_locationbutton() @@ -110,7 +111,7 @@ def check_end_experiment(self): if self.st.pause_after: self.wid_status.setCurrentIndex(0) self.wid_laser.toggle() - self.refresh_param_values() + self.refresh_param_values(omit_wid_camera=True) self.wid_display.experiment_progress.hide() self.st.saver.saver_stopped_signal.clear() From 0288b44acd37d2289aba040d66599475a6a53d0a Mon Sep 17 00:00:00 2001 From: diego Date: Wed, 15 Jul 2020 22:30:57 +0200 Subject: [PATCH 07/11] Impossible to miss overwrite alert --- lightsheet/gui/save_gui.py | 2 ++ lightsheet/gui/scanning_gui.py | 25 ++++++++++++++++++++++++- lightsheet/state.py | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lightsheet/gui/save_gui.py b/lightsheet/gui/save_gui.py index d75a2170..e3509638 100644 --- a/lightsheet/gui/save_gui.py +++ b/lightsheet/gui/save_gui.py @@ -41,6 +41,8 @@ def set_locationbutton(self): self.save_location_button.setStyleSheet( "background-color:#b5880d; border-color:#fcc203" ) + self.state.save_settings.overwrite_save_folder = 1 else: self.save_location_button.setText("Save in " + pathtext) self.save_location_button.setStyleSheet("") + self.state.save_settings.overwrite_save_folder = 0 diff --git a/lightsheet/gui/scanning_gui.py b/lightsheet/gui/scanning_gui.py index 7075c74b..077060aa 100644 --- a/lightsheet/gui/scanning_gui.py +++ b/lightsheet/gui/scanning_gui.py @@ -3,7 +3,8 @@ QVBoxLayout, QPushButton, QLabel, - QCheckBox + QCheckBox, + QMessageBox ) from PyQt5.QtCore import QTimer from lightparam.gui import ParameterGui @@ -63,6 +64,11 @@ def __init__(self, state, timer): self.wid_collapsible_wave = CollapsibleWidget(child=self.wid_wave, name="Piezo impulse-response waveform") self.wid_collapsible_wave.toggle_collapse() + self.dialog_box = QMessageBox() + self.dialog_ok_button = self.dialog_box.addButton(self.dialog_box.Ok) + self.dialog_abort_button = self.dialog_box.addButton(self.dialog_box.Abort) + self.override_overwrite = False + self.layout().addWidget(self.wid_volume) self.layout().addWidget(self.btn_start) self.layout().addWidget(self.chk_pause) @@ -77,6 +83,7 @@ def __init__(self, state, timer): self.timer_scope_info.timeout.connect(self.update_alignment) self.chk_pause.clicked.connect(self.change_pause_status) + self.dialog_ok_button.clicked.connect(self.overwrite_anyway) self.chk_pause.click() @@ -109,5 +116,21 @@ def change_experiment_state(self): if self.state.experiment_state == ExperimentPrepareState.EXPERIMENT_STARTED: # Here what happens if experiment is aborted self.state.saver.saving_signal.clear() + elif self.state.save_settings.overwrite_save_folder == 1 and not self.override_overwrite: + self.overwrite_alert_popup() + self.override_overwrite = False else: self.state.toggle_experiment_state() + + def overwrite_alert_popup(self): + self.dialog_box.setIcon(QMessageBox.Warning) + self.dialog_box.setWindowTitle("Overwrite alert!") + self.dialog_box.setText( + "You are overwriting an existing folder with data. \n\n " + "Press ok to start the experiment anyway or abort to change saving folder." + ) + self.dialog_box.show() + + def overwrite_anyway(self): + self.override_overwrite = True + self.change_experiment_state() diff --git a/lightsheet/state.py b/lightsheet/state.py index bbfa250f..e7da5d3a 100644 --- a/lightsheet/state.py +++ b/lightsheet/state.py @@ -51,6 +51,7 @@ def __init__(self): self.save_dir = Param(r"F:/Vilim", gui=False) self.experiment_duration = Param(0, (0, 100_000), gui=False) self.notification_email = Param("None") + self.overwrite_save_folder = Param(0, (0, 1), gui=False, loadable=False) class ScanningSettings(ParametrizedQt): From fe4b622c815e3f4163f43fd3348eacfb59b66e10 Mon Sep 17 00:00:00 2001 From: diego Date: Thu, 16 Jul 2020 00:06:06 +0200 Subject: [PATCH 08/11] Instructions can't be modified --- lightsheet/gui/save_settings_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lightsheet/gui/save_settings_gui.py b/lightsheet/gui/save_settings_gui.py index 33e4efb7..d52e2fe2 100644 --- a/lightsheet/gui/save_settings_gui.py +++ b/lightsheet/gui/save_settings_gui.py @@ -28,6 +28,7 @@ def __init__(self, st: State): f = open(r"../../lightsheet_procedure.md") self.html_markdown = markdown.markdown(f.read()) self.instructions = QTextEdit(self.html_markdown) + self.instructions.setReadOnly(True) self.popup_window = QDialog() self.layout().addWidget(self.btn_load) self.layout().addWidget(self.btn_save) From 5e93c07022e777fd8f128811eaf403babd8fa6db Mon Sep 17 00:00:00 2001 From: diego Date: Thu, 16 Jul 2020 14:00:04 +0200 Subject: [PATCH 09/11] Refactored code waveform plot --- lightsheet/gui/waveform_gui.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lightsheet/gui/waveform_gui.py b/lightsheet/gui/waveform_gui.py index e302119a..089c9e98 100644 --- a/lightsheet/gui/waveform_gui.py +++ b/lightsheet/gui/waveform_gui.py @@ -3,6 +3,9 @@ from queue import Empty import numpy as np +color_plane = (166, 196, 240, 100) +color_current_plane = (100, 100, 240, 100) + class WaveformWidget(QWidget): def __init__(self, waveform_queue, timer, state): @@ -39,7 +42,7 @@ def update_pulses(self): values=(pulse, pulse + self.state.camera_settings.exposure / 1000), movable=False, brush=pg.mkBrush( - 100, 100, 240, 100 + *color_current_plane ) ) else: @@ -47,7 +50,7 @@ def update_pulses(self): values=(pulse, pulse + self.state.camera_settings.exposure / 1000), movable=False, brush=pg.mkBrush( - 166, 196, 240, 100 + *color_plane ) ) for line in self.pulse_regions[i_pulse].lines: From 5f5ab43df34efc7023db0ceada5ec61e35a1e27e Mon Sep 17 00:00:00 2001 From: diego Date: Fri, 17 Jul 2020 15:11:59 +0200 Subject: [PATCH 10/11] Fizes, tested in lightsheet --- lightsheet/gui/main_gui.py | 4 ++-- lightsheet/gui/save_settings_gui.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lightsheet/gui/main_gui.py b/lightsheet/gui/main_gui.py index 2610ba58..90f86f9d 100644 --- a/lightsheet/gui/main_gui.py +++ b/lightsheet/gui/main_gui.py @@ -101,8 +101,8 @@ def refresh_param_values(self, omit_wid_camera=False): self.wid_display.wid_display_settings.refresh_widgets() if not omit_wid_camera: self.wid_camera.wid_camera_settings.refresh_widgets() + self.wid_camera.set_roi() self.wid_save_options.wid_save_options.refresh_widgets() - self.wid_camera.set_roi() self.wid_save_options.set_locationbutton() def check_end_experiment(self): @@ -110,7 +110,7 @@ def check_end_experiment(self): self.st.toggle_experiment_state() if self.st.pause_after: self.wid_status.setCurrentIndex(0) - self.wid_laser.toggle() + self.wid_laser.btn_off.click() self.refresh_param_values(omit_wid_camera=True) self.wid_display.experiment_progress.hide() self.st.saver.saver_stopped_signal.clear() diff --git a/lightsheet/gui/save_settings_gui.py b/lightsheet/gui/save_settings_gui.py index d52e2fe2..c5808d55 100644 --- a/lightsheet/gui/save_settings_gui.py +++ b/lightsheet/gui/save_settings_gui.py @@ -6,7 +6,7 @@ QDialog, QTextEdit ) -from PyQt5.QtCore import pyqtSignal +from PyQt5.QtCore import pyqtSignal, Qt from lightsheet.state import State from pathlib import Path from datetime import datetime @@ -25,14 +25,15 @@ def __init__(self, st: State): self.btn_load = QPushButton("Load settings") self.btn_save = QPushButton("Save settings") self.btn_instructions = QPushButton("User guide") - f = open(r"../../lightsheet_procedure.md") + f = open(r"../lightsheet_procedure.md") self.html_markdown = markdown.markdown(f.read()) self.instructions = QTextEdit(self.html_markdown) self.instructions.setReadOnly(True) - self.popup_window = QDialog() + self.popup_window = QDialog(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.layout().addWidget(self.btn_load) self.layout().addWidget(self.btn_save) self.layout().addWidget(self.btn_instructions) + self.popup_window.setLayout(QVBoxLayout()) self.popup_window.layout().addWidget(self.instructions) self.btn_load.clicked.connect(self.load) self.btn_save.clicked.connect(self.save) From 0f15cfd8f204d6c4b5ab852c2d51a68024d719aa Mon Sep 17 00:00:00 2001 From: diego Date: Fri, 17 Jul 2020 19:38:45 +0200 Subject: [PATCH 11/11] Log piezo triggering in metadata --- lightsheet/gui/scanning_gui.py | 1 - lightsheet/gui/waveform_gui.py | 14 ++++---------- lightsheet/state.py | 22 +++++++++++++++++++++- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lightsheet/gui/scanning_gui.py b/lightsheet/gui/scanning_gui.py index 077060aa..9bec5279 100644 --- a/lightsheet/gui/scanning_gui.py +++ b/lightsheet/gui/scanning_gui.py @@ -57,7 +57,6 @@ def __init__(self, state, timer): self.lbl_interplane_distance.setStyleSheet("color: yellow") self.wid_wave = WaveformWidget( - waveform_queue=self.state.scanner.waveform_queue, timer=self.timer, state=self.state ) diff --git a/lightsheet/gui/waveform_gui.py b/lightsheet/gui/waveform_gui.py index 089c9e98..c3eedcd5 100644 --- a/lightsheet/gui/waveform_gui.py +++ b/lightsheet/gui/waveform_gui.py @@ -8,12 +8,11 @@ class WaveformWidget(QWidget): - def __init__(self, waveform_queue, timer, state): + def __init__(self, timer, state): super().__init__() self.state = state self.sample_rate = self.state.sample_rate self.timer = timer - self.waveform_queue = waveform_queue self.pulse_regions = [] self.plot_widget = pg.PlotWidget() @@ -28,10 +27,7 @@ def __init__(self, waveform_queue, timer, state): self.state.camera_settings.sig_param_changed.connect(self.update_pulses) def update_pulses(self): - pulse_times = np.arange( - self.state.volume_setting.n_skip_start, - self.state.volume_setting.n_planes - self.state.volume_setting.n_skip_end - ) / (self.state.volume_setting.frequency * self.state.volume_setting.n_planes) + pulse_times = self.state.calculate_pulse_times() for region in range(len(self.pulse_regions)): self.plot_widget.removeItem(self.pulse_regions[region]) @@ -58,8 +54,6 @@ def update_pulses(self): self.plot_widget.addItem(self.pulse_regions[i_pulse]) def update(self): - try: - current_waveform = self.waveform_queue.get(timeout=0.001) + current_waveform = self.state.get_waveform() + if current_waveform is not None: self.plot_curve.setData(np.arange(len(current_waveform)) / self.sample_rate, current_waveform) - except Empty: - pass diff --git a/lightsheet/state.py b/lightsheet/state.py index e7da5d3a..32877d2e 100644 --- a/lightsheet/state.py +++ b/lightsheet/state.py @@ -294,6 +294,7 @@ class State: def __init__(self, sample_rate): self.sample_rate = sample_rate self.calibration_ref = None + self.waveform = None self.stop_event = Event() self.experiment_start_event = Event() self.experiment_state = ExperimentPrepareState.PREVIEW @@ -410,9 +411,15 @@ def send_scan_settings(self): params = convert_volume_params( self.planar_setting, self.volume_setting, self.calibration ) + if self.waveform is not None: + pulses = self.calculate_pulse_times() * self.sample_rate + try: + pulse_log = self.waveform[pulses.astype(int)] + self.all_settings["piezo_log"] = {"trigger": pulse_log.tolist()} + except IndexError: + pass params.experiment_state = self.experiment_state - self.all_settings["scanning"] = params self.scanner.parameter_queue.put(params) @@ -501,6 +508,19 @@ def get_triggered_frame_rate(self): except Empty: return None + def get_waveform(self): + try: + self.waveform = self.scanner.waveform_queue.get(timeout=0.001) + return self.waveform + except Empty: + return None + + def calculate_pulse_times(self): + return np.arange( + self.volume_setting.n_skip_start, + self.volume_setting.n_planes - self.volume_setting.n_skip_end + ) / (self.volume_setting.frequency * self.volume_setting.n_planes) + def wrap_up(self): self.stop_event.set() self.laser.close()