general_grasp_training.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. This experiment was created using PsychoPy3 Experiment Builder (v2023.2.3),
  5. on Thu Nov 16 17:00:04 2023
  6. If you publish work using this script the most relevant publication is:
  7. Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, Lindeløv JK. (2019)
  8. PsychoPy2: Experiments in behavior made easy Behav Res 51: 195.
  9. https://doi.org/10.3758/s13428-018-01193-y
  10. """
  11. # --- Import packages ---
  12. from psychopy import locale_setup
  13. from psychopy import prefs
  14. from psychopy import plugins
  15. plugins.activatePlugins()
  16. prefs.hardware['audioLib'] = 'ptb'
  17. prefs.hardware['audioLatencyMode'] = '3'
  18. from psychopy import sound, gui, visual, core, data, event, logging, clock, colors, layout
  19. from psychopy.tools import environmenttools
  20. from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED,
  21. STOPPED, FINISHED, PRESSED, RELEASED, FOREVER, priority)
  22. import numpy as np # whole numpy lib is available, prepend 'np.'
  23. from numpy import (sin, cos, tan, log, log10, pi, average,
  24. sqrt, std, deg2rad, rad2deg, linspace, asarray)
  25. from numpy.random import random, randint, normal, shuffle, choice as randchoice
  26. import os # handy system and path functions
  27. import sys # to get file system encoding
  28. from psychopy.hardware import keyboard
  29. # Run 'Before Experiment' code from config
  30. import os
  31. import datetime
  32. from time import sleep
  33. import argparse
  34. from device.data_client import NeuracleDataClient
  35. from device.trigger_box import TriggerNeuracle
  36. from device.fubo_pneumatic_finger import FuboPneumaticFingerClient
  37. from settings.config import settings
  38. from bci_core.online import Controller
  39. from settings.config import settings
  40. config_info = settings.CONFIG_INFO
  41. # get train params
  42. def parse_args():
  43. parser = argparse.ArgumentParser(
  44. description='Hand gesture train'
  45. )
  46. parser.add_argument(
  47. '--subj',
  48. dest='subj',
  49. help='Subject name',
  50. default=None,
  51. type=str
  52. )
  53. parser.add_argument(
  54. '--n-trials',
  55. dest='n_trials',
  56. help='Trial number',
  57. type=int,
  58. )
  59. parser.add_argument(
  60. '--com',
  61. dest='com',
  62. help='Peripheral serial port',
  63. type=str
  64. )
  65. parser.add_argument(
  66. '--finger-model',
  67. '-fm',
  68. dest='finger_model',
  69. help='Gesture to train',
  70. type=str
  71. )
  72. parser.add_argument(
  73. '--virtual-feedback-rate',
  74. '-vfr',
  75. dest='virtual_feedback_rate',
  76. help='Virtual feedback rate',
  77. type=float
  78. )
  79. parser.add_argument(
  80. '--model-path',
  81. dest='model_path',
  82. help='Path to model file',
  83. default=None,
  84. type=str
  85. )
  86. return parser.parse_args()
  87. args = parse_args()
  88. # connect neo
  89. receiver = NeuracleDataClient(n_channel=len(config_info['channel_labels']),
  90. samplerate=config_info['sample_rate'],
  91. host=config_info['host'],
  92. port=config_info['port'])
  93. # connect to trigger box
  94. trigger = TriggerNeuracle()
  95. # connect to mechanical hand
  96. hand_device = FuboPneumaticFingerClient({'port': args.com})
  97. # build bci controller
  98. controller = Controller(args.virtual_feedback_rate, args.model_path, state_change_threshold=0.8)
  99. # Run 'Before Experiment' code from decision
  100. # --- Setup global variables (available in all functions) ---
  101. # Ensure that relative paths start from the same directory as this script
  102. _thisDir = os.path.dirname(os.path.abspath(__file__))
  103. # Store info about the experiment session
  104. psychopyVersion = '2023.2.3'
  105. expName = 'train' # from the Builder filename that created this script
  106. expInfo = {
  107. 'participant': f"{randint(0, 999999):06.0f}",
  108. 'session': '001',
  109. 'date': data.getDateStr(), # add a simple timestamp
  110. 'expName': expName,
  111. 'psychopyVersion': psychopyVersion,
  112. }
  113. def showExpInfoDlg(expInfo):
  114. """
  115. Show participant info dialog.
  116. Parameters
  117. ==========
  118. expInfo : dict
  119. Information about this experiment, created by the `setupExpInfo` function.
  120. Returns
  121. ==========
  122. dict
  123. Information about this experiment.
  124. """
  125. # temporarily remove keys which the dialog doesn't need to show
  126. poppedKeys = {
  127. 'date': expInfo.pop('date', data.getDateStr()),
  128. 'expName': expInfo.pop('expName', expName),
  129. 'psychopyVersion': expInfo.pop('psychopyVersion', psychopyVersion),
  130. }
  131. # show participant info dialog
  132. dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName)
  133. if dlg.OK == False:
  134. core.quit() # user pressed cancel
  135. # restore hidden keys
  136. expInfo.update(poppedKeys)
  137. # return expInfo
  138. return expInfo
  139. def setupData(expInfo, dataDir=None):
  140. """
  141. Make an ExperimentHandler to handle trials and saving.
  142. Parameters
  143. ==========
  144. expInfo : dict
  145. Information about this experiment, created by the `setupExpInfo` function.
  146. dataDir : Path, str or None
  147. Folder to save the data to, leave as None to create a folder in the current directory.
  148. Returns
  149. ==========
  150. psychopy.data.ExperimentHandler
  151. Handler object for this experiment, contains the data to save and information about
  152. where to save it to.
  153. """
  154. # data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
  155. if dataDir is None:
  156. dataDir = _thisDir
  157. filename = u'data/%s_%s_%s' % (expInfo['participant'], expName, expInfo['date'])
  158. # make sure filename is relative to dataDir
  159. if os.path.isabs(filename):
  160. dataDir = os.path.commonprefix([dataDir, filename])
  161. filename = os.path.relpath(filename, dataDir)
  162. # an ExperimentHandler isn't essential but helps with data saving
  163. thisExp = data.ExperimentHandler(
  164. name=expName, version='',
  165. extraInfo=expInfo, runtimeInfo=None,
  166. originPath='/Users/dingkunliu/Projects/MI-BCI-Proj/kraken/backend/general_grasp_training.py',
  167. savePickle=True, saveWideText=True,
  168. dataFileName=dataDir + os.sep + filename, sortColumns='time'
  169. )
  170. thisExp.setPriority('thisRow.t', priority.CRITICAL)
  171. thisExp.setPriority('expName', priority.LOW)
  172. # return experiment handler
  173. return thisExp
  174. def setupLogging(filename):
  175. """
  176. Setup a log file and tell it what level to log at.
  177. Parameters
  178. ==========
  179. filename : str or pathlib.Path
  180. Filename to save log file and data files as, doesn't need an extension.
  181. Returns
  182. ==========
  183. psychopy.logging.LogFile
  184. Text stream to receive inputs from the logging system.
  185. """
  186. # this outputs to the screen, not a file
  187. logging.console.setLevel(logging.EXP)
  188. # save a log file for detail verbose info
  189. logFile = logging.LogFile(filename+'.log', level=logging.EXP)
  190. return logFile
  191. def setupWindow(expInfo=None, win=None):
  192. """
  193. Setup the Window
  194. Parameters
  195. ==========
  196. expInfo : dict
  197. Information about this experiment, created by the `setupExpInfo` function.
  198. win : psychopy.visual.Window
  199. Window to setup - leave as None to create a new window.
  200. Returns
  201. ==========
  202. psychopy.visual.Window
  203. Window in which to run this experiment.
  204. """
  205. if win is None:
  206. # if not given a window to setup, make one
  207. win = visual.Window(
  208. size=[1493, 933], fullscr=True, screen=0,
  209. winType='pyglet', allowStencil=False,
  210. monitor='testMonitor', color=[1,1,1], colorSpace='rgb',
  211. backgroundImage='', backgroundFit='none',
  212. blendMode='avg', useFBO=True,
  213. units='height'
  214. )
  215. if expInfo is not None:
  216. # store frame rate of monitor if we can measure it
  217. expInfo['frameRate'] = win.getActualFrameRate()
  218. else:
  219. # if we have a window, just set the attributes which are safe to set
  220. win.color = [1,1,1]
  221. win.colorSpace = 'rgb'
  222. win.backgroundImage = ''
  223. win.backgroundFit = 'none'
  224. win.units = 'height'
  225. win.mouseVisible = False
  226. win.hideMessage()
  227. return win
  228. def setupInputs(expInfo, thisExp, win):
  229. """
  230. Setup whatever inputs are available (mouse, keyboard, eyetracker, etc.)
  231. Parameters
  232. ==========
  233. expInfo : dict
  234. Information about this experiment, created by the `setupExpInfo` function.
  235. thisExp : psychopy.data.ExperimentHandler
  236. Handler object for this experiment, contains the data to save and information about
  237. where to save it to.
  238. win : psychopy.visual.Window
  239. Window in which to run this experiment.
  240. Returns
  241. ==========
  242. dict
  243. Dictionary of input devices by name.
  244. """
  245. # --- Setup input devices ---
  246. inputs = {}
  247. ioConfig = {}
  248. ioSession = ioServer = eyetracker = None
  249. # create a default keyboard (e.g. to check for escape)
  250. defaultKeyboard = keyboard.Keyboard(backend='ptb')
  251. # return inputs dict
  252. return {
  253. 'ioServer': ioServer,
  254. 'defaultKeyboard': defaultKeyboard,
  255. 'eyetracker': eyetracker,
  256. }
  257. def pauseExperiment(thisExp, inputs=None, win=None, timers=[], playbackComponents=[]):
  258. """
  259. Pause this experiment, preventing the flow from advancing to the next routine until resumed.
  260. Parameters
  261. ==========
  262. thisExp : psychopy.data.ExperimentHandler
  263. Handler object for this experiment, contains the data to save and information about
  264. where to save it to.
  265. inputs : dict
  266. Dictionary of input devices by name.
  267. win : psychopy.visual.Window
  268. Window for this experiment.
  269. timers : list, tuple
  270. List of timers to reset once pausing is finished.
  271. playbackComponents : list, tuple
  272. List of any components with a `pause` method which need to be paused.
  273. """
  274. # if we are not paused, do nothing
  275. if thisExp.status != PAUSED:
  276. return
  277. # pause any playback components
  278. for comp in playbackComponents:
  279. comp.pause()
  280. # prevent components from auto-drawing
  281. win.stashAutoDraw()
  282. # run a while loop while we wait to unpause
  283. while thisExp.status == PAUSED:
  284. # make sure we have a keyboard
  285. if inputs is None:
  286. inputs = {
  287. 'defaultKeyboard': keyboard.Keyboard(backend='PsychToolbox')
  288. }
  289. # check for quit (typically the Esc key)
  290. if inputs['defaultKeyboard'].getKeys(keyList=['escape']):
  291. endExperiment(thisExp, win=win, inputs=inputs)
  292. # flip the screen
  293. win.flip()
  294. # if stop was requested while paused, quit
  295. if thisExp.status == FINISHED:
  296. endExperiment(thisExp, inputs=inputs, win=win)
  297. # resume any playback components
  298. for comp in playbackComponents:
  299. comp.play()
  300. # restore auto-drawn components
  301. win.retrieveAutoDraw()
  302. # reset any timers
  303. for timer in timers:
  304. timer.reset()
  305. def run(expInfo, thisExp, win, inputs, globalClock=None, thisSession=None):
  306. """
  307. Run the experiment flow.
  308. Parameters
  309. ==========
  310. expInfo : dict
  311. Information about this experiment, created by the `setupExpInfo` function.
  312. thisExp : psychopy.data.ExperimentHandler
  313. Handler object for this experiment, contains the data to save and information about
  314. where to save it to.
  315. psychopy.visual.Window
  316. Window in which to run this experiment.
  317. inputs : dict
  318. Dictionary of input devices by name.
  319. globalClock : psychopy.core.clock.Clock or None
  320. Clock to get global time from - supply None to make a new one.
  321. thisSession : psychopy.session.Session or None
  322. Handle of the Session object this experiment is being run from, if any.
  323. """
  324. # mark experiment as started
  325. thisExp.status = STARTED
  326. # make sure variables created by exec are available globally
  327. exec = environmenttools.setExecEnvironment(globals())
  328. # get device handles from dict of input devices
  329. ioServer = inputs['ioServer']
  330. defaultKeyboard = inputs['defaultKeyboard']
  331. eyetracker = inputs['eyetracker']
  332. # make sure we're running in the directory for this experiment
  333. os.chdir(_thisDir)
  334. # get filename from ExperimentHandler for convenience
  335. filename = thisExp.dataFileName
  336. frameTolerance = 0.001 # how close to onset before 'same' frame
  337. endExpNow = False # flag for 'escape' or other condition => quit the exp
  338. # get frame duration from frame rate in expInfo
  339. if 'frameRate' in expInfo and expInfo['frameRate'] is not None:
  340. frameDur = 1.0 / round(expInfo['frameRate'])
  341. else:
  342. frameDur = 1.0 / 60.0 # could not measure, so guess
  343. # Start Code - component code to be run after the window creation
  344. # --- Initialize components for Routine "before_mi" ---
  345. train_position = visual.TextStim(win=win, name='train_position',
  346. text='训练部位:右手',
  347. font='Open Sans',
  348. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  349. color='black', colorSpace='rgb', opacity=None,
  350. languageStyle='LTR',
  351. depth=0.0);
  352. instruction = visual.TextStim(win=win, name='instruction',
  353. text='准备进行一般抓握训练,\n按空格键继续',
  354. font='Open Sans',
  355. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  356. color='black', colorSpace='rgb', opacity=None,
  357. languageStyle='LTR',
  358. depth=-1.0);
  359. key_resp = keyboard.Keyboard()
  360. # --- Initialize components for Routine "mi_prepare" ---
  361. text = visual.TextStim(win=win, name='text',
  362. text='请准备',
  363. font='Open Sans',
  364. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  365. color='black', colorSpace='rgb', opacity=None,
  366. languageStyle='LTR',
  367. depth=-1.0);
  368. # --- Initialize components for Routine "mi_begin" ---
  369. img_right = visual.ImageStim(
  370. win=win,
  371. name='img_right',
  372. image='static/images/hand_move.jpg', mask=None, anchor='center',
  373. ori=0.0, pos=(0, 0), size=None,
  374. color=[1,1,1], colorSpace='rgb', opacity=None,
  375. flipHoriz=False, flipVert=False,
  376. texRes=128.0, interpolate=True, depth=0.0)
  377. # --- Initialize components for Routine "decision" ---
  378. # --- Initialize components for Routine "mi_feedback" ---
  379. feedback = visual.TextStim(win=win, name='feedback',
  380. text=None,
  381. font='Open Sans',
  382. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  383. color='black', colorSpace='rgb', opacity=None,
  384. languageStyle='LTR',
  385. depth=0.0);
  386. # --- Initialize components for Routine "mi_feedback_2" ---
  387. feedback_2 = visual.TextStim(win=win, name='feedback_2',
  388. text=None,
  389. font='Open Sans',
  390. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  391. color='white', colorSpace='rgb', opacity=None,
  392. languageStyle='LTR',
  393. depth=0.0);
  394. # --- Initialize components for Routine "mi_rest" ---
  395. img_rest = visual.ImageStim(
  396. win=win,
  397. name='img_rest',
  398. image='static/images/rest.jpg', mask=None, anchor='center',
  399. ori=0.0, pos=(0, 0), size=None,
  400. color=[1,1,1], colorSpace='rgb', opacity=None,
  401. flipHoriz=False, flipVert=False,
  402. texRes=128.0, interpolate=True, depth=0.0)
  403. # --- Initialize components for Routine "end" ---
  404. mi_end = visual.TextStim(win=win, name='mi_end',
  405. text='结束实验',
  406. font='Open Sans',
  407. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  408. color='black', colorSpace='rgb', opacity=None,
  409. languageStyle='LTR',
  410. depth=0.0);
  411. # create some handy timers
  412. if globalClock is None:
  413. globalClock = core.Clock() # to track the time since experiment started
  414. if ioServer is not None:
  415. ioServer.syncClock(globalClock)
  416. logging.setDefaultClock(globalClock)
  417. routineTimer = core.Clock() # to track time remaining of each (possibly non-slip) routine
  418. win.flip() # flip window to reset last flip timer
  419. # store the exact time the global clock started
  420. expInfo['expStart'] = data.getDateStr(format='%Y-%m-%d %Hh%M.%S.%f %z', fractionalSecondDigits=6)
  421. # --- Prepare to start Routine "before_mi" ---
  422. continueRoutine = True
  423. # update component parameters for each repeat
  424. thisExp.addData('before_mi.started', globalClock.getTime())
  425. key_resp.keys = []
  426. key_resp.rt = []
  427. _key_resp_allKeys = []
  428. # keep track of which components have finished
  429. before_miComponents = [train_position, instruction, key_resp]
  430. for thisComponent in before_miComponents:
  431. thisComponent.tStart = None
  432. thisComponent.tStop = None
  433. thisComponent.tStartRefresh = None
  434. thisComponent.tStopRefresh = None
  435. if hasattr(thisComponent, 'status'):
  436. thisComponent.status = NOT_STARTED
  437. # reset timers
  438. t = 0
  439. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  440. frameN = -1
  441. # --- Run Routine "before_mi" ---
  442. routineForceEnded = not continueRoutine
  443. while continueRoutine:
  444. # get current time
  445. t = routineTimer.getTime()
  446. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  447. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  448. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  449. # update/draw components on each frame
  450. # *train_position* updates
  451. # if train_position is starting this frame...
  452. if train_position.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  453. # keep track of start time/frame for later
  454. train_position.frameNStart = frameN # exact frame index
  455. train_position.tStart = t # local t and not account for scr refresh
  456. train_position.tStartRefresh = tThisFlipGlobal # on global time
  457. win.timeOnFlip(train_position, 'tStartRefresh') # time at next scr refresh
  458. # add timestamp to datafile
  459. thisExp.timestampOnFlip(win, 'train_position.started')
  460. # update status
  461. train_position.status = STARTED
  462. train_position.setAutoDraw(True)
  463. # if train_position is active this frame...
  464. if train_position.status == STARTED:
  465. # update params
  466. pass
  467. # if train_position is stopping this frame...
  468. if train_position.status == STARTED:
  469. # is it time to stop? (based on global clock, using actual start)
  470. if tThisFlipGlobal > train_position.tStartRefresh + 2-frameTolerance:
  471. # keep track of stop time/frame for later
  472. train_position.tStop = t # not accounting for scr refresh
  473. train_position.frameNStop = frameN # exact frame index
  474. # add timestamp to datafile
  475. thisExp.timestampOnFlip(win, 'train_position.stopped')
  476. # update status
  477. train_position.status = FINISHED
  478. train_position.setAutoDraw(False)
  479. # *instruction* updates
  480. # if instruction is starting this frame...
  481. if instruction.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  482. # keep track of start time/frame for later
  483. instruction.frameNStart = frameN # exact frame index
  484. instruction.tStart = t # local t and not account for scr refresh
  485. instruction.tStartRefresh = tThisFlipGlobal # on global time
  486. win.timeOnFlip(instruction, 'tStartRefresh') # time at next scr refresh
  487. # add timestamp to datafile
  488. thisExp.timestampOnFlip(win, 'instruction.started')
  489. # update status
  490. instruction.status = STARTED
  491. instruction.setAutoDraw(True)
  492. # if instruction is active this frame...
  493. if instruction.status == STARTED:
  494. # update params
  495. pass
  496. # *key_resp* updates
  497. waitOnFlip = False
  498. # if key_resp is starting this frame...
  499. if key_resp.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  500. # keep track of start time/frame for later
  501. key_resp.frameNStart = frameN # exact frame index
  502. key_resp.tStart = t # local t and not account for scr refresh
  503. key_resp.tStartRefresh = tThisFlipGlobal # on global time
  504. win.timeOnFlip(key_resp, 'tStartRefresh') # time at next scr refresh
  505. # add timestamp to datafile
  506. thisExp.timestampOnFlip(win, 'key_resp.started')
  507. # update status
  508. key_resp.status = STARTED
  509. # keyboard checking is just starting
  510. waitOnFlip = True
  511. win.callOnFlip(key_resp.clock.reset) # t=0 on next screen flip
  512. win.callOnFlip(key_resp.clearEvents, eventType='keyboard') # clear events on next screen flip
  513. if key_resp.status == STARTED and not waitOnFlip:
  514. theseKeys = key_resp.getKeys(keyList=['space'], ignoreKeys=["escape"], waitRelease=False)
  515. _key_resp_allKeys.extend(theseKeys)
  516. if len(_key_resp_allKeys):
  517. key_resp.keys = _key_resp_allKeys[-1].name # just the last key pressed
  518. key_resp.rt = _key_resp_allKeys[-1].rt
  519. key_resp.duration = _key_resp_allKeys[-1].duration
  520. # a response ends the routine
  521. continueRoutine = False
  522. # check for quit (typically the Esc key)
  523. if defaultKeyboard.getKeys(keyList=["escape"]):
  524. thisExp.status = FINISHED
  525. if thisExp.status == FINISHED or endExpNow:
  526. endExperiment(thisExp, inputs=inputs, win=win)
  527. return
  528. # check if all components have finished
  529. if not continueRoutine: # a component has requested a forced-end of Routine
  530. routineForceEnded = True
  531. break
  532. continueRoutine = False # will revert to True if at least one component still running
  533. for thisComponent in before_miComponents:
  534. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  535. continueRoutine = True
  536. break # at least one component has not yet finished
  537. # refresh the screen
  538. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  539. win.flip()
  540. # --- Ending Routine "before_mi" ---
  541. for thisComponent in before_miComponents:
  542. if hasattr(thisComponent, "setAutoDraw"):
  543. thisComponent.setAutoDraw(False)
  544. thisExp.addData('before_mi.stopped', globalClock.getTime())
  545. # check responses
  546. if key_resp.keys in ['', [], None]: # No response was made
  547. key_resp.keys = None
  548. thisExp.addData('key_resp.keys',key_resp.keys)
  549. if key_resp.keys != None: # we had a response
  550. thisExp.addData('key_resp.rt', key_resp.rt)
  551. thisExp.addData('key_resp.duration', key_resp.duration)
  552. thisExp.nextEntry()
  553. # the Routine "before_mi" was not non-slip safe, so reset the non-slip timer
  554. routineTimer.reset()
  555. # set up handler to look after randomisation of conditions etc
  556. trials = data.TrialHandler(nReps=args.n_trials, method='sequential',
  557. extraInfo=expInfo, originPath=-1,
  558. trialList=[None],
  559. seed=None, name='trials')
  560. thisExp.addLoop(trials) # add the loop to the experiment
  561. thisTrial = trials.trialList[0] # so we can initialise stimuli with some values
  562. # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
  563. if thisTrial != None:
  564. for paramName in thisTrial:
  565. globals()[paramName] = thisTrial[paramName]
  566. for thisTrial in trials:
  567. currentLoop = trials
  568. thisExp.timestampOnFlip(win, 'thisRow.t')
  569. # pause experiment here if requested
  570. if thisExp.status == PAUSED:
  571. pauseExperiment(
  572. thisExp=thisExp,
  573. inputs=inputs,
  574. win=win,
  575. timers=[routineTimer],
  576. playbackComponents=[]
  577. )
  578. # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
  579. if thisTrial != None:
  580. for paramName in thisTrial:
  581. globals()[paramName] = thisTrial[paramName]
  582. # --- Prepare to start Routine "mi_prepare" ---
  583. continueRoutine = True
  584. # update component parameters for each repeat
  585. thisExp.addData('mi_prepare.started', globalClock.getTime())
  586. # Run 'Begin Routine' code from initialize_buffer
  587. decision_buffer = []
  588. # keep track of which components have finished
  589. mi_prepareComponents = [text]
  590. for thisComponent in mi_prepareComponents:
  591. thisComponent.tStart = None
  592. thisComponent.tStop = None
  593. thisComponent.tStartRefresh = None
  594. thisComponent.tStopRefresh = None
  595. if hasattr(thisComponent, 'status'):
  596. thisComponent.status = NOT_STARTED
  597. # reset timers
  598. t = 0
  599. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  600. frameN = -1
  601. # --- Run Routine "mi_prepare" ---
  602. routineForceEnded = not continueRoutine
  603. while continueRoutine and routineTimer.getTime() < 1.5:
  604. # get current time
  605. t = routineTimer.getTime()
  606. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  607. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  608. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  609. # update/draw components on each frame
  610. # *text* updates
  611. # if text is starting this frame...
  612. if text.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  613. # keep track of start time/frame for later
  614. text.frameNStart = frameN # exact frame index
  615. text.tStart = t # local t and not account for scr refresh
  616. text.tStartRefresh = tThisFlipGlobal # on global time
  617. win.timeOnFlip(text, 'tStartRefresh') # time at next scr refresh
  618. # add timestamp to datafile
  619. thisExp.timestampOnFlip(win, 'text.started')
  620. # update status
  621. text.status = STARTED
  622. text.setAutoDraw(True)
  623. # if text is active this frame...
  624. if text.status == STARTED:
  625. # update params
  626. pass
  627. # if text is stopping this frame...
  628. if text.status == STARTED:
  629. # is it time to stop? (based on global clock, using actual start)
  630. if tThisFlipGlobal > text.tStartRefresh + 1.5-frameTolerance:
  631. # keep track of stop time/frame for later
  632. text.tStop = t # not accounting for scr refresh
  633. text.frameNStop = frameN # exact frame index
  634. # add timestamp to datafile
  635. thisExp.timestampOnFlip(win, 'text.stopped')
  636. # update status
  637. text.status = FINISHED
  638. text.setAutoDraw(False)
  639. # check for quit (typically the Esc key)
  640. if defaultKeyboard.getKeys(keyList=["escape"]):
  641. thisExp.status = FINISHED
  642. if thisExp.status == FINISHED or endExpNow:
  643. endExperiment(thisExp, inputs=inputs, win=win)
  644. return
  645. # check if all components have finished
  646. if not continueRoutine: # a component has requested a forced-end of Routine
  647. routineForceEnded = True
  648. break
  649. continueRoutine = False # will revert to True if at least one component still running
  650. for thisComponent in mi_prepareComponents:
  651. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  652. continueRoutine = True
  653. break # at least one component has not yet finished
  654. # refresh the screen
  655. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  656. win.flip()
  657. # --- Ending Routine "mi_prepare" ---
  658. for thisComponent in mi_prepareComponents:
  659. if hasattr(thisComponent, "setAutoDraw"):
  660. thisComponent.setAutoDraw(False)
  661. thisExp.addData('mi_prepare.stopped', globalClock.getTime())
  662. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  663. if routineForceEnded:
  664. routineTimer.reset()
  665. else:
  666. routineTimer.addTime(-1.500000)
  667. # set up handler to look after randomisation of conditions etc
  668. classification = data.TrialHandler(nReps=5.0, method='sequential',
  669. extraInfo=expInfo, originPath=-1,
  670. trialList=[None],
  671. seed=None, name='classification')
  672. thisExp.addLoop(classification) # add the loop to the experiment
  673. thisClassification = classification.trialList[0] # so we can initialise stimuli with some values
  674. # abbreviate parameter names if possible (e.g. rgb = thisClassification.rgb)
  675. if thisClassification != None:
  676. for paramName in thisClassification:
  677. globals()[paramName] = thisClassification[paramName]
  678. for thisClassification in classification:
  679. currentLoop = classification
  680. thisExp.timestampOnFlip(win, 'thisRow.t')
  681. # pause experiment here if requested
  682. if thisExp.status == PAUSED:
  683. pauseExperiment(
  684. thisExp=thisExp,
  685. inputs=inputs,
  686. win=win,
  687. timers=[routineTimer],
  688. playbackComponents=[]
  689. )
  690. # abbreviate parameter names if possible (e.g. rgb = thisClassification.rgb)
  691. if thisClassification != None:
  692. for paramName in thisClassification:
  693. globals()[paramName] = thisClassification[paramName]
  694. # --- Prepare to start Routine "mi_begin" ---
  695. continueRoutine = True
  696. # update component parameters for each repeat
  697. thisExp.addData('mi_begin.started', globalClock.getTime())
  698. # Run 'Begin Routine' code from algo
  699. # send trigger
  700. current_true_label = settings.FINGERMODEL_IDS[args.finger_model]
  701. win.callOnFlip(trigger.send_trigger, current_true_label)
  702. # keep track of which components have finished
  703. mi_beginComponents = [img_right]
  704. for thisComponent in mi_beginComponents:
  705. thisComponent.tStart = None
  706. thisComponent.tStop = None
  707. thisComponent.tStartRefresh = None
  708. thisComponent.tStopRefresh = None
  709. if hasattr(thisComponent, 'status'):
  710. thisComponent.status = NOT_STARTED
  711. # reset timers
  712. t = 0
  713. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  714. frameN = -1
  715. # --- Run Routine "mi_begin" ---
  716. routineForceEnded = not continueRoutine
  717. while continueRoutine and routineTimer.getTime() < 1.0:
  718. # get current time
  719. t = routineTimer.getTime()
  720. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  721. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  722. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  723. # update/draw components on each frame
  724. # *img_right* updates
  725. # if img_right is starting this frame...
  726. if img_right.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  727. # keep track of start time/frame for later
  728. img_right.frameNStart = frameN # exact frame index
  729. img_right.tStart = t # local t and not account for scr refresh
  730. img_right.tStartRefresh = tThisFlipGlobal # on global time
  731. win.timeOnFlip(img_right, 'tStartRefresh') # time at next scr refresh
  732. # add timestamp to datafile
  733. thisExp.timestampOnFlip(win, 'img_right.started')
  734. # update status
  735. img_right.status = STARTED
  736. img_right.setAutoDraw(True)
  737. # if img_right is active this frame...
  738. if img_right.status == STARTED:
  739. # update params
  740. pass
  741. # if img_right is stopping this frame...
  742. if img_right.status == STARTED:
  743. # is it time to stop? (based on global clock, using actual start)
  744. if tThisFlipGlobal > img_right.tStartRefresh + 1-frameTolerance:
  745. # keep track of stop time/frame for later
  746. img_right.tStop = t # not accounting for scr refresh
  747. img_right.frameNStop = frameN # exact frame index
  748. # add timestamp to datafile
  749. thisExp.timestampOnFlip(win, 'img_right.stopped')
  750. # update status
  751. img_right.status = FINISHED
  752. img_right.setAutoDraw(False)
  753. # check for quit (typically the Esc key)
  754. if defaultKeyboard.getKeys(keyList=["escape"]):
  755. thisExp.status = FINISHED
  756. if thisExp.status == FINISHED or endExpNow:
  757. endExperiment(thisExp, inputs=inputs, win=win)
  758. return
  759. # check if all components have finished
  760. if not continueRoutine: # a component has requested a forced-end of Routine
  761. routineForceEnded = True
  762. break
  763. continueRoutine = False # will revert to True if at least one component still running
  764. for thisComponent in mi_beginComponents:
  765. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  766. continueRoutine = True
  767. break # at least one component has not yet finished
  768. # refresh the screen
  769. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  770. win.flip()
  771. # --- Ending Routine "mi_begin" ---
  772. for thisComponent in mi_beginComponents:
  773. if hasattr(thisComponent, "setAutoDraw"):
  774. thisComponent.setAutoDraw(False)
  775. thisExp.addData('mi_begin.stopped', globalClock.getTime())
  776. # Run 'End Routine' code from algo
  777. data_from_buffer = receiver.get_trial_data(clear=False)
  778. decision = controller.step_decision(data_from_buffer, current_true_label)
  779. decision_buffer.append(decision)
  780. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  781. if routineForceEnded:
  782. routineTimer.reset()
  783. else:
  784. routineTimer.addTime(-1.000000)
  785. # completed 5.0 repeats of 'classification'
  786. # --- Prepare to start Routine "decision" ---
  787. continueRoutine = True
  788. # update component parameters for each repeat
  789. thisExp.addData('decision.started', globalClock.getTime())
  790. # Run 'Begin Routine' code from decision
  791. cnt = 0
  792. for d in decision_buffer:
  793. if d == current_true_label:
  794. cnt += 1
  795. if cnt >= 3:
  796. feedback_time = 10
  797. else:
  798. feedback_time = 2
  799. # keep track of which components have finished
  800. decisionComponents = []
  801. for thisComponent in decisionComponents:
  802. thisComponent.tStart = None
  803. thisComponent.tStop = None
  804. thisComponent.tStartRefresh = None
  805. thisComponent.tStopRefresh = None
  806. if hasattr(thisComponent, 'status'):
  807. thisComponent.status = NOT_STARTED
  808. # reset timers
  809. t = 0
  810. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  811. frameN = -1
  812. # --- Run Routine "decision" ---
  813. routineForceEnded = not continueRoutine
  814. while continueRoutine:
  815. # get current time
  816. t = routineTimer.getTime()
  817. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  818. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  819. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  820. # update/draw components on each frame
  821. # check for quit (typically the Esc key)
  822. if defaultKeyboard.getKeys(keyList=["escape"]):
  823. thisExp.status = FINISHED
  824. if thisExp.status == FINISHED or endExpNow:
  825. endExperiment(thisExp, inputs=inputs, win=win)
  826. return
  827. # check if all components have finished
  828. if not continueRoutine: # a component has requested a forced-end of Routine
  829. routineForceEnded = True
  830. break
  831. continueRoutine = False # will revert to True if at least one component still running
  832. for thisComponent in decisionComponents:
  833. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  834. continueRoutine = True
  835. break # at least one component has not yet finished
  836. # refresh the screen
  837. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  838. win.flip()
  839. # --- Ending Routine "decision" ---
  840. for thisComponent in decisionComponents:
  841. if hasattr(thisComponent, "setAutoDraw"):
  842. thisComponent.setAutoDraw(False)
  843. thisExp.addData('decision.stopped', globalClock.getTime())
  844. # the Routine "decision" was not non-slip safe, so reset the non-slip timer
  845. routineTimer.reset()
  846. # --- Prepare to start Routine "mi_feedback" ---
  847. continueRoutine = True
  848. # update component parameters for each repeat
  849. thisExp.addData('mi_feedback.started', globalClock.getTime())
  850. # Run 'Begin Routine' code from code
  851. if feedback_time == 10:
  852. feedback.text = '恭喜!'
  853. hand_device.start(args.finger_model)
  854. else:
  855. feedback.text = '继续努力!'
  856. # keep track of which components have finished
  857. mi_feedbackComponents = [feedback]
  858. for thisComponent in mi_feedbackComponents:
  859. thisComponent.tStart = None
  860. thisComponent.tStop = None
  861. thisComponent.tStartRefresh = None
  862. thisComponent.tStopRefresh = None
  863. if hasattr(thisComponent, 'status'):
  864. thisComponent.status = NOT_STARTED
  865. # reset timers
  866. t = 0
  867. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  868. frameN = -1
  869. # --- Run Routine "mi_feedback" ---
  870. routineForceEnded = not continueRoutine
  871. while continueRoutine:
  872. # get current time
  873. t = routineTimer.getTime()
  874. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  875. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  876. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  877. # update/draw components on each frame
  878. # *feedback* updates
  879. # if feedback is starting this frame...
  880. if feedback.status == NOT_STARTED and tThisFlip >= 0-frameTolerance:
  881. # keep track of start time/frame for later
  882. feedback.frameNStart = frameN # exact frame index
  883. feedback.tStart = t # local t and not account for scr refresh
  884. feedback.tStartRefresh = tThisFlipGlobal # on global time
  885. win.timeOnFlip(feedback, 'tStartRefresh') # time at next scr refresh
  886. # add timestamp to datafile
  887. thisExp.timestampOnFlip(win, 'feedback.started')
  888. # update status
  889. feedback.status = STARTED
  890. feedback.setAutoDraw(True)
  891. # if feedback is active this frame...
  892. if feedback.status == STARTED:
  893. # update params
  894. pass
  895. # if feedback is stopping this frame...
  896. if feedback.status == STARTED:
  897. # is it time to stop? (based on global clock, using actual start)
  898. if tThisFlipGlobal > feedback.tStartRefresh + feedback_time / 2-frameTolerance:
  899. # keep track of stop time/frame for later
  900. feedback.tStop = t # not accounting for scr refresh
  901. feedback.frameNStop = frameN # exact frame index
  902. # add timestamp to datafile
  903. thisExp.timestampOnFlip(win, 'feedback.stopped')
  904. # update status
  905. feedback.status = FINISHED
  906. feedback.setAutoDraw(False)
  907. # check for quit (typically the Esc key)
  908. if defaultKeyboard.getKeys(keyList=["escape"]):
  909. thisExp.status = FINISHED
  910. if thisExp.status == FINISHED or endExpNow:
  911. endExperiment(thisExp, inputs=inputs, win=win)
  912. return
  913. # check if all components have finished
  914. if not continueRoutine: # a component has requested a forced-end of Routine
  915. routineForceEnded = True
  916. break
  917. continueRoutine = False # will revert to True if at least one component still running
  918. for thisComponent in mi_feedbackComponents:
  919. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  920. continueRoutine = True
  921. break # at least one component has not yet finished
  922. # refresh the screen
  923. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  924. win.flip()
  925. # --- Ending Routine "mi_feedback" ---
  926. for thisComponent in mi_feedbackComponents:
  927. if hasattr(thisComponent, "setAutoDraw"):
  928. thisComponent.setAutoDraw(False)
  929. thisExp.addData('mi_feedback.stopped', globalClock.getTime())
  930. # the Routine "mi_feedback" was not non-slip safe, so reset the non-slip timer
  931. routineTimer.reset()
  932. # --- Prepare to start Routine "mi_feedback_2" ---
  933. continueRoutine = True
  934. # update component parameters for each repeat
  935. thisExp.addData('mi_feedback_2.started', globalClock.getTime())
  936. # Run 'Begin Routine' code from code_2
  937. if feedback_time == 10:
  938. feedback_2.text = '恭喜!'
  939. hand_device.extend()
  940. else:
  941. feedback_2.text = '继续努力!'
  942. # keep track of which components have finished
  943. mi_feedback_2Components = [feedback_2]
  944. for thisComponent in mi_feedback_2Components:
  945. thisComponent.tStart = None
  946. thisComponent.tStop = None
  947. thisComponent.tStartRefresh = None
  948. thisComponent.tStopRefresh = None
  949. if hasattr(thisComponent, 'status'):
  950. thisComponent.status = NOT_STARTED
  951. # reset timers
  952. t = 0
  953. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  954. frameN = -1
  955. # --- Run Routine "mi_feedback_2" ---
  956. routineForceEnded = not continueRoutine
  957. while continueRoutine:
  958. # get current time
  959. t = routineTimer.getTime()
  960. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  961. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  962. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  963. # update/draw components on each frame
  964. # *feedback_2* updates
  965. # if feedback_2 is starting this frame...
  966. if feedback_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  967. # keep track of start time/frame for later
  968. feedback_2.frameNStart = frameN # exact frame index
  969. feedback_2.tStart = t # local t and not account for scr refresh
  970. feedback_2.tStartRefresh = tThisFlipGlobal # on global time
  971. win.timeOnFlip(feedback_2, 'tStartRefresh') # time at next scr refresh
  972. # add timestamp to datafile
  973. thisExp.timestampOnFlip(win, 'feedback_2.started')
  974. # update status
  975. feedback_2.status = STARTED
  976. feedback_2.setAutoDraw(True)
  977. # if feedback_2 is active this frame...
  978. if feedback_2.status == STARTED:
  979. # update params
  980. pass
  981. # if feedback_2 is stopping this frame...
  982. if feedback_2.status == STARTED:
  983. # is it time to stop? (based on global clock, using actual start)
  984. if tThisFlipGlobal > feedback_2.tStartRefresh + feedback_time / 2-frameTolerance:
  985. # keep track of stop time/frame for later
  986. feedback_2.tStop = t # not accounting for scr refresh
  987. feedback_2.frameNStop = frameN # exact frame index
  988. # add timestamp to datafile
  989. thisExp.timestampOnFlip(win, 'feedback_2.stopped')
  990. # update status
  991. feedback_2.status = FINISHED
  992. feedback_2.setAutoDraw(False)
  993. # check for quit (typically the Esc key)
  994. if defaultKeyboard.getKeys(keyList=["escape"]):
  995. thisExp.status = FINISHED
  996. if thisExp.status == FINISHED or endExpNow:
  997. endExperiment(thisExp, inputs=inputs, win=win)
  998. return
  999. # check if all components have finished
  1000. if not continueRoutine: # a component has requested a forced-end of Routine
  1001. routineForceEnded = True
  1002. break
  1003. continueRoutine = False # will revert to True if at least one component still running
  1004. for thisComponent in mi_feedback_2Components:
  1005. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1006. continueRoutine = True
  1007. break # at least one component has not yet finished
  1008. # refresh the screen
  1009. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1010. win.flip()
  1011. # --- Ending Routine "mi_feedback_2" ---
  1012. for thisComponent in mi_feedback_2Components:
  1013. if hasattr(thisComponent, "setAutoDraw"):
  1014. thisComponent.setAutoDraw(False)
  1015. thisExp.addData('mi_feedback_2.stopped', globalClock.getTime())
  1016. # the Routine "mi_feedback_2" was not non-slip safe, so reset the non-slip timer
  1017. routineTimer.reset()
  1018. # --- Prepare to start Routine "mi_rest" ---
  1019. continueRoutine = True
  1020. # update component parameters for each repeat
  1021. thisExp.addData('mi_rest.started', globalClock.getTime())
  1022. # Run 'Begin Routine' code from trigger_rest
  1023. # send trigger
  1024. win.callOnFlip(trigger.send_trigger, 0)
  1025. # keep track of which components have finished
  1026. mi_restComponents = [img_rest]
  1027. for thisComponent in mi_restComponents:
  1028. thisComponent.tStart = None
  1029. thisComponent.tStop = None
  1030. thisComponent.tStartRefresh = None
  1031. thisComponent.tStopRefresh = None
  1032. if hasattr(thisComponent, 'status'):
  1033. thisComponent.status = NOT_STARTED
  1034. # reset timers
  1035. t = 0
  1036. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  1037. frameN = -1
  1038. # --- Run Routine "mi_rest" ---
  1039. routineForceEnded = not continueRoutine
  1040. while continueRoutine and routineTimer.getTime() < 5.0:
  1041. # get current time
  1042. t = routineTimer.getTime()
  1043. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  1044. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  1045. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  1046. # update/draw components on each frame
  1047. # *img_rest* updates
  1048. # if img_rest is starting this frame...
  1049. if img_rest.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  1050. # keep track of start time/frame for later
  1051. img_rest.frameNStart = frameN # exact frame index
  1052. img_rest.tStart = t # local t and not account for scr refresh
  1053. img_rest.tStartRefresh = tThisFlipGlobal # on global time
  1054. win.timeOnFlip(img_rest, 'tStartRefresh') # time at next scr refresh
  1055. # add timestamp to datafile
  1056. thisExp.timestampOnFlip(win, 'img_rest.started')
  1057. # update status
  1058. img_rest.status = STARTED
  1059. img_rest.setAutoDraw(True)
  1060. # if img_rest is active this frame...
  1061. if img_rest.status == STARTED:
  1062. # update params
  1063. pass
  1064. # if img_rest is stopping this frame...
  1065. if img_rest.status == STARTED:
  1066. # is it time to stop? (based on global clock, using actual start)
  1067. if tThisFlipGlobal > img_rest.tStartRefresh + 5-frameTolerance:
  1068. # keep track of stop time/frame for later
  1069. img_rest.tStop = t # not accounting for scr refresh
  1070. img_rest.frameNStop = frameN # exact frame index
  1071. # add timestamp to datafile
  1072. thisExp.timestampOnFlip(win, 'img_rest.stopped')
  1073. # update status
  1074. img_rest.status = FINISHED
  1075. img_rest.setAutoDraw(False)
  1076. # check for quit (typically the Esc key)
  1077. if defaultKeyboard.getKeys(keyList=["escape"]):
  1078. thisExp.status = FINISHED
  1079. if thisExp.status == FINISHED or endExpNow:
  1080. endExperiment(thisExp, inputs=inputs, win=win)
  1081. return
  1082. # check if all components have finished
  1083. if not continueRoutine: # a component has requested a forced-end of Routine
  1084. routineForceEnded = True
  1085. break
  1086. continueRoutine = False # will revert to True if at least one component still running
  1087. for thisComponent in mi_restComponents:
  1088. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1089. continueRoutine = True
  1090. break # at least one component has not yet finished
  1091. # refresh the screen
  1092. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1093. win.flip()
  1094. # --- Ending Routine "mi_rest" ---
  1095. for thisComponent in mi_restComponents:
  1096. if hasattr(thisComponent, "setAutoDraw"):
  1097. thisComponent.setAutoDraw(False)
  1098. thisExp.addData('mi_rest.stopped', globalClock.getTime())
  1099. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1100. if routineForceEnded:
  1101. routineTimer.reset()
  1102. else:
  1103. routineTimer.addTime(-5.000000)
  1104. thisExp.nextEntry()
  1105. if thisSession is not None:
  1106. # if running in a Session with a Liaison client, send data up to now
  1107. thisSession.sendExperimentData()
  1108. # completed args.n_trials repeats of 'trials'
  1109. # --- Prepare to start Routine "end" ---
  1110. continueRoutine = True
  1111. # update component parameters for each repeat
  1112. thisExp.addData('end.started', globalClock.getTime())
  1113. # keep track of which components have finished
  1114. endComponents = [mi_end]
  1115. for thisComponent in endComponents:
  1116. thisComponent.tStart = None
  1117. thisComponent.tStop = None
  1118. thisComponent.tStartRefresh = None
  1119. thisComponent.tStopRefresh = None
  1120. if hasattr(thisComponent, 'status'):
  1121. thisComponent.status = NOT_STARTED
  1122. # reset timers
  1123. t = 0
  1124. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  1125. frameN = -1
  1126. # --- Run Routine "end" ---
  1127. routineForceEnded = not continueRoutine
  1128. while continueRoutine and routineTimer.getTime() < 5.0:
  1129. # get current time
  1130. t = routineTimer.getTime()
  1131. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  1132. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  1133. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  1134. # update/draw components on each frame
  1135. # *mi_end* updates
  1136. # if mi_end is starting this frame...
  1137. if mi_end.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  1138. # keep track of start time/frame for later
  1139. mi_end.frameNStart = frameN # exact frame index
  1140. mi_end.tStart = t # local t and not account for scr refresh
  1141. mi_end.tStartRefresh = tThisFlipGlobal # on global time
  1142. win.timeOnFlip(mi_end, 'tStartRefresh') # time at next scr refresh
  1143. # add timestamp to datafile
  1144. thisExp.timestampOnFlip(win, 'mi_end.started')
  1145. # update status
  1146. mi_end.status = STARTED
  1147. mi_end.setAutoDraw(True)
  1148. # if mi_end is active this frame...
  1149. if mi_end.status == STARTED:
  1150. # update params
  1151. pass
  1152. # if mi_end is stopping this frame...
  1153. if mi_end.status == STARTED:
  1154. # is it time to stop? (based on global clock, using actual start)
  1155. if tThisFlipGlobal > mi_end.tStartRefresh + 5-frameTolerance:
  1156. # keep track of stop time/frame for later
  1157. mi_end.tStop = t # not accounting for scr refresh
  1158. mi_end.frameNStop = frameN # exact frame index
  1159. # add timestamp to datafile
  1160. thisExp.timestampOnFlip(win, 'mi_end.stopped')
  1161. # update status
  1162. mi_end.status = FINISHED
  1163. mi_end.setAutoDraw(False)
  1164. # check for quit (typically the Esc key)
  1165. if defaultKeyboard.getKeys(keyList=["escape"]):
  1166. thisExp.status = FINISHED
  1167. if thisExp.status == FINISHED or endExpNow:
  1168. endExperiment(thisExp, inputs=inputs, win=win)
  1169. return
  1170. # check if all components have finished
  1171. if not continueRoutine: # a component has requested a forced-end of Routine
  1172. routineForceEnded = True
  1173. break
  1174. continueRoutine = False # will revert to True if at least one component still running
  1175. for thisComponent in endComponents:
  1176. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1177. continueRoutine = True
  1178. break # at least one component has not yet finished
  1179. # refresh the screen
  1180. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1181. win.flip()
  1182. # --- Ending Routine "end" ---
  1183. for thisComponent in endComponents:
  1184. if hasattr(thisComponent, "setAutoDraw"):
  1185. thisComponent.setAutoDraw(False)
  1186. thisExp.addData('end.stopped', globalClock.getTime())
  1187. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1188. if routineForceEnded:
  1189. routineTimer.reset()
  1190. else:
  1191. routineTimer.addTime(-5.000000)
  1192. # Run 'End Experiment' code from config
  1193. receiver.close()
  1194. # mark experiment as finished
  1195. endExperiment(thisExp, win=win, inputs=inputs)
  1196. def saveData(thisExp):
  1197. """
  1198. Save data from this experiment
  1199. Parameters
  1200. ==========
  1201. thisExp : psychopy.data.ExperimentHandler
  1202. Handler object for this experiment, contains the data to save and information about
  1203. where to save it to.
  1204. """
  1205. filename = thisExp.dataFileName
  1206. # these shouldn't be strictly necessary (should auto-save)
  1207. thisExp.saveAsWideText(filename + '.csv', delim='auto')
  1208. thisExp.saveAsPickle(filename)
  1209. def endExperiment(thisExp, inputs=None, win=None):
  1210. """
  1211. End this experiment, performing final shut down operations.
  1212. This function does NOT close the window or end the Python process - use `quit` for this.
  1213. Parameters
  1214. ==========
  1215. thisExp : psychopy.data.ExperimentHandler
  1216. Handler object for this experiment, contains the data to save and information about
  1217. where to save it to.
  1218. inputs : dict
  1219. Dictionary of input devices by name.
  1220. win : psychopy.visual.Window
  1221. Window for this experiment.
  1222. """
  1223. if win is not None:
  1224. # remove autodraw from all current components
  1225. win.clearAutoDraw()
  1226. # Flip one final time so any remaining win.callOnFlip()
  1227. # and win.timeOnFlip() tasks get executed
  1228. win.flip()
  1229. # mark experiment handler as finished
  1230. thisExp.status = FINISHED
  1231. # shut down eyetracker, if there is one
  1232. if inputs is not None:
  1233. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1234. inputs['eyetracker'].setConnectionState(False)
  1235. logging.flush()
  1236. def quit(thisExp, win=None, inputs=None, thisSession=None):
  1237. """
  1238. Fully quit, closing the window and ending the Python process.
  1239. Parameters
  1240. ==========
  1241. win : psychopy.visual.Window
  1242. Window to close.
  1243. inputs : dict
  1244. Dictionary of input devices by name.
  1245. thisSession : psychopy.session.Session or None
  1246. Handle of the Session object this experiment is being run from, if any.
  1247. """
  1248. thisExp.abort() # or data files will save again on exit
  1249. # make sure everything is closed down
  1250. if win is not None:
  1251. # Flip one final time so any remaining win.callOnFlip()
  1252. # and win.timeOnFlip() tasks get executed before quitting
  1253. win.flip()
  1254. win.close()
  1255. if inputs is not None:
  1256. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1257. inputs['eyetracker'].setConnectionState(False)
  1258. logging.flush()
  1259. if thisSession is not None:
  1260. thisSession.stop()
  1261. # terminate Python process
  1262. core.quit()
  1263. # if running this experiment as a script...
  1264. if __name__ == '__main__':
  1265. # call all functions in order
  1266. expInfo = showExpInfoDlg(expInfo=expInfo)
  1267. thisExp = setupData(expInfo=expInfo)
  1268. logFile = setupLogging(filename=thisExp.dataFileName)
  1269. win = setupWindow(expInfo=expInfo)
  1270. inputs = setupInputs(expInfo=expInfo, thisExp=thisExp, win=win)
  1271. run(
  1272. expInfo=expInfo,
  1273. thisExp=thisExp,
  1274. win=win,
  1275. inputs=inputs
  1276. )
  1277. saveData(thisExp=thisExp)
  1278. quit(thisExp=thisExp, win=win, inputs=inputs)