11
11
from abc import abstractmethod
12
12
from typing import Callable
13
13
from psychopy import prefs
14
+ from psychopy .visual .rift import Rift
14
15
#change the pref libraty to PTB and set the latency mode to high precision
15
16
prefs .hardware ['audioLib' ] = 'PTB'
16
17
prefs .hardware ['audioLatencyMode' ] = 3
@@ -50,8 +51,11 @@ def __init__(self, exp_name, duration, eeg, save_fn, n_trials: int, iti: float,
50
51
self .soa = soa
51
52
self .jitter = jitter
52
53
self .use_vr = use_vr
54
+ if use_vr :
55
+ # VR interface accessible by specific experiment classes for customizing and using controllers.
56
+ self .rift : Rift = visual .Rift (monoscopic = True , headLocked = True )
53
57
self .use_fullscr = use_fullscr
54
- self .window_size = [1600 ,800 ]
58
+ self .window_size = [1600 ,800 ]
55
59
56
60
@abstractmethod
57
61
def load_stimulus (self ):
@@ -86,7 +90,7 @@ def setup(self, instructions=True):
86
90
87
91
# Setting up Graphics
88
92
self .window = (
89
- visual . Rift ( monoscopic = True , headLocked = True ) if self .use_vr
93
+ self . rift if self .use_vr
90
94
else visual .Window (self .window_size , monitor = "testMonitor" , units = "deg" , fullscr = self .use_fullscr ))
91
95
92
96
# Loading the stimulus from the specific experiment, throws an error if not overwritten in the specific experiment
@@ -123,15 +127,71 @@ def show_instructions(self):
123
127
# Disabling the cursor during display of instructions
124
128
self .window .mouseVisible = False
125
129
126
- # Waiting for the user to press the spacebar to start the experiment
127
- while len (event .getKeys (keyList = "space" )) == 0 :
130
+ # clear/reset any old key/controller events
131
+ self .__clear_user_input ()
132
+
133
+ # Waiting for the user to press the spacebar or controller button or trigger to start the experiment
134
+ while not self .__user_input ('start' ):
128
135
# Displaying the instructions on the screen
129
136
text = visual .TextStim (win = self .window , text = self .instruction_text , color = [- 1 , - 1 , - 1 ])
130
137
self .__draw (lambda : self .__draw_instructions (text ))
131
138
132
139
# Enabling the cursor again
133
140
self .window .mouseVisible = True
134
141
142
+ def __user_input (self , input_type ):
143
+ if input_type == 'start' :
144
+ key_input = 'spacebar'
145
+ vr_inputs = [
146
+ ('RightTouch' , 'A' , True ),
147
+ ('LeftTouch' , 'X' , True ),
148
+ ('Xbox' , 'A' , None )
149
+ ]
150
+
151
+ elif input_type == 'cancel' :
152
+ key_input = 'escape'
153
+ vr_inputs = [
154
+ ('RightTouch' , 'B' , False ),
155
+ ('LeftTouch' , 'Y' , False ),
156
+ ('Xbox' , 'B' , None )
157
+ ]
158
+
159
+ if len (event .getKeys (keyList = key_input )) > 0 :
160
+ return True
161
+
162
+ if self .use_vr :
163
+ for controller , button , trigger in vr_inputs :
164
+ if self .get_vr_input (controller , button , trigger ):
165
+ return True
166
+
167
+ return False
168
+
169
+ def get_vr_input (self , vr_controller , button = None , trigger = False ):
170
+ """
171
+ Method that returns True if the user presses the corresponding vr controller button or trigger
172
+ Args:
173
+ vr_controller: 'Xbox', 'LeftTouch' or 'RightTouch'
174
+ button: None, 'A', 'B', 'X' or 'Y'
175
+ trigger (bool): Set to True for trigger
176
+
177
+ Returns:
178
+
179
+ """
180
+ trigger_squeezed = False
181
+ if trigger :
182
+ for x in self .rift .getIndexTriggerValues (vr_controller ):
183
+ if x > 0.0 :
184
+ trigger_squeezed = True
185
+
186
+ button_pressed = False
187
+ if button is not None :
188
+ button_pressed , tsec = self .rift .getButtons ([button ], vr_controller , 'released' )
189
+
190
+ if trigger_squeezed or button_pressed :
191
+ return True
192
+
193
+ return False
194
+
135
195
def __draw_instructions (self , text ):
136
196
text .draw ()
137
197
self .window .flip ()
@@ -147,6 +207,17 @@ def __draw(self, present_stimulus: Callable):
147
207
self .window .setDefaultView ()
148
208
present_stimulus ()
149
209
210
+ def __clear_user_input (self ):
211
+ event .getKeys ()
212
+ self .clear_vr_input ()
213
+
214
+ def clear_vr_input (self ):
215
+ """
216
+ Clears/resets input events from vr controllers
217
+ """
218
+ if self .use_vr :
219
+ self .rift .updateInputState ()
220
+
150
221
def run (self , instructions = True ):
151
222
""" Do the present operation for a bunch of experiments """
152
223
@@ -171,7 +242,11 @@ def iti_with_jitter():
171
242
172
243
# Current trial being rendered
173
244
rendering_trial = - 1
174
- while len (event .getKeys ()) == 0 and (time () - start ) < self .record_duration :
245
+
246
+ # Clear/reset user input buffer
247
+ self .__clear_user_input ()
248
+
249
+ while not self .__user_input ('cancel' ) and (time () - start ) < self .record_duration :
175
250
176
251
current_experiment_seconds = time () - start
177
252
# Do not present stimulus until current trial begins(Adhere to inter-trial interval).
0 commit comments