From 098d68260216a60561c5efdbfb0caa486a7ed451 Mon Sep 17 00:00:00 2001 From: Austin Hurst Date: Tue, 3 Feb 2026 18:20:42 -0400 Subject: [PATCH] Fix occasional evm eyelink issues & clean up __trial__ --- docs/CHANGELOG.rst | 7 ++++++- klibs/KLExperiment.py | 45 ++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.rst b/docs/CHANGELOG.rst index 9bbf4ee..e394e1b 100644 --- a/docs/CHANGELOG.rst +++ b/docs/CHANGELOG.rst @@ -41,7 +41,12 @@ Fixed Bugs: * KLibs no longer crashes on launch with Python 3.12. * KLibs no longer briefly shows a blank screen when a trial is recycled. - +* When eye tracking, KLibs now starts the trial clock (`self.evm`) *after* + setting up the eye tracker to record. Previously tracker setup was run after + the clock was started, meaning that if the tracker took unexpectedly long to + initialize the trial could (on rare occasions) start with anywhere between 50 + and 1000 ms already elapsed on the clock, making stimuli appear sooner than + expected. 0.7.7b1 ------- diff --git a/klibs/KLExperiment.py b/klibs/KLExperiment.py index 6b95039..43fb673 100755 --- a/klibs/KLExperiment.py +++ b/klibs/KLExperiment.py @@ -96,7 +96,7 @@ def __trial__(self, trial): """ Private method; manages a trial. """ - from klibs.KLEventQueue import pump + from klibs.KLEventQueue import flush from klibs.KLUserInterface import show_cursor, hide_cursor # At start of every trial, before setup_response_collector or trial_prep are run, retrieve @@ -105,34 +105,39 @@ def __trial__(self, trial): for iv, value in trial.items(): setattr(self, iv, value) - pump() + # Run trial prep methods self.setup_response_collector() self.trial_prep() - tx = None + flush() + + # Get everything ready to start the trial + trylink = P.eye_tracking and not P.eye_tracker_available + if P.development_mode and (P.dm_trial_show_mouse or trylink): + show_cursor() + if P.eye_tracking and not P.manual_eyelink_recording: + self.el.start(P.trial_number) + self.evm.start_clock() + + # Actually run the trial and log the data to the database + recycle = None try: - if P.development_mode and (P.dm_trial_show_mouse or (P.eye_tracking and not P.eye_tracker_available)): - show_cursor() - self.evm.start_clock() - if P.eye_tracking and not P.manual_eyelink_recording: - self.el.start(P.trial_number) P.in_trial = True self.__log_trial__(self.trial()) P.in_trial = False - if P.eye_tracking and not P.manual_eyelink_recording: - self.el.stop() - if P.development_mode and (P.dm_trial_show_mouse or (P.eye_tracking and not P.eye_tracker_available)): - hide_cursor() - self.evm.stop_clock() - self.trial_clean_up() except TrialException as e: - self.trial_clean_up() - self.evm.stop_clock() - tx = e + recycle = e + + # Clean up after the trial + self.evm.stop_clock() if P.eye_tracking and not P.manual_eyelink_recording: - # todo: add a warning, here, if the recording hasn't been stopped when under manual control self.el.stop() - if tx: - raise tx + if P.development_mode and (P.dm_trial_show_mouse or trylink): + hide_cursor() + self.trial_clean_up() + + # Recycle trial if TrialException encountered + if recycle: + raise recycle def __log_trial__(self, trial_data):