general_grasp_training.py 54 KB

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