general_grasp_training.py 60 KB

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