Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow user to start and escape experiments with keyboard and vr controllers #288

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 80 additions & 5 deletions eegnb/experiments/Experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from abc import abstractmethod
from typing import Callable
from psychopy import prefs
from psychopy.visual.rift import Rift
#change the pref libraty to PTB and set the latency mode to high precision
prefs.hardware['audioLib'] = 'PTB'
prefs.hardware['audioLatencyMode'] = 3
Expand Down Expand Up @@ -50,8 +51,11 @@ def __init__(self, exp_name, duration, eeg, save_fn, n_trials: int, iti: float,
self.soa = soa
self.jitter = jitter
self.use_vr = use_vr
if use_vr:
# VR interface accessible by specific experiment classes for customizing and using controllers.
self.rift: Rift = visual.Rift(monoscopic=True, headLocked=True)
self.use_fullscr = use_fullscr
self.window_size = [1600,800]
self.window_size = [1600,800]

@abstractmethod
def load_stimulus(self):
Expand Down Expand Up @@ -86,7 +90,7 @@ def setup(self, instructions=True):

# Setting up Graphics
self.window = (
visual.Rift(monoscopic=True, headLocked=True) if self.use_vr
self.rift if self.use_vr
else visual.Window(self.window_size, monitor="testMonitor", units="deg", fullscr=self.use_fullscr))

# Loading the stimulus from the specific experiment, throws an error if not overwritten in the specific experiment
Expand Down Expand Up @@ -123,15 +127,71 @@ def show_instructions(self):
# Disabling the cursor during display of instructions
self.window.mouseVisible = False

# Waiting for the user to press the spacebar to start the experiment
while len(event.getKeys(keyList="space")) == 0:
# clear/reset any old key/controller events
self.__clear_user_input()

# Waiting for the user to press the spacebar or controller button or trigger to start the experiment
while not self.__user_input('start'):
# Displaying the instructions on the screen
text = visual.TextStim(win=self.window, text=self.instruction_text, color=[-1, -1, -1])
self.__draw(lambda: self.__draw_instructions(text))

# Enabling the cursor again
self.window.mouseVisible = True

def __user_input(self, input_type):
if input_type == 'start':
key_input = 'spacebar'
vr_inputs = [
('RightTouch', 'A', True),
('LeftTouch', 'X', True),
('Xbox', 'A', None)
]

elif input_type == 'cancel':
key_input = 'escape'
vr_inputs = [
('RightTouch', 'B', False),
('LeftTouch', 'Y', False),
('Xbox', 'B', None)
]

if len(event.getKeys(keyList=key_input)) > 0:
return True

if self.use_vr:
for controller, button, trigger in vr_inputs:
if self.get_vr_input(controller, button, trigger):
return True

return False

def get_vr_input(self, vr_controller, button=None, trigger=False):
"""
Method that returns True if the user presses the corresponding vr controller button or trigger
Args:
vr_controller: 'Xbox', 'LeftTouch' or 'RightTouch'
button: None, 'A', 'B', 'X' or 'Y'
trigger (bool): Set to True for trigger

Returns:

"""
trigger_squeezed = False
if trigger:
for x in self.rift.getIndexTriggerValues(vr_controller):
if x > 0.0:
trigger_squeezed = True

button_pressed = False
if button is not None:
button_pressed, tsec = self.rift.getButtons([button], vr_controller, 'released')

if trigger_squeezed or button_pressed:
return True

return False

def __draw_instructions(self, text):
text.draw()
self.window.flip()
Expand All @@ -147,6 +207,17 @@ def __draw(self, present_stimulus: Callable):
self.window.setDefaultView()
present_stimulus()

def __clear_user_input(self):
event.getKeys()
self.clear_vr_input()

def clear_vr_input(self):
"""
Clears/resets input events from vr controllers
"""
if self.use_vr:
self.rift.updateInputState()

def run(self, instructions=True):
""" Do the present operation for a bunch of experiments """

Expand All @@ -171,7 +242,11 @@ def iti_with_jitter():

# Current trial being rendered
rendering_trial = -1
while len(event.getKeys()) == 0 and (time() - start) < self.record_duration:

# Clear/reset user input buffer
self.__clear_user_input()

while not self.__user_input('cancel') and (time() - start) < self.record_duration:

current_experiment_seconds = time() - start
# Do not present stimulus until current trial begins(Adhere to inter-trial interval).
Expand Down
Loading