general_grasp_training.py 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. This experiment was created using PsychoPy3 Experiment Builder (v2023.2.3),
  5. on 十一月 14, 2023, at 15:00
  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.sig_chain.sig_receive import Receiver
  35. from device.sig_chain.device.connector_interface import Device
  36. from device.sig_chain.trigger_box import TriggerNeuracle
  37. from device.peripheral.manager import PeripheralHandManager
  38. from device import utils
  39. from schemas.hand_peripheral import FINGERMODEL_IDS
  40. from settings.config import settings
  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. '--train-id',
  61. dest='train_id',
  62. help='Train id in db',
  63. type=int
  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 = Receiver()
  90. config_info = settings.CONFIG_INFO
  91. receiver.select_connector(Device.NEO, 0.04, config_info)
  92. success = receiver.setup_connector()
  93. print(success)
  94. # begin to receive data from device.
  95. sleep(1)
  96. receiver.start_receive_wave()
  97. # connect to trigger box
  98. trigger = TriggerNeuracle(port='COM?')
  99. # connect to mechanical hand
  100. hand_device = PeripheralHandManager('fubo_pneumatic_finger', {'port': 'COM?'})
  101. # --- Setup global variables (available in all functions) ---
  102. # Ensure that relative paths start from the same directory as this script
  103. _thisDir = os.path.dirname(os.path.abspath(__file__))
  104. # Store info about the experiment session
  105. psychopyVersion = '2023.2.3'
  106. expName = 'train' # from the Builder filename that created this script
  107. expInfo = {
  108. 'participant': f"{randint(0, 999999):06.0f}",
  109. 'session': '001',
  110. 'date': data.getDateStr(), # add a simple timestamp
  111. 'expName': expName,
  112. 'psychopyVersion': psychopyVersion,
  113. }
  114. def showExpInfoDlg(expInfo):
  115. """
  116. Show participant info dialog.
  117. Parameters
  118. ==========
  119. expInfo : dict
  120. Information about this experiment, created by the `setupExpInfo` function.
  121. Returns
  122. ==========
  123. dict
  124. Information about this experiment.
  125. """
  126. # temporarily remove keys which the dialog doesn't need to show
  127. poppedKeys = {
  128. 'date': expInfo.pop('date', data.getDateStr()),
  129. 'expName': expInfo.pop('expName', expName),
  130. 'psychopyVersion': expInfo.pop('psychopyVersion', psychopyVersion),
  131. }
  132. # show participant info dialog
  133. dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName)
  134. if dlg.OK == False:
  135. core.quit() # user pressed cancel
  136. # restore hidden keys
  137. expInfo.update(poppedKeys)
  138. # return expInfo
  139. return expInfo
  140. def setupData(expInfo, dataDir=None):
  141. """
  142. Make an ExperimentHandler to handle trials and saving.
  143. Parameters
  144. ==========
  145. expInfo : dict
  146. Information about this experiment, created by the `setupExpInfo` function.
  147. dataDir : Path, str or None
  148. Folder to save the data to, leave as None to create a folder in the current directory.
  149. Returns
  150. ==========
  151. psychopy.data.ExperimentHandler
  152. Handler object for this experiment, contains the data to save and information about
  153. where to save it to.
  154. """
  155. # data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
  156. if dataDir is None:
  157. dataDir = _thisDir
  158. filename = u'data/%s_%s_%s' % (expInfo['participant'], expName, expInfo['date'])
  159. # make sure filename is relative to dataDir
  160. if os.path.isabs(filename):
  161. dataDir = os.path.commonprefix([dataDir, filename])
  162. filename = os.path.relpath(filename, dataDir)
  163. # an ExperimentHandler isn't essential but helps with data saving
  164. thisExp = data.ExperimentHandler(
  165. name=expName, version='',
  166. extraInfo=expInfo, runtimeInfo=None,
  167. originPath='C:\\Users\\dkunl\\kraken\\backend\\general_grasp_training.py',
  168. savePickle=True, saveWideText=True,
  169. dataFileName=dataDir + os.sep + filename, sortColumns='time'
  170. )
  171. thisExp.setPriority('thisRow.t', priority.CRITICAL)
  172. thisExp.setPriority('expName', priority.LOW)
  173. # return experiment handler
  174. return thisExp
  175. def setupLogging(filename):
  176. """
  177. Setup a log file and tell it what level to log at.
  178. Parameters
  179. ==========
  180. filename : str or pathlib.Path
  181. Filename to save log file and data files as, doesn't need an extension.
  182. Returns
  183. ==========
  184. psychopy.logging.LogFile
  185. Text stream to receive inputs from the logging system.
  186. """
  187. # this outputs to the screen, not a file
  188. logging.console.setLevel(logging.EXP)
  189. # save a log file for detail verbose info
  190. logFile = logging.LogFile(filename+'.log', level=logging.EXP)
  191. return logFile
  192. def setupWindow(expInfo=None, win=None):
  193. """
  194. Setup the Window
  195. Parameters
  196. ==========
  197. expInfo : dict
  198. Information about this experiment, created by the `setupExpInfo` function.
  199. win : psychopy.visual.Window
  200. Window to setup - leave as None to create a new window.
  201. Returns
  202. ==========
  203. psychopy.visual.Window
  204. Window in which to run this experiment.
  205. """
  206. if win is None:
  207. # if not given a window to setup, make one
  208. win = visual.Window(
  209. size=[1493, 933], fullscr=True, screen=0,
  210. winType='pyglet', allowStencil=False,
  211. monitor='testMonitor', color=[1,1,1], colorSpace='rgb',
  212. backgroundImage='', backgroundFit='none',
  213. blendMode='avg', useFBO=True,
  214. units='height'
  215. )
  216. if expInfo is not None:
  217. # store frame rate of monitor if we can measure it
  218. expInfo['frameRate'] = win.getActualFrameRate()
  219. else:
  220. # if we have a window, just set the attributes which are safe to set
  221. win.color = [1,1,1]
  222. win.colorSpace = 'rgb'
  223. win.backgroundImage = ''
  224. win.backgroundFit = 'none'
  225. win.units = 'height'
  226. win.mouseVisible = False
  227. win.hideMessage()
  228. return win
  229. def setupInputs(expInfo, thisExp, win):
  230. """
  231. Setup whatever inputs are available (mouse, keyboard, eyetracker, etc.)
  232. Parameters
  233. ==========
  234. expInfo : dict
  235. Information about this experiment, created by the `setupExpInfo` function.
  236. thisExp : psychopy.data.ExperimentHandler
  237. Handler object for this experiment, contains the data to save and information about
  238. where to save it to.
  239. win : psychopy.visual.Window
  240. Window in which to run this experiment.
  241. Returns
  242. ==========
  243. dict
  244. Dictionary of input devices by name.
  245. """
  246. # --- Setup input devices ---
  247. inputs = {}
  248. ioConfig = {}
  249. ioSession = ioServer = eyetracker = None
  250. # create a default keyboard (e.g. to check for escape)
  251. defaultKeyboard = keyboard.Keyboard(backend='ptb')
  252. # return inputs dict
  253. return {
  254. 'ioServer': ioServer,
  255. 'defaultKeyboard': defaultKeyboard,
  256. 'eyetracker': eyetracker,
  257. }
  258. def pauseExperiment(thisExp, inputs=None, win=None, timers=[], playbackComponents=[]):
  259. """
  260. Pause this experiment, preventing the flow from advancing to the next routine until resumed.
  261. Parameters
  262. ==========
  263. thisExp : psychopy.data.ExperimentHandler
  264. Handler object for this experiment, contains the data to save and information about
  265. where to save it to.
  266. inputs : dict
  267. Dictionary of input devices by name.
  268. win : psychopy.visual.Window
  269. Window for this experiment.
  270. timers : list, tuple
  271. List of timers to reset once pausing is finished.
  272. playbackComponents : list, tuple
  273. List of any components with a `pause` method which need to be paused.
  274. """
  275. # if we are not paused, do nothing
  276. if thisExp.status != PAUSED:
  277. return
  278. # pause any playback components
  279. for comp in playbackComponents:
  280. comp.pause()
  281. # prevent components from auto-drawing
  282. win.stashAutoDraw()
  283. # run a while loop while we wait to unpause
  284. while thisExp.status == PAUSED:
  285. # make sure we have a keyboard
  286. if inputs is None:
  287. inputs = {
  288. 'defaultKeyboard': keyboard.Keyboard(backend='PsychToolbox')
  289. }
  290. # check for quit (typically the Esc key)
  291. if inputs['defaultKeyboard'].getKeys(keyList=['escape']):
  292. endExperiment(thisExp, win=win, inputs=inputs)
  293. # flip the screen
  294. win.flip()
  295. # if stop was requested while paused, quit
  296. if thisExp.status == FINISHED:
  297. endExperiment(thisExp, inputs=inputs, win=win)
  298. # resume any playback components
  299. for comp in playbackComponents:
  300. comp.play()
  301. # restore auto-drawn components
  302. win.retrieveAutoDraw()
  303. # reset any timers
  304. for timer in timers:
  305. timer.reset()
  306. def run(expInfo, thisExp, win, inputs, globalClock=None, thisSession=None):
  307. """
  308. Run the experiment flow.
  309. Parameters
  310. ==========
  311. expInfo : dict
  312. Information about this experiment, created by the `setupExpInfo` function.
  313. thisExp : psychopy.data.ExperimentHandler
  314. Handler object for this experiment, contains the data to save and information about
  315. where to save it to.
  316. psychopy.visual.Window
  317. Window in which to run this experiment.
  318. inputs : dict
  319. Dictionary of input devices by name.
  320. globalClock : psychopy.core.clock.Clock or None
  321. Clock to get global time from - supply None to make a new one.
  322. thisSession : psychopy.session.Session or None
  323. Handle of the Session object this experiment is being run from, if any.
  324. """
  325. # mark experiment as started
  326. thisExp.status = STARTED
  327. # make sure variables created by exec are available globally
  328. exec = environmenttools.setExecEnvironment(globals())
  329. # get device handles from dict of input devices
  330. ioServer = inputs['ioServer']
  331. defaultKeyboard = inputs['defaultKeyboard']
  332. eyetracker = inputs['eyetracker']
  333. # make sure we're running in the directory for this experiment
  334. os.chdir(_thisDir)
  335. # get filename from ExperimentHandler for convenience
  336. filename = thisExp.dataFileName
  337. frameTolerance = 0.001 # how close to onset before 'same' frame
  338. endExpNow = False # flag for 'escape' or other condition => quit the exp
  339. # get frame duration from frame rate in expInfo
  340. if 'frameRate' in expInfo and expInfo['frameRate'] is not None:
  341. frameDur = 1.0 / round(expInfo['frameRate'])
  342. else:
  343. frameDur = 1.0 / 60.0 # could not measure, so guess
  344. # Start Code - component code to be run after the window creation
  345. # --- Initialize components for Routine "before_mi" ---
  346. train_position = visual.TextStim(win=win, name='train_position',
  347. text='训练部位:右手',
  348. font='Open Sans',
  349. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  350. color='black', colorSpace='rgb', opacity=None,
  351. languageStyle='LTR',
  352. depth=0.0);
  353. instruction = visual.TextStim(win=win, name='instruction',
  354. text='准备进行一般抓握训练,\n按回车键继续',
  355. font='Open Sans',
  356. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  357. color='black', colorSpace='rgb', opacity=None,
  358. languageStyle='LTR',
  359. depth=-1.0);
  360. key_resp = keyboard.Keyboard()
  361. # --- Initialize components for Routine "mi_prepare" ---
  362. text = visual.TextStim(win=win, name='text',
  363. text='请准备开始尝试运动',
  364. font='Open Sans',
  365. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  366. color='black', colorSpace='rgb', opacity=None,
  367. languageStyle='LTR',
  368. depth=-1.0);
  369. # --- Initialize components for Routine "mi_begin" ---
  370. img_right = visual.ImageStim(
  371. win=win,
  372. name='img_right',
  373. image='static/images/hand_move.jpg', mask=None, anchor='center',
  374. ori=0.0, pos=(0, 0), size=None,
  375. color=[1,1,1], colorSpace='rgb', opacity=None,
  376. flipHoriz=False, flipVert=False,
  377. texRes=128.0, interpolate=True, depth=0.0)
  378. # --- Initialize components for Routine "collect_result" ---
  379. # --- Initialize components for Routine "mi_feedback" ---
  380. feedback = visual.TextStim(win=win, name='feedback',
  381. text=None,
  382. font='Open Sans',
  383. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  384. color='black', colorSpace='rgb', opacity=None,
  385. languageStyle='LTR',
  386. depth=0.0);
  387. # --- Initialize components for Routine "mi_rest" ---
  388. img_rest = visual.ImageStim(
  389. win=win,
  390. name='img_rest',
  391. image='static/images/rest.jpg', mask=None, anchor='center',
  392. ori=0.0, pos=(0, 0), size=None,
  393. color=[1,1,1], colorSpace='rgb', opacity=None,
  394. flipHoriz=False, flipVert=False,
  395. texRes=128.0, interpolate=True, depth=0.0)
  396. # --- Initialize components for Routine "end" ---
  397. mi_end = visual.TextStim(win=win, name='mi_end',
  398. text='结束实验',
  399. font='Open Sans',
  400. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  401. color='black', colorSpace='rgb', opacity=None,
  402. languageStyle='LTR',
  403. depth=0.0);
  404. # create some handy timers
  405. if globalClock is None:
  406. globalClock = core.Clock() # to track the time since experiment started
  407. if ioServer is not None:
  408. ioServer.syncClock(globalClock)
  409. logging.setDefaultClock(globalClock)
  410. routineTimer = core.Clock() # to track time remaining of each (possibly non-slip) routine
  411. win.flip() # flip window to reset last flip timer
  412. # store the exact time the global clock started
  413. expInfo['expStart'] = data.getDateStr(format='%Y-%m-%d %Hh%M.%S.%f %z', fractionalSecondDigits=6)
  414. # --- Prepare to start Routine "before_mi" ---
  415. continueRoutine = True
  416. # update component parameters for each repeat
  417. thisExp.addData('before_mi.started', globalClock.getTime())
  418. key_resp.keys = []
  419. key_resp.rt = []
  420. _key_resp_allKeys = []
  421. # keep track of which components have finished
  422. before_miComponents = [train_position, instruction, key_resp]
  423. for thisComponent in before_miComponents:
  424. thisComponent.tStart = None
  425. thisComponent.tStop = None
  426. thisComponent.tStartRefresh = None
  427. thisComponent.tStopRefresh = None
  428. if hasattr(thisComponent, 'status'):
  429. thisComponent.status = NOT_STARTED
  430. # reset timers
  431. t = 0
  432. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  433. frameN = -1
  434. # --- Run Routine "before_mi" ---
  435. routineForceEnded = not continueRoutine
  436. while continueRoutine:
  437. # get current time
  438. t = routineTimer.getTime()
  439. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  440. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  441. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  442. # update/draw components on each frame
  443. # *train_position* updates
  444. # if train_position is starting this frame...
  445. if train_position.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  446. # keep track of start time/frame for later
  447. train_position.frameNStart = frameN # exact frame index
  448. train_position.tStart = t # local t and not account for scr refresh
  449. train_position.tStartRefresh = tThisFlipGlobal # on global time
  450. win.timeOnFlip(train_position, 'tStartRefresh') # time at next scr refresh
  451. # add timestamp to datafile
  452. thisExp.timestampOnFlip(win, 'train_position.started')
  453. # update status
  454. train_position.status = STARTED
  455. train_position.setAutoDraw(True)
  456. # if train_position is active this frame...
  457. if train_position.status == STARTED:
  458. # update params
  459. pass
  460. # if train_position is stopping this frame...
  461. if train_position.status == STARTED:
  462. # is it time to stop? (based on global clock, using actual start)
  463. if tThisFlipGlobal > train_position.tStartRefresh + 2-frameTolerance:
  464. # keep track of stop time/frame for later
  465. train_position.tStop = t # not accounting for scr refresh
  466. train_position.frameNStop = frameN # exact frame index
  467. # add timestamp to datafile
  468. thisExp.timestampOnFlip(win, 'train_position.stopped')
  469. # update status
  470. train_position.status = FINISHED
  471. train_position.setAutoDraw(False)
  472. # *instruction* updates
  473. # if instruction is starting this frame...
  474. if instruction.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  475. # keep track of start time/frame for later
  476. instruction.frameNStart = frameN # exact frame index
  477. instruction.tStart = t # local t and not account for scr refresh
  478. instruction.tStartRefresh = tThisFlipGlobal # on global time
  479. win.timeOnFlip(instruction, 'tStartRefresh') # time at next scr refresh
  480. # add timestamp to datafile
  481. thisExp.timestampOnFlip(win, 'instruction.started')
  482. # update status
  483. instruction.status = STARTED
  484. instruction.setAutoDraw(True)
  485. # if instruction is active this frame...
  486. if instruction.status == STARTED:
  487. # update params
  488. pass
  489. # *key_resp* updates
  490. waitOnFlip = False
  491. # if key_resp is starting this frame...
  492. if key_resp.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  493. # keep track of start time/frame for later
  494. key_resp.frameNStart = frameN # exact frame index
  495. key_resp.tStart = t # local t and not account for scr refresh
  496. key_resp.tStartRefresh = tThisFlipGlobal # on global time
  497. win.timeOnFlip(key_resp, 'tStartRefresh') # time at next scr refresh
  498. # add timestamp to datafile
  499. thisExp.timestampOnFlip(win, 'key_resp.started')
  500. # update status
  501. key_resp.status = STARTED
  502. # keyboard checking is just starting
  503. waitOnFlip = True
  504. win.callOnFlip(key_resp.clock.reset) # t=0 on next screen flip
  505. win.callOnFlip(key_resp.clearEvents, eventType='keyboard') # clear events on next screen flip
  506. if key_resp.status == STARTED and not waitOnFlip:
  507. theseKeys = key_resp.getKeys(keyList=['space'], ignoreKeys=["escape"], waitRelease=False)
  508. _key_resp_allKeys.extend(theseKeys)
  509. if len(_key_resp_allKeys):
  510. key_resp.keys = _key_resp_allKeys[-1].name # just the last key pressed
  511. key_resp.rt = _key_resp_allKeys[-1].rt
  512. key_resp.duration = _key_resp_allKeys[-1].duration
  513. # a response ends the routine
  514. continueRoutine = False
  515. # check for quit (typically the Esc key)
  516. if defaultKeyboard.getKeys(keyList=["escape"]):
  517. thisExp.status = FINISHED
  518. if thisExp.status == FINISHED or endExpNow:
  519. endExperiment(thisExp, inputs=inputs, win=win)
  520. return
  521. # check if all components have finished
  522. if not continueRoutine: # a component has requested a forced-end of Routine
  523. routineForceEnded = True
  524. break
  525. continueRoutine = False # will revert to True if at least one component still running
  526. for thisComponent in before_miComponents:
  527. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  528. continueRoutine = True
  529. break # at least one component has not yet finished
  530. # refresh the screen
  531. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  532. win.flip()
  533. # --- Ending Routine "before_mi" ---
  534. for thisComponent in before_miComponents:
  535. if hasattr(thisComponent, "setAutoDraw"):
  536. thisComponent.setAutoDraw(False)
  537. thisExp.addData('before_mi.stopped', globalClock.getTime())
  538. # Run 'End Routine' code from config
  539. # set saver
  540. receiver.connector.set_saver()
  541. subject = args.subj
  542. path = utils.create_data_dir(subject, args.train_id) # owner_name, train_id
  543. filename = f"{subject}_{datetime.datetime.now().strftime('%H%M%S')}.bdf"
  544. receiver.connector.saver.set_edf_header(subject, filename, 5, path)
  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 = 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 = get_data()
  778. receiver.connector.receive_wave()
  779. data_from_buffer = receiver.get_data_from_buffer("classify_online", clear=False)
  780. if data_from_buffer["status"] == "ok":
  781. decision = controller.step_decision(data_from_buffer, current_true_label)
  782. decision_buffer.append(decision)
  783. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  784. if routineForceEnded:
  785. routineTimer.reset()
  786. else:
  787. routineTimer.addTime(-1.000000)
  788. # completed 5.0 repeats of 'classification'
  789. # --- Prepare to start Routine "collect_result" ---
  790. continueRoutine = True
  791. # update component parameters for each repeat
  792. thisExp.addData('collect_result.started', globalClock.getTime())
  793. # Run 'Begin Routine' code from collect_decisions
  794. cnt = 0
  795. for d in decision_buffer:
  796. if d == current_true_label:
  797. cnt += 1
  798. if cnt >= 3:
  799. hand_device.start_round(args.finger_model)
  800. feedback_time = 10
  801. else:
  802. feedback_time = 2
  803. # keep track of which components have finished
  804. collect_resultComponents = []
  805. for thisComponent in collect_resultComponents:
  806. thisComponent.tStart = None
  807. thisComponent.tStop = None
  808. thisComponent.tStartRefresh = None
  809. thisComponent.tStopRefresh = None
  810. if hasattr(thisComponent, 'status'):
  811. thisComponent.status = NOT_STARTED
  812. # reset timers
  813. t = 0
  814. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  815. frameN = -1
  816. # --- Run Routine "collect_result" ---
  817. routineForceEnded = not continueRoutine
  818. while continueRoutine:
  819. # get current time
  820. t = routineTimer.getTime()
  821. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  822. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  823. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  824. # update/draw components on each frame
  825. # check for quit (typically the Esc key)
  826. if defaultKeyboard.getKeys(keyList=["escape"]):
  827. thisExp.status = FINISHED
  828. if thisExp.status == FINISHED or endExpNow:
  829. endExperiment(thisExp, inputs=inputs, win=win)
  830. return
  831. # check if all components have finished
  832. if not continueRoutine: # a component has requested a forced-end of Routine
  833. routineForceEnded = True
  834. break
  835. continueRoutine = False # will revert to True if at least one component still running
  836. for thisComponent in collect_resultComponents:
  837. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  838. continueRoutine = True
  839. break # at least one component has not yet finished
  840. # refresh the screen
  841. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  842. win.flip()
  843. # --- Ending Routine "collect_result" ---
  844. for thisComponent in collect_resultComponents:
  845. if hasattr(thisComponent, "setAutoDraw"):
  846. thisComponent.setAutoDraw(False)
  847. thisExp.addData('collect_result.stopped', globalClock.getTime())
  848. # the Routine "collect_result" was not non-slip safe, so reset the non-slip timer
  849. routineTimer.reset()
  850. # --- Prepare to start Routine "mi_feedback" ---
  851. continueRoutine = True
  852. # update component parameters for each repeat
  853. thisExp.addData('mi_feedback.started', globalClock.getTime())
  854. # Run 'Begin Routine' code from code
  855. if feedback_time == 10:
  856. feedback.text = '恭喜!'
  857. else:
  858. feedback.text = '继续努力!'
  859. # keep track of which components have finished
  860. mi_feedbackComponents = [feedback]
  861. for thisComponent in mi_feedbackComponents:
  862. thisComponent.tStart = None
  863. thisComponent.tStop = None
  864. thisComponent.tStartRefresh = None
  865. thisComponent.tStopRefresh = None
  866. if hasattr(thisComponent, 'status'):
  867. thisComponent.status = NOT_STARTED
  868. # reset timers
  869. t = 0
  870. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  871. frameN = -1
  872. # --- Run Routine "mi_feedback" ---
  873. routineForceEnded = not continueRoutine
  874. while continueRoutine:
  875. # get current time
  876. t = routineTimer.getTime()
  877. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  878. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  879. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  880. # update/draw components on each frame
  881. # *feedback* updates
  882. # if feedback is starting this frame...
  883. if feedback.status == NOT_STARTED and tThisFlip >= 0-frameTolerance:
  884. # keep track of start time/frame for later
  885. feedback.frameNStart = frameN # exact frame index
  886. feedback.tStart = t # local t and not account for scr refresh
  887. feedback.tStartRefresh = tThisFlipGlobal # on global time
  888. win.timeOnFlip(feedback, 'tStartRefresh') # time at next scr refresh
  889. # add timestamp to datafile
  890. thisExp.timestampOnFlip(win, 'feedback.started')
  891. # update status
  892. feedback.status = STARTED
  893. feedback.setAutoDraw(True)
  894. # if feedback is active this frame...
  895. if feedback.status == STARTED:
  896. # update params
  897. pass
  898. # if feedback is stopping this frame...
  899. if feedback.status == STARTED:
  900. # is it time to stop? (based on global clock, using actual start)
  901. if tThisFlipGlobal > feedback.tStartRefresh + feedback_time-frameTolerance:
  902. # keep track of stop time/frame for later
  903. feedback.tStop = t # not accounting for scr refresh
  904. feedback.frameNStop = frameN # exact frame index
  905. # add timestamp to datafile
  906. thisExp.timestampOnFlip(win, 'feedback.stopped')
  907. # update status
  908. feedback.status = FINISHED
  909. feedback.setAutoDraw(False)
  910. # check for quit (typically the Esc key)
  911. if defaultKeyboard.getKeys(keyList=["escape"]):
  912. thisExp.status = FINISHED
  913. if thisExp.status == FINISHED or endExpNow:
  914. endExperiment(thisExp, inputs=inputs, win=win)
  915. return
  916. # check if all components have finished
  917. if not continueRoutine: # a component has requested a forced-end of Routine
  918. routineForceEnded = True
  919. break
  920. continueRoutine = False # will revert to True if at least one component still running
  921. for thisComponent in mi_feedbackComponents:
  922. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  923. continueRoutine = True
  924. break # at least one component has not yet finished
  925. # refresh the screen
  926. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  927. win.flip()
  928. # --- Ending Routine "mi_feedback" ---
  929. for thisComponent in mi_feedbackComponents:
  930. if hasattr(thisComponent, "setAutoDraw"):
  931. thisComponent.setAutoDraw(False)
  932. thisExp.addData('mi_feedback.stopped', globalClock.getTime())
  933. # the Routine "mi_feedback" was not non-slip safe, so reset the non-slip timer
  934. routineTimer.reset()
  935. # --- Prepare to start Routine "mi_rest" ---
  936. continueRoutine = True
  937. # update component parameters for each repeat
  938. thisExp.addData('mi_rest.started', globalClock.getTime())
  939. # Run 'Begin Routine' code from trigger_rest
  940. # send trigger
  941. win.callOnFlip(trigger.send_trigger, 0)
  942. # keep track of which components have finished
  943. mi_restComponents = [img_rest]
  944. for thisComponent in mi_restComponents:
  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_rest" ---
  956. routineForceEnded = not continueRoutine
  957. while continueRoutine and routineTimer.getTime() < 5.0:
  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. # *img_rest* updates
  965. # if img_rest is starting this frame...
  966. if img_rest.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  967. # keep track of start time/frame for later
  968. img_rest.frameNStart = frameN # exact frame index
  969. img_rest.tStart = t # local t and not account for scr refresh
  970. img_rest.tStartRefresh = tThisFlipGlobal # on global time
  971. win.timeOnFlip(img_rest, 'tStartRefresh') # time at next scr refresh
  972. # add timestamp to datafile
  973. thisExp.timestampOnFlip(win, 'img_rest.started')
  974. # update status
  975. img_rest.status = STARTED
  976. img_rest.setAutoDraw(True)
  977. # if img_rest is active this frame...
  978. if img_rest.status == STARTED:
  979. # update params
  980. pass
  981. # if img_rest is stopping this frame...
  982. if img_rest.status == STARTED:
  983. # is it time to stop? (based on global clock, using actual start)
  984. if tThisFlipGlobal > img_rest.tStartRefresh + 5-frameTolerance:
  985. # keep track of stop time/frame for later
  986. img_rest.tStop = t # not accounting for scr refresh
  987. img_rest.frameNStop = frameN # exact frame index
  988. # add timestamp to datafile
  989. thisExp.timestampOnFlip(win, 'img_rest.stopped')
  990. # update status
  991. img_rest.status = FINISHED
  992. img_rest.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_restComponents:
  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_rest" ---
  1012. for thisComponent in mi_restComponents:
  1013. if hasattr(thisComponent, "setAutoDraw"):
  1014. thisComponent.setAutoDraw(False)
  1015. thisExp.addData('mi_rest.stopped', globalClock.getTime())
  1016. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1017. if routineForceEnded:
  1018. routineTimer.reset()
  1019. else:
  1020. routineTimer.addTime(-5.000000)
  1021. thisExp.nextEntry()
  1022. if thisSession is not None:
  1023. # if running in a Session with a Liaison client, send data up to now
  1024. thisSession.sendExperimentData()
  1025. # completed args.n_trials repeats of 'trials'
  1026. # --- Prepare to start Routine "end" ---
  1027. continueRoutine = True
  1028. # update component parameters for each repeat
  1029. thisExp.addData('end.started', globalClock.getTime())
  1030. # keep track of which components have finished
  1031. endComponents = [mi_end]
  1032. for thisComponent in endComponents:
  1033. thisComponent.tStart = None
  1034. thisComponent.tStop = None
  1035. thisComponent.tStartRefresh = None
  1036. thisComponent.tStopRefresh = None
  1037. if hasattr(thisComponent, 'status'):
  1038. thisComponent.status = NOT_STARTED
  1039. # reset timers
  1040. t = 0
  1041. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  1042. frameN = -1
  1043. # --- Run Routine "end" ---
  1044. routineForceEnded = not continueRoutine
  1045. while continueRoutine and routineTimer.getTime() < 5.0:
  1046. # get current time
  1047. t = routineTimer.getTime()
  1048. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  1049. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  1050. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  1051. # update/draw components on each frame
  1052. # *mi_end* updates
  1053. # if mi_end is starting this frame...
  1054. if mi_end.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  1055. # keep track of start time/frame for later
  1056. mi_end.frameNStart = frameN # exact frame index
  1057. mi_end.tStart = t # local t and not account for scr refresh
  1058. mi_end.tStartRefresh = tThisFlipGlobal # on global time
  1059. win.timeOnFlip(mi_end, 'tStartRefresh') # time at next scr refresh
  1060. # add timestamp to datafile
  1061. thisExp.timestampOnFlip(win, 'mi_end.started')
  1062. # update status
  1063. mi_end.status = STARTED
  1064. mi_end.setAutoDraw(True)
  1065. # if mi_end is active this frame...
  1066. if mi_end.status == STARTED:
  1067. # update params
  1068. pass
  1069. # if mi_end is stopping this frame...
  1070. if mi_end.status == STARTED:
  1071. # is it time to stop? (based on global clock, using actual start)
  1072. if tThisFlipGlobal > mi_end.tStartRefresh + 5-frameTolerance:
  1073. # keep track of stop time/frame for later
  1074. mi_end.tStop = t # not accounting for scr refresh
  1075. mi_end.frameNStop = frameN # exact frame index
  1076. # add timestamp to datafile
  1077. thisExp.timestampOnFlip(win, 'mi_end.stopped')
  1078. # update status
  1079. mi_end.status = FINISHED
  1080. mi_end.setAutoDraw(False)
  1081. # check for quit (typically the Esc key)
  1082. if defaultKeyboard.getKeys(keyList=["escape"]):
  1083. thisExp.status = FINISHED
  1084. if thisExp.status == FINISHED or endExpNow:
  1085. endExperiment(thisExp, inputs=inputs, win=win)
  1086. return
  1087. # check if all components have finished
  1088. if not continueRoutine: # a component has requested a forced-end of Routine
  1089. routineForceEnded = True
  1090. break
  1091. continueRoutine = False # will revert to True if at least one component still running
  1092. for thisComponent in endComponents:
  1093. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1094. continueRoutine = True
  1095. break # at least one component has not yet finished
  1096. # refresh the screen
  1097. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1098. win.flip()
  1099. # --- Ending Routine "end" ---
  1100. for thisComponent in endComponents:
  1101. if hasattr(thisComponent, "setAutoDraw"):
  1102. thisComponent.setAutoDraw(False)
  1103. thisExp.addData('end.stopped', globalClock.getTime())
  1104. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1105. if routineForceEnded:
  1106. routineTimer.reset()
  1107. else:
  1108. routineTimer.addTime(-5.000000)
  1109. # Run 'End Experiment' code from config
  1110. receiver.stop_receive()
  1111. # mark experiment as finished
  1112. endExperiment(thisExp, win=win, inputs=inputs)
  1113. def saveData(thisExp):
  1114. """
  1115. Save data from this experiment
  1116. Parameters
  1117. ==========
  1118. thisExp : psychopy.data.ExperimentHandler
  1119. Handler object for this experiment, contains the data to save and information about
  1120. where to save it to.
  1121. """
  1122. filename = thisExp.dataFileName
  1123. # these shouldn't be strictly necessary (should auto-save)
  1124. thisExp.saveAsWideText(filename + '.csv', delim='auto')
  1125. thisExp.saveAsPickle(filename)
  1126. def endExperiment(thisExp, inputs=None, win=None):
  1127. """
  1128. End this experiment, performing final shut down operations.
  1129. This function does NOT close the window or end the Python process - use `quit` for this.
  1130. Parameters
  1131. ==========
  1132. thisExp : psychopy.data.ExperimentHandler
  1133. Handler object for this experiment, contains the data to save and information about
  1134. where to save it to.
  1135. inputs : dict
  1136. Dictionary of input devices by name.
  1137. win : psychopy.visual.Window
  1138. Window for this experiment.
  1139. """
  1140. if win is not None:
  1141. # remove autodraw from all current components
  1142. win.clearAutoDraw()
  1143. # Flip one final time so any remaining win.callOnFlip()
  1144. # and win.timeOnFlip() tasks get executed
  1145. win.flip()
  1146. # mark experiment handler as finished
  1147. thisExp.status = FINISHED
  1148. # shut down eyetracker, if there is one
  1149. if inputs is not None:
  1150. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1151. inputs['eyetracker'].setConnectionState(False)
  1152. logging.flush()
  1153. def quit(thisExp, win=None, inputs=None, thisSession=None):
  1154. """
  1155. Fully quit, closing the window and ending the Python process.
  1156. Parameters
  1157. ==========
  1158. win : psychopy.visual.Window
  1159. Window to close.
  1160. inputs : dict
  1161. Dictionary of input devices by name.
  1162. thisSession : psychopy.session.Session or None
  1163. Handle of the Session object this experiment is being run from, if any.
  1164. """
  1165. thisExp.abort() # or data files will save again on exit
  1166. # make sure everything is closed down
  1167. if win is not None:
  1168. # Flip one final time so any remaining win.callOnFlip()
  1169. # and win.timeOnFlip() tasks get executed before quitting
  1170. win.flip()
  1171. win.close()
  1172. if inputs is not None:
  1173. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1174. inputs['eyetracker'].setConnectionState(False)
  1175. logging.flush()
  1176. if thisSession is not None:
  1177. thisSession.stop()
  1178. # terminate Python process
  1179. core.quit()
  1180. # if running this experiment as a script...
  1181. if __name__ == '__main__':
  1182. # call all functions in order
  1183. expInfo = showExpInfoDlg(expInfo=expInfo)
  1184. thisExp = setupData(expInfo=expInfo)
  1185. logFile = setupLogging(filename=thisExp.dataFileName)
  1186. win = setupWindow(expInfo=expInfo)
  1187. inputs = setupInputs(expInfo=expInfo, thisExp=thisExp, win=win)
  1188. run(
  1189. expInfo=expInfo,
  1190. thisExp=thisExp,
  1191. win=win,
  1192. inputs=inputs
  1193. )
  1194. saveData(thisExp=thisExp)
  1195. quit(thisExp=thisExp, win=win, inputs=inputs)