diff --git a/src/foraging_gui/Foraging.py b/src/foraging_gui/Foraging.py index e30abd628..eb3cb73b6 100644 --- a/src/foraging_gui/Foraging.py +++ b/src/foraging_gui/Foraging.py @@ -45,7 +45,7 @@ from foraging_gui.Dialogs import LaserCalibrationDialog from foraging_gui.Dialogs import LickStaDialog,TimeDistributionDialog from foraging_gui.Dialogs import AutoTrainDialog, MouseSelectorDialog -from foraging_gui.MyFunctions import GenerateTrials, Worker,TimerWorker, NewScaleSerialY, EphysRecording +from foraging_gui.MyFunctions import GenerateTrials, Worker,TimerWorker, TimerWorker2, NewScaleSerialY, EphysRecording from foraging_gui.stage import Stage from foraging_gui.bias_indicator import BiasIndicator from foraging_gui.warning_widget import WarningWidget @@ -72,6 +72,7 @@ def default(self, obj): class Window(QMainWindow): Time = QtCore.pyqtSignal(int) # Photometry timer signal + Time_ephys = QtCore.pyqtSignal(int) # Ephys timer signal sessionGenerated = QtCore.pyqtSignal(Session) # signal to indicate Session has been generated def __init__(self, parent=None,box_number=1,start_bonsai_ide=True): @@ -223,7 +224,7 @@ def __init__(self, parent=None,box_number=1,start_bonsai_ide=True): self._StopPhotometry() # Make sure photoexcitation is stopped # Initialize open ephys saving dictionary self.open_ephys=[] - + self._disable_ephys_duration() # Disable ephys duration if not surface finding # load the rig metadata self._load_rig_metadata() @@ -357,6 +358,7 @@ def connectSignalsSlots(self): self.AutoWaterType.currentIndexChanged.connect(self._keyPressEvent) self.UncoupledReward.textChanged.connect(self._ShowRewardPairs) self.UncoupledReward.returnPressed.connect(self._ShowRewardPairs) + self.OpenEphysRecordingType.currentIndexChanged.connect(self._disable_ephys_duration) # Connect to ID change in the mainwindow self.ID.returnPressed.connect( lambda: self.AutoTrain_dialog.update_auto_train_lock(engaged=False) @@ -450,6 +452,55 @@ def _set_reference(self): if current_positions is not None: self.Metadata_dialog._set_reference(current_positions) + def _check_ephys_duration(self): + ''' + Check the duration of the ephys recording + ''' + if self.StartEphysRecording.isChecked() and self.OpenEphysRecordingType.currentText()=='Surface finding': + # record in a specific duration for the surface finding + # If we already created a workertimer and thread we can reuse them + recording_duration = int(np.floor(float(self.recording_duration.text()) * 60)) + self.workertimer_ephys = TimerWorker2(recording_duration) + self.workertimer_ephys.signals.finished.connect(self._thread_complete_ephys_timer) + self.workertimer_ephys.signals.progress.connect(self._update_ephys_timer) + self.threadpool_workertimer.start(self.workertimer_ephys) + + def _update_ephys_timer(self, time): + ''' + Updates ephys baseline timer + ''' + minutes = int(np.floor(time/60)) + seconds = np.remainder(time,60) + self.ephys_timer_label.setText('Running ephys: {}:{}'.format(minutes,seconds)) + + def _thread_complete_ephys_timer(self): + if self.StartEphysRecording.isChecked(): + reply = QMessageBox.question(self, '', + 'The ephys recording has reached the duration. Do you want to stop the recording?', + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.Yes: + self.StartEphysRecording.setChecked(False) + self._StartEphysRecording() + else: + # Continue the process without blocking further actions + pass + + def _disable_ephys_duration(self): + ''' + Disable the ephys duration if the recording type is not surface finding + ''' + if self.OpenEphysRecordingType.currentText()=='Surface finding': + self.recording_duration.setEnabled(True) + else: + self.recording_duration.setEnabled(False) + + def _stop_ephys_worker(self): + """Stops the ephys worker.""" + if self.workertimer_ephys: + self.workertimer_ephys.stop() + self.workertimer_ephys = None + self.ephys_timer_label.setText('') + def _StartEphysRecording(self): ''' Start/stop ephys recording @@ -481,6 +532,7 @@ def _StartEphysRecording(self): EphysControl.start_open_ephys_recording() self.openephys_start_recording_time = str(datetime.now()) QMessageBox.warning(self, '', f'Open Ephys has started recording!\n Recording type: {self.OpenEphysRecordingType.currentText()}') + self._check_ephys_duration() except Exception as e: logging.error(traceback.format_exc()) self.StartEphysRecording.setChecked(False) @@ -511,6 +563,7 @@ def _StartEphysRecording(self): self.unsaved_data=True self.Save.setStyleSheet("color: white;background-color : mediumorchid;") EphysControl.stop_open_ephys_recording() + self._stop_ephys_worker() QMessageBox.warning(self, '', 'Open Ephys has stopped recording! Please save the data again!') except Exception as e: logging.error(traceback.format_exc()) diff --git a/src/foraging_gui/ForagingGUI.ui b/src/foraging_gui/ForagingGUI.ui index ccf1dc1bb..2d16fec3c 100644 --- a/src/foraging_gui/ForagingGUI.ui +++ b/src/foraging_gui/ForagingGUI.ui @@ -79,7 +79,7 @@ 0 0 894 - 236 + 226 @@ -674,7 +674,7 @@ - 0 + 3 @@ -691,8 +691,8 @@ 0 0 - 627 - 325 + 624 + 334 @@ -1194,8 +1194,7 @@ - - + @@ -1534,8 +1533,7 @@ - - + @@ -1628,8 +1626,8 @@ 0 0 - 610 - 339 + 655 + 365 @@ -1731,7 +1729,7 @@ 0.030000000000000 - + true @@ -1759,8 +1757,8 @@ 0.030000000000000 - - true + + true @@ -1813,7 +1811,7 @@ 3.000000000000000 - + true @@ -1841,9 +1839,9 @@ 3.000000000000000 - - true - + + true + @@ -2272,8 +2270,8 @@ Bias: 0 0 - 330 - 209 + 349 + 189 @@ -2344,8 +2342,8 @@ Double dipping: 0 0 - 1072 - 463 + 1540 + 504 @@ -4445,10 +4443,10 @@ Current pair: - 0 + -15 0 - 627 - 325 + 747 + 286 @@ -4485,8 +4483,8 @@ Current pair: - - + + 0 @@ -4494,31 +4492,18 @@ Current pair: - Start excitation - - - true - - - - - - - - 0 - 0 - + FIP mode= - - Start bleaching + + Qt::AutoText - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + 0 @@ -4526,7 +4511,7 @@ Current pair: - FIP mode= + optogenetics = Qt::AutoText @@ -4559,8 +4544,8 @@ Current pair: - - + + 0 @@ -4568,13 +4553,45 @@ Current pair: - optogenetics = + Start excitation - - Qt::AutoText + + true - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0 + 0 + + + + Start bleaching + + + true + + + + + + + + 0 + 0 + + + + + 50 + 16777215 + + + + 10 @@ -4616,25 +4633,6 @@ Current pair: - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - 10 - - - @@ -4709,25 +4707,6 @@ Current pair: - - - - - 0 - 0 - - - - recording type= - - - Qt::AutoText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -4766,6 +4745,76 @@ Current pair: + + + + + 0 + 0 + + + + recording type= + + + Qt::AutoText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 1 + + + + + + + + 0 + 0 + + + + recording duration (m)= + + + Qt::AutoText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + Qt::AutoText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -4805,7 +4854,7 @@ Current pair: 0 0 960 - 21 + 31 @@ -5285,4 +5334,4 @@ Current pair: - \ No newline at end of file + diff --git a/src/foraging_gui/ForagingGUI_Ephys.ui b/src/foraging_gui/ForagingGUI_Ephys.ui index ce38a260a..036ebc515 100644 --- a/src/foraging_gui/ForagingGUI_Ephys.ui +++ b/src/foraging_gui/ForagingGUI_Ephys.ui @@ -1463,9 +1463,9 @@ 0.030000000000000 - - true - + + true + @@ -1491,9 +1491,9 @@ 0.030000000000000 - - true - + + true + @@ -1884,9 +1884,9 @@ 3.000000000000000 - - true - + + true + @@ -1912,9 +1912,9 @@ 3.000000000000000 - - true - + + true + @@ -2176,7 +2176,7 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 863 @@ -4525,6 +4525,75 @@ + + + + 630 + 860 + 121 + 20 + + + + + 0 + 0 + + + + recording duration (m)= + + + Qt::AutoText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 750 + 860 + 80 + 20 + + + + + 0 + 0 + + + + 1 + + + + + + 840 + 860 + 121 + 20 + + + + + 0 + 0 + + + + + + + Qt::AutoText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + TrainingParameters StartFIP FIPMode @@ -4562,6 +4631,9 @@ DiskSpaceProgreeBar diskspace motor_stage_widget + label_66 + recording_duration + ephys_timer_label diff --git a/src/foraging_gui/MyFunctions.py b/src/foraging_gui/MyFunctions.py index ce98b746c..3cd39ba53 100644 --- a/src/foraging_gui/MyFunctions.py +++ b/src/foraging_gui/MyFunctions.py @@ -13,6 +13,7 @@ from serial.tools.list_ports import comports as list_comports from PyQt5 import QtWidgets from PyQt5 import QtCore +from PyQt5.QtCore import QThread from foraging_gui.reward_schedules.uncoupled_block import UncoupledBlocks @@ -2051,6 +2052,30 @@ def run(self): finally: self.signals.finished.emit() # Done +class TimerWorker2(QtCore.QRunnable): + """ + Worker thread that performs the timer counting logic. + """ + def __init__(self, counter): + super().__init__() + self.counter = counter + self.signals = WorkerSignals() + self.is_running = True # Control flag for stopping the worker + + def stop(self): + """Stops the worker.""" + self.is_running = False + + def run(self): + """ + Executes the worker thread logic. + """ + for time in range(self.counter, 0, -1): + if not self.is_running: # Check if the worker should stop + break + self.signals.progress.emit(time) + QThread.msleep(1000) # 1 second interval + self.signals.finished.emit() class TimerWorker(QtCore.QObject):