Source code for rtcog.gui.basic_gui

import pickle
import os
import os.path as osp

from psychopy import core, event
from psychopy import logging as psychopy_logging
from psychopy.visual import TextStim
from psychopy import prefs

if os.environ.get("READTHEDOCS") != "True":
    prefs.hardware['keyboard'] = 'pygame'

from rtcog.utils.core import get_logger
from rtcog.gui.gui_utils import get_experiment_info
from rtcog.gui.base_gui import BaseGUI

log = get_logger()

[docs] class BasicGUI(BaseGUI): """ Default GUI class for real-time fMRI experiments using PsychoPy. Displays a resting instruction screen and optionally collects TR trigger timings. Parameters ---------- opts : Options Configuration options for the experiment run. clock : SharedClock, optional Clock object for precise timestamping of trigger events. """ def __init__(self, opts, *, clock=None, **kwargs): self.exp_info = get_experiment_info(opts) self.out_dir = opts.out_dir self.out_prefix = opts.out_prefix if self.exp_info['fullScreen'] == 'Yes': self.fscreen = True else: self.fscreen = False if self.exp_info['screen'] == 'Laptop': self.screen = 0 if self.exp_info['screen'] == 'External': self.screen = 1 self.ewin = self._create_experiment_window() # Default Screen self.default_inst = [ TextStim(win=self.ewin, text='Fixate on crosshair', pos=(0.0,0.42)), TextStim(win=self.ewin, text='Let your mind wander freely', pos=(0.0,0.3)), TextStim(win=self.ewin, text='Do not sleep', pos=(0.0,-0.3)), TextStim(win=self.ewin, text='X', pos=(0,0)) ] self.test_latency = opts.test_latency if self.test_latency: self.ewin.winHandle.activate() self.clock = clock self.trigger_path = osp.join(opts.out_dir, f'{opts.out_prefix}_trigger_timing.pkl') self.triggers = []
[docs] def draw_resting_screen(self): """ Display the default resting instruction screen. """ self._draw_stims(self.default_inst)
[docs] def poll_trigger(self): """ Listen for trigger keys ('t') and escape key. Records timestamps for triggers and allows early termination via `esc`. """ keys = event.getKeys([self.exp_info["triggerKey"], 'escape']) now = self.clock.now() for key in keys: if key == 't': self.triggers.append((now)) print(f"Trig @ {now:.3f} - Time point [t={len(self.triggers)}]", flush=True) elif key == 'escape': self.save_trigger() self.close_psychopy_window()
[docs] def save_trigger(self): """ Save collected trigger timestamps to disk. """ with open(self.trigger_path, 'wb') as f: pickle.dump(self.triggers, f) print(f'Timing saved to {self.trigger_path}')
[docs] def close_psychopy_window(self): """ Close the PsychoPy window. """ log.debug('Closing psychopy window...') try: self.ewin.close() except Exception as e: log.error(f'Error closing PsychoPy window: {e}')