psychopy 2024.1.3__py3-none-any.whl → 2024.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of psychopy might be problematic. Click here for more details.

Files changed (331) hide show
  1. psychopy/.DS_Store +0 -0
  2. psychopy/CHANGELOG.txt +206 -0
  3. psychopy/GIT_SHA +1 -0
  4. psychopy/VERSION +1 -0
  5. psychopy/__init__.py +77 -15
  6. psychopy/app/Resources/classic/plugin16.png +0 -0
  7. psychopy/app/Resources/classic/plugin16@2x.png +0 -0
  8. psychopy/app/Resources/dark/plugin16.png +0 -0
  9. psychopy/app/Resources/dark/plugin16@2x.png +0 -0
  10. psychopy/app/Resources/light/plugin16.png +0 -0
  11. psychopy/app/Resources/light/plugin16@2x.png +0 -0
  12. psychopy/app/__init__.py +76 -2
  13. psychopy/app/_psychopyApp.py +126 -101
  14. psychopy/app/builder/builder.py +14 -10
  15. psychopy/app/builder/dialogs/__init__.py +8 -8
  16. psychopy/app/builder/dialogs/dlgsConditions.py +12 -13
  17. psychopy/app/builder/dialogs/paramCtrls.py +24 -57
  18. psychopy/app/builder/localizedStrings.py +11 -9
  19. psychopy/app/builder/validators.py +2 -2
  20. psychopy/app/coder/codeEditorBase.py +8 -8
  21. psychopy/app/coder/coder.py +4 -4
  22. psychopy/app/connections/sendusage.py +2 -2
  23. psychopy/app/connections/updates.py +9 -9
  24. psychopy/app/dialogs.py +34 -2
  25. psychopy/app/idle.py +31 -0
  26. psychopy/app/jobs.py +21 -3
  27. psychopy/app/linuxconfig/__init__.py +9 -0
  28. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  29. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +4602 -2540
  30. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  31. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +56 -54
  32. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +53 -43
  33. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  34. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +56 -54
  35. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  36. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1258 -1176
  37. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +9415 -5
  38. psychopy/app/pavlovia_ui/_base.py +33 -3
  39. psychopy/app/pavlovia_ui/search.py +0 -1
  40. psychopy/app/plugin_manager/dialog.py +104 -51
  41. psychopy/app/plugin_manager/packages.py +5 -0
  42. psychopy/app/plugin_manager/plugins.py +152 -67
  43. psychopy/app/preferencesDlg.py +8 -8
  44. psychopy/app/psychopyApp.py +11 -5
  45. psychopy/app/ribbon.py +124 -14
  46. psychopy/app/runner/runner.py +6 -1
  47. psychopy/app/stdout/stdOutRich.py +27 -11
  48. psychopy/app/themes/icons.py +52 -2
  49. psychopy/assets/__init__.py +0 -0
  50. psychopy/assets/click.png +0 -0
  51. psychopy/assets/clicknext.png +0 -0
  52. psychopy/assets/next.png +0 -0
  53. psychopy/assets/psychopy.ico +0 -0
  54. psychopy/assets/psychopy.png +0 -0
  55. psychopy/assets/templates/__init__.py +0 -0
  56. psychopy/assets/touch.png +0 -0
  57. psychopy/assets/touchnext.png +0 -0
  58. psychopy/assets/window.ico +0 -0
  59. psychopy/changes/2023.1.0.md +9 -0
  60. psychopy/changes/2024.1.0.md +16 -0
  61. psychopy/changes/__init__.py +0 -0
  62. psychopy/clock.py +2 -2
  63. psychopy/colors.py +2 -1
  64. psychopy/compatibility.py +53 -1
  65. psychopy/contrib/.DS_Store +0 -0
  66. psychopy/contrib/configobj/__init__.py +10 -8
  67. psychopy/data/__init__.py +3 -2
  68. psychopy/data/base.py +5 -5
  69. psychopy/data/experiment.py +130 -4
  70. psychopy/data/routine.py +56 -0
  71. psychopy/data/staircase.py +2 -2
  72. psychopy/data/trial.py +559 -97
  73. psychopy/data/utils.py +56 -21
  74. psychopy/demos/.DS_Store +0 -0
  75. psychopy/demos/builder/.DS_Store +0 -0
  76. psychopy/demos/builder/Design Templates/.DS_Store +0 -0
  77. psychopy/demos/builder/Experiments/.DS_Store +0 -0
  78. psychopy/demos/builder/Feature Demos/.DS_Store +0 -0
  79. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +375 -0
  80. psychopy/demos/builder/Feature Demos/buttonBox/readme.md +5 -0
  81. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +433 -0
  82. psychopy/demos/builder/Feature Demos/pilotMode/readme.md +7 -0
  83. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +4 -4
  84. psychopy/demos/builder/Hardware/.DS_Store +0 -0
  85. psychopy/demos/builder/Helper Tools/.DS_Store +0 -0
  86. psychopy/demos/coder/.DS_Store +0 -0
  87. psychopy/demos/coder/hardware/testSoundLatency.py +2 -2
  88. psychopy/demos/coder/iohub/.DS_Store +0 -0
  89. psychopy/demos/coder/misc/hdf5_2_csv +33 -0
  90. psychopy/event.py +30 -29
  91. psychopy/experiment/.DS_Store +0 -0
  92. psychopy/experiment/_experiment.py +6 -6
  93. psychopy/experiment/components/.DS_Store +0 -0
  94. psychopy/experiment/components/__init__.py +6 -3
  95. psychopy/experiment/components/_base.py +286 -131
  96. psychopy/experiment/components/aperture/.DS_Store +0 -0
  97. psychopy/experiment/components/brush/.DS_Store +0 -0
  98. psychopy/experiment/components/button/.DS_Store +0 -0
  99. psychopy/experiment/components/button/__init__.py +5 -1
  100. psychopy/experiment/components/buttonBox/.DS_Store +0 -0
  101. psychopy/experiment/components/buttonBox/__init__.py +21 -12
  102. psychopy/experiment/components/camera/.DS_Store +0 -0
  103. psychopy/experiment/components/code/.DS_Store +0 -0
  104. psychopy/experiment/components/dots/.DS_Store +0 -0
  105. psychopy/experiment/components/eyetracker_record/.DS_Store +0 -0
  106. psychopy/experiment/components/eyetracker_record/__init__.py +92 -30
  107. psychopy/experiment/components/form/.DS_Store +0 -0
  108. psychopy/experiment/components/form/__init__.py +6 -2
  109. psychopy/experiment/components/grating/.DS_Store +0 -0
  110. psychopy/experiment/components/grating/__init__.py +14 -3
  111. psychopy/experiment/components/image/.DS_Store +0 -0
  112. psychopy/experiment/components/image/__init__.py +14 -3
  113. psychopy/experiment/components/joyButtons/.DS_Store +0 -0
  114. psychopy/experiment/components/joystick/.DS_Store +0 -0
  115. psychopy/experiment/components/keyboard/.DS_Store +0 -0
  116. psychopy/experiment/components/keyboard/__init__.py +22 -10
  117. psychopy/experiment/components/microphone/.DS_Store +0 -0
  118. psychopy/experiment/components/microphone/__init__.py +59 -39
  119. psychopy/experiment/components/mouse/.DS_Store +0 -0
  120. psychopy/experiment/components/mouse/__init__.py +44 -29
  121. psychopy/experiment/components/movie/.DS_Store +0 -0
  122. psychopy/experiment/components/movie/__init__.py +1 -1
  123. psychopy/experiment/components/panorama/.DS_Store +0 -0
  124. psychopy/experiment/components/parallelOut/.DS_Store +0 -0
  125. psychopy/experiment/components/patch/.DS_Store +0 -0
  126. psychopy/experiment/components/polygon/.DS_Store +0 -0
  127. psychopy/experiment/components/polygon/__init__.py +26 -6
  128. psychopy/experiment/components/progress/.DS_Store +0 -0
  129. psychopy/experiment/components/progress/__init__.py +1 -1
  130. psychopy/experiment/components/ratingScale/.DS_Store +0 -0
  131. psychopy/experiment/components/resourceManager/.DS_Store +0 -0
  132. psychopy/experiment/components/roi/.DS_Store +0 -0
  133. psychopy/experiment/components/roi/__init__.py +5 -0
  134. psychopy/experiment/components/routineSettings/.DS_Store +0 -0
  135. psychopy/experiment/components/routineSettings/__init__.py +57 -10
  136. psychopy/experiment/components/serialOut/.DS_Store +0 -0
  137. psychopy/experiment/components/settings/.DS_Store +0 -0
  138. psychopy/experiment/components/settings/__init__.py +117 -42
  139. psychopy/experiment/components/slider/.DS_Store +0 -0
  140. psychopy/experiment/components/sound/.DS_Store +0 -0
  141. psychopy/experiment/components/sound/__init__.py +54 -19
  142. psychopy/experiment/components/static/.DS_Store +0 -0
  143. psychopy/experiment/components/static/__init__.py +1 -1
  144. psychopy/experiment/components/text/.DS_Store +0 -0
  145. psychopy/experiment/components/text/__init__.py +28 -3
  146. psychopy/experiment/components/textbox/.DS_Store +0 -0
  147. psychopy/experiment/components/textbox/__init__.py +12 -2
  148. psychopy/experiment/components/unknown/.DS_Store +0 -0
  149. psychopy/experiment/components/unknown/__init__.py +1 -2
  150. psychopy/experiment/components/unknownPlugin/.DS_Store +0 -0
  151. psychopy/experiment/components/unknownPlugin/__init__.py +2 -2
  152. psychopy/experiment/components/variable/.DS_Store +0 -0
  153. psychopy/experiment/flow.py +11 -4
  154. psychopy/experiment/loops.py +85 -37
  155. psychopy/experiment/params.py +74 -32
  156. psychopy/experiment/py2js_transpiler.py +8 -1
  157. psychopy/experiment/routines/.DS_Store +0 -0
  158. psychopy/experiment/routines/_base.py +102 -22
  159. psychopy/experiment/routines/counterbalance/.DS_Store +0 -0
  160. psychopy/experiment/routines/counterbalance/__init__.py +5 -1
  161. psychopy/experiment/routines/eyetracker_calibrate/.DS_Store +0 -0
  162. psychopy/experiment/routines/eyetracker_validate/.DS_Store +0 -0
  163. psychopy/experiment/routines/pavlovia_survey/.DS_Store +0 -0
  164. psychopy/experiment/routines/photodiodeValidator/.DS_Store +0 -0
  165. psychopy/experiment/routines/photodiodeValidator/__init__.py +7 -6
  166. psychopy/experiment/routines/unknown/.DS_Store +0 -0
  167. psychopy/gui/wxgui.py +4 -4
  168. psychopy/hardware/.DS_Store +0 -0
  169. psychopy/hardware/__init__.py +1 -1
  170. psychopy/hardware/base.py +12 -0
  171. psychopy/hardware/camera/__init__.py +1 -15
  172. psychopy/hardware/cedrus.py +10 -11
  173. psychopy/hardware/crs/colorcal.py +13 -22
  174. psychopy/hardware/crs/optical.py +10 -20
  175. psychopy/hardware/emulator.py +17 -14
  176. psychopy/hardware/eyetracker.py +42 -118
  177. psychopy/hardware/gammasci.py +4 -15
  178. psychopy/hardware/keyboard.py +102 -11
  179. psychopy/hardware/listener.py +3 -0
  180. psychopy/hardware/microphone.py +148 -18
  181. psychopy/hardware/minolta.py +8 -15
  182. psychopy/hardware/photodiode.py +191 -16
  183. psychopy/hardware/photometer/__init__.py +11 -19
  184. psychopy/hardware/pr.py +8 -15
  185. psychopy/hardware/speaker.py +39 -4
  186. psychopy/info.py +0 -71
  187. psychopy/iohub/.DS_Store +0 -0
  188. psychopy/iohub/__init__.py +1 -1
  189. psychopy/iohub/client/__init__.py +30 -20
  190. psychopy/iohub/client/keyboard.py +24 -24
  191. psychopy/iohub/datastore/__init__.py +2 -2
  192. psychopy/iohub/datastore/util.py +2 -2
  193. psychopy/iohub/default_config.yaml +1 -1
  194. psychopy/iohub/devices/.DS_Store +0 -0
  195. psychopy/iohub/devices/__init__.py +112 -25
  196. psychopy/iohub/devices/deviceConfigValidation.py +2 -1
  197. psychopy/iohub/devices/experiment/default_experiment.yaml +12 -1
  198. psychopy/iohub/devices/experiment/supported_config_settings.yaml +5 -1
  199. psychopy/iohub/devices/eyetracker/.DS_Store +0 -0
  200. psychopy/iohub/devices/eyetracker/__init__.py +46 -0
  201. psychopy/iohub/devices/eyetracker/calibration/procedure.py +2 -2
  202. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +14 -2
  203. psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +3 -4
  204. psychopy/iohub/server.py +2 -2
  205. psychopy/iohub/start_iohub_process.py +3 -0
  206. psychopy/iohub/util/__init__.py +62 -70
  207. psychopy/layout.py +5 -5
  208. psychopy/logging.py +8 -1
  209. psychopy/microphone.py +10 -37
  210. psychopy/platform_specific/__init__.py +0 -2
  211. psychopy/platform_specific/darwin.py +1 -3
  212. psychopy/platform_specific/linux.py +31 -33
  213. psychopy/platform_specific/win32.py +38 -13
  214. psychopy/plugins/__init__.py +148 -116
  215. psychopy/plugins/util.py +39 -0
  216. psychopy/preferences/Darwin.spec +4 -2
  217. psychopy/preferences/FreeBSD.spec +4 -2
  218. psychopy/preferences/Linux.spec +4 -2
  219. psychopy/preferences/Windows.spec +4 -2
  220. psychopy/preferences/baseNoArch.spec +4 -2
  221. psychopy/preferences/preferences.py +47 -24
  222. psychopy/projects/pavlovia.py +47 -4
  223. psychopy/scripts/psyexpCompile.py +0 -4
  224. psychopy/session.py +153 -21
  225. psychopy/sound/__init__.py +31 -21
  226. psychopy/sound/_base.py +20 -3
  227. psychopy/sound/audioclip.py +320 -33
  228. psychopy/sound/backend_ptb.py +47 -58
  229. psychopy/sound/backend_pygame.py +1 -1
  230. psychopy/sound/backend_pysound.py +6 -15
  231. psychopy/sound/transcribe.py +53 -0
  232. psychopy/tests/.DS_Store +0 -0
  233. psychopy/tests/data/.DS_Store +0 -0
  234. psychopy/tests/data/TestUnknownPluginComponent_load_resave.psyexp +135 -0
  235. psychopy/tests/data/Test_textbox/test_ori_0_bottom right.png +0 -0
  236. psychopy/tests/data/Test_textbox/test_ori_0_center.png +0 -0
  237. psychopy/tests/data/Test_textbox/test_ori_0_top left.png +0 -0
  238. psychopy/tests/data/Test_textbox/test_ori_120_bottom right.png +0 -0
  239. psychopy/tests/data/Test_textbox/test_ori_120_center.png +0 -0
  240. psychopy/tests/data/Test_textbox/test_ori_120_top left.png +0 -0
  241. psychopy/tests/data/Test_textbox/test_ori_180_bottom right.png +0 -0
  242. psychopy/tests/data/Test_textbox/test_ori_180_center.png +0 -0
  243. psychopy/tests/data/Test_textbox/test_ori_180_top left.png +0 -0
  244. psychopy/tests/data/Test_textbox/test_ori_240_bottom right.png +0 -0
  245. psychopy/tests/data/Test_textbox/test_ori_240_center.png +0 -0
  246. psychopy/tests/data/Test_textbox/test_ori_240_top left.png +0 -0
  247. psychopy/tests/data/correctScript/.DS_Store +0 -0
  248. psychopy/tests/data/test_components/testClearKeyboard/testClearKeyboard.psyexp +200 -0
  249. psychopy/tests/data/test_session/.DS_Store +0 -0
  250. psychopy/tests/data/test_session/root/testFutureTrials/testFutureTrials.psyexp +155 -0
  251. psychopy/tests/data/test_session/root/testTrialNav/trialNav.psyexp +158 -0
  252. psychopy/tests/test_app/.DS_Store +0 -0
  253. psychopy/tests/test_app/conftest.py +2 -2
  254. psychopy/tests/test_app/test_speed.py +4 -1
  255. psychopy/tests/test_data/test_TrialHandler2.py +146 -1
  256. psychopy/tests/test_experiment/.DS_Store +0 -0
  257. psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +3 -3
  258. psychopy/tests/test_experiment/needs_wx/test_components.py +2 -2
  259. psychopy/tests/test_experiment/test_components/test_KeyboardComponent.py +28 -0
  260. psychopy/tests/test_experiment/test_components/test_UnknownPluginComponent.py +27 -0
  261. psychopy/tests/test_experiment/test_components/test_base_components.py +58 -0
  262. psychopy/tests/test_experiment/test_py2js.py +1 -1
  263. psychopy/tests/test_hardware/test_keyboard.py +184 -16
  264. psychopy/tests/test_hardware/test_ports.py +1 -11
  265. psychopy/tests/test_liaison/test_Liaison.py +47 -0
  266. psychopy/tests/test_misc/test_core.py +5 -0
  267. psychopy/tests/test_session/test_Session.py +5 -1
  268. psychopy/tests/test_tools/test_versionchooser.py +39 -8
  269. psychopy/tests/test_visual/test_all_stimuli.py +0 -97
  270. psychopy/tests/test_visual/test_image.py +6 -5
  271. psychopy/tests/test_visual/test_textbox.py +36 -0
  272. psychopy/tests/utils.py +4 -0
  273. psychopy/tools/filetools.py +1 -1
  274. psychopy/tools/pkgtools.py +160 -137
  275. psychopy/tools/versionchooser.py +10 -10
  276. psychopy/tools/wizard.py +3 -3
  277. psychopy/visual/.DS_Store +0 -0
  278. psychopy/visual/backends/pygletbackend.py +24 -13
  279. psychopy/visual/basevisual.py +5 -11
  280. psychopy/visual/button.py +2 -14
  281. psychopy/visual/helpers.py +5 -5
  282. psychopy/visual/line.py +1 -2
  283. psychopy/visual/movie2.py +7 -816
  284. psychopy/visual/movie3.py +7 -589
  285. psychopy/visual/movies/__init__.py +8 -11
  286. psychopy/visual/movies/frame.py +5 -2
  287. psychopy/visual/movies/players/ffpyplayer_player.py +5 -2
  288. psychopy/visual/noise.py +8 -7
  289. psychopy/visual/patch.py +7 -16
  290. psychopy/visual/progress.py +1 -1
  291. psychopy/visual/radial.py +9 -7
  292. psychopy/visual/ratingscale.py +8 -1415
  293. psychopy/visual/secondorder.py +10 -9
  294. psychopy/visual/shape.py +7 -2
  295. psychopy/visual/text.py +1 -1
  296. psychopy/visual/textbox2/textbox2.py +28 -5
  297. psychopy/web.py +5 -2
  298. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/METADATA +8 -13
  299. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/RECORD +313 -219
  300. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/WHEEL +1 -1
  301. psychopy/app/Resources/click.png +0 -0
  302. psychopy/app/Resources/next.png +0 -0
  303. psychopy/experiment/components/patch/__init__.py +0 -121
  304. psychopy/experiment/components/patch/classic/patch.png +0 -0
  305. psychopy/experiment/components/patch/dark/patch.png +0 -0
  306. psychopy/experiment/components/patch/dark/patch@2x.png +0 -0
  307. psychopy/experiment/components/patch/light/patch.png +0 -0
  308. psychopy/experiment/components/patch/light/patch@2x.png +0 -0
  309. psychopy/experiment/components/ratingScale/__init__.py +0 -337
  310. psychopy/experiment/components/ratingScale/classic/ratingscale.png +0 -0
  311. psychopy/experiment/components/ratingScale/classic/ratingscale@2x.png +0 -0
  312. psychopy/experiment/components/ratingScale/dark/ratingScale@2x.png +0 -0
  313. psychopy/experiment/components/ratingScale/dark/ratingscale.png +0 -0
  314. psychopy/experiment/components/ratingScale/light/ratingScale@2x.png +0 -0
  315. psychopy/experiment/components/ratingScale/light/ratingscale.png +0 -0
  316. psychopy/platform_specific/posix.py +0 -16
  317. psychopy/tests/test_sound/test_microphone.py +0 -217
  318. psychopy/tests/test_visual/test_ratingScale.py +0 -299
  319. /psychopy/{app/Resources → assets}/Psychopy Window Favicon@16w.png +0 -0
  320. /psychopy/{app/Resources → assets}/Psychopy Window Favicon@32w.png +0 -0
  321. /psychopy/{app/Resources → assets}/USB-C.png +0 -0
  322. /psychopy/{app/Resources → assets}/USB.png +0 -0
  323. /psychopy/{app/Resources → assets}/creditCard.png +0 -0
  324. /psychopy/{app/Resources → assets}/default.mp3 +0 -0
  325. /psychopy/{app/Resources → assets}/default.mp4 +0 -0
  326. /psychopy/{app/Resources → assets}/default.png +0 -0
  327. /psychopy/{app/Resources → assets/templates}/instruct1.png +0 -0
  328. /psychopy/{app/Resources → assets/templates}/instruct2.png +0 -0
  329. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/entry_points.txt +0 -0
  330. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/AUTHORS.md +0 -0
  331. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -300,6 +300,8 @@ class BaseComponent:
300
300
  loop = currLoop.params['name']
301
301
  name = self.params['name']
302
302
 
303
+ # NOTE: this function does not write any code right now!
304
+
303
305
  def writeRoutineEndCodeJS(self, buff):
304
306
  """Write the code that will be called at the end of
305
307
  a routine (e.g. to save data)
@@ -312,7 +314,7 @@ class BaseComponent:
312
314
  """
313
315
  pass
314
316
 
315
- def writeStartTestCode(self, buff):
317
+ def writeStartTestCode(self, buff, extra=""):
316
318
  """
317
319
  Test whether we need to start (if there is a start time at all)
318
320
 
@@ -326,50 +328,76 @@ class BaseComponent:
326
328
  buff.writeIndentedLines(code % self.params)
327
329
  buff.setIndentLevel(-indented, relative=True)
328
330
  ```
331
+
332
+ Parameters
333
+ ----------
334
+ buff : io.StringIO
335
+ Text buffer to write code to.
336
+ extra : str
337
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
338
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
339
+ string.
329
340
  """
341
+ # create copy of params dict so we can change stuff without harm
342
+ params = self.params.copy()
330
343
 
331
344
  # Get starting indent level
332
345
  startIndent = buff.indentLevel
333
346
 
334
- if self.params['startVal'].val in ('', None, -1, 'None'):
335
- # If we have no start time, don't write start code
336
- return buff.indentLevel - startIndent
347
+ if params['startVal'].val in ('', None, -1, 'None'):
348
+ if extra:
349
+ # if we have extra and no stop condition, extra is the only stop condition
350
+ params['startType'] = params['startType'].copy()
351
+ params['startVal'] = params['startVal'].copy()
352
+ params['startType'].val = "condition"
353
+ params['startVal'].val = "False"
354
+ else:
355
+ # if we just have no stop time, don't write stop code
356
+ return buff.indentLevel - startIndent
337
357
 
338
358
  # Newline
339
359
  buff.writeIndentedLines("\n")
340
360
 
341
- if self.params['syncScreenRefresh']:
361
+ if params['syncScreenRefresh']:
342
362
  tCompare = 'tThisFlip'
343
363
  else:
344
364
  tCompare = 't'
345
- params = self.params
346
365
  t = tCompare
347
366
  # add handy comment
348
367
  code = (
349
368
  "# if %(name)s is starting this frame...\n"
350
369
  )
351
370
  buff.writeIndentedLines(code % params)
352
- if self.params['startType'].val == 'time (s)':
371
+ # add starting if statement
372
+ if params['startType'].val == 'time (s)':
353
373
  # if startVal is an empty string then set to be 0.0
354
- if (isinstance(self.params['startVal'].val, str) and
355
- not self.params['startVal'].val.strip()):
356
- self.params['startVal'].val = '0.0'
357
- code = (f"if {params['name']}.status == NOT_STARTED and "
358
- f"{t} >= {params['startVal']}-frameTolerance:\n")
359
- elif self.params['startType'].val == 'frame N':
360
- code = (f"if {params['name']}.status == NOT_STARTED and "
361
- f"frameN >= {params['startVal']}:\n")
362
- elif self.params['startType'].val == 'condition':
363
- code = (f"if {params['name']}.status == NOT_STARTED and "
364
- f"{params['startVal']}:\n")
374
+ if (
375
+ isinstance(params['startVal'].val, str)
376
+ and not params['startVal'].val.strip()
377
+ ):
378
+ params['startVal'].val = '0.0'
379
+ code = (
380
+ "if %(name)s.status == NOT_STARTED and {} >= %(startVal)s-frameTolerance"
381
+ ).format(t)
382
+ elif params['startType'].val == 'frame N':
383
+ code = (
384
+ "if %(name)s.status == NOT_STARTED and frameN >= %(startVal)s"
385
+ )
386
+ elif params['startType'].val == 'condition':
387
+ code = (
388
+ "if %(name)s.status == NOT_STARTED and %(startVal)s"
389
+ )
365
390
  else:
366
- msg = f"Not a known startType ({params['startVal']}) for {params['name']}"
367
- raise CodeGenerationException(msg % self.params)
368
-
369
- buff.writeIndented(code)
370
-
391
+ msg = f"Not a known startType (%(startVal)s) for %(name)s"
392
+ raise CodeGenerationException(msg % params)
393
+ # add any other conditions and finish the statement
394
+ if extra and not extra.startswith(" "):
395
+ extra = " " + extra
396
+ code += f"{extra}:\n"
397
+ # write if statement and indent
398
+ buff.writeIndentedLines(code % params)
371
399
  buff.setIndentLevel(+1, relative=True)
372
- params = self.params
400
+
373
401
  code = (f"# keep track of start time/frame for later\n"
374
402
  f"{params['name']}.frameNStart = frameN # exact frame index\n"
375
403
  f"{params['name']}.tStart = t # local t and not account for scr refresh\n"
@@ -380,12 +408,12 @@ class BaseComponent:
380
408
  # on the *expected* time of the flip
381
409
  code += (f"win.timeOnFlip({params['name']}, 'tStartRefresh')"
382
410
  f" # time at next scr refresh\n")
383
- if self.params['saveStartStop']:
411
+ if params['saveStartStop']:
384
412
  code += f"# add timestamp to datafile\n"
385
- if self.type=='Sound' and self.params['syncScreenRefresh']:
413
+ if self.type=='Sound' and params['syncScreenRefresh']:
386
414
  # use the time we *expect* the flip
387
415
  code += f"thisExp.addData('{params['name']}.started', tThisFlipGlobal)\n"
388
- elif 'syncScreenRefresh' in self.params and self.params['syncScreenRefresh']:
416
+ elif 'syncScreenRefresh' in params and params['syncScreenRefresh']:
389
417
  # use the time we *detect* the flip (in the future)
390
418
  code += f"thisExp.timestampOnFlip(win, '{params['name']}.started')\n"
391
419
  else:
@@ -406,42 +434,69 @@ class BaseComponent:
406
434
  "# update status\n"
407
435
  "%(name)s.status = STARTED\n"
408
436
  )
409
- buff.writeIndentedLines(code % self.params)
437
+ buff.writeIndentedLines(code % params)
410
438
 
411
439
  # Return True if start test was written
412
440
  return buff.indentLevel - startIndent
413
441
 
414
- def writeStartTestCodeJS(self, buff):
442
+ def writeStartTestCodeJS(self, buff, extra=""):
415
443
  """Test whether we need to start
444
+
445
+ Parameters
446
+ ----------
447
+ buff : io.StringIO
448
+ Text buffer to write code to.
449
+ extra : str
450
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
451
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
452
+ string.
416
453
  """
454
+ # create copy of params dict so we can change stuff without harm
455
+ params = self.params.copy()
456
+
417
457
  # Get starting indent level
418
458
  startIndent = buff.indentLevel
419
459
 
420
- if self.params['startVal'].val in ('', None, -1, 'None'):
421
- # If we have no start time, don't write start code
422
- return buff.indentLevel - startIndent
460
+ if params['startVal'].val in ('', None, -1, 'None'):
461
+ if extra:
462
+ # if we have extra and no stop condition, extra is the only stop condition
463
+ params['startType'] = params['startType'].copy()
464
+ params['startVal'] = params['startVal'].copy()
465
+ params['startType'].val = "condition"
466
+ params['startVal'].val = "False"
467
+ else:
468
+ # if we just have no stop time, don't write stop code
469
+ return buff.indentLevel - startIndent
423
470
 
424
- params = self.params
425
- if self.params['startType'].val == 'time (s)':
471
+ if params['startType'].val == 'time (s)':
426
472
  # if startVal is an empty string then set to be 0.0
427
- if (isinstance(self.params['startVal'].val, str) and
428
- not self.params['startVal'].val.strip()):
429
- self.params['startVal'].val = '0.0'
430
- code = (f"if (t >= {params['startVal']} "
431
- f"&& {params['name']}.status === PsychoJS.Status.NOT_STARTED) {{\n")
432
- elif self.params['startType'].val == 'frame N':
433
- code = (f"if (frameN >= {params['startVal']} "
434
- f"&& {params['name']}.status === PsychoJS.Status.NOT_STARTED) {{\n")
435
- elif self.params['startType'].val == 'condition':
436
- code = (f"if (({params['startVal']}) "
437
- f"&& {params['name']}.status === PsychoJS.Status.NOT_STARTED) {{\n")
473
+ if (
474
+ isinstance(params['startVal'].val, str)
475
+ and not params['startVal'].val.strip()
476
+ ):
477
+ params['startVal'].val = '0.0'
478
+ code = (
479
+ "if (t >= %(startVal)s && %(name)s.status === PsychoJS.Status.NOT_STARTED"
480
+ )
481
+ elif params['startType'].val == 'frame N':
482
+ code = (
483
+ "if (frameN >= %(startVal)s && %(name)s.status === PsychoJS.Status.NOT_STARTED"
484
+ )
485
+ elif params['startType'].val == 'condition':
486
+ code = (
487
+ "if ((%(startVal)s) && %(name)s.status === PsychoJS.Status.NOT_STARTED"
488
+ )
438
489
  else:
439
- msg = f"Not a known startType ({params['startVal']}) for {params['name']}"
490
+ msg = f"Not a known startType (%(startVal)s) for %(name)s"
440
491
  raise CodeGenerationException(msg)
441
-
442
- buff.writeIndented(code)
443
-
492
+ # add any other conditions and finish the statement
493
+ if extra and not extra.startswith(" "):
494
+ extra = " " + extra
495
+ code += f"{extra}) {{\n"
496
+ # write if statement and indent
497
+ buff.writeIndentedLines(code % params)
444
498
  buff.setIndentLevel(+1, relative=True)
499
+
445
500
  code = (f"// keep track of start time/frame for later\n"
446
501
  f"{params['name']}.tStart = t; // (not accounting for frame time here)\n"
447
502
  f"{params['name']}.frameNStart = frameN; // exact frame index\n\n")
@@ -450,7 +505,7 @@ class BaseComponent:
450
505
  # Return True if start test was written
451
506
  return buff.indentLevel - startIndent
452
507
 
453
- def writeStopTestCode(self, buff):
508
+ def writeStopTestCode(self, buff, extra=""):
454
509
  """
455
510
  Test whether we need to stop (if there is a stop time at all)
456
511
 
@@ -461,22 +516,39 @@ class BaseComponent:
461
516
  code = (
462
517
  "%(name)s.attribute = value\n"
463
518
  )
464
- buff.writeIndentedLines(code % self.params)
519
+ buff.writeIndentedLines(code % params)
465
520
  buff.setIndentLevel(-indented, relative=True)
466
521
  ```
522
+
523
+ Parameters
524
+ ----------
525
+ buff : io.StringIO
526
+ Text buffer to write code to.
527
+ extra : str
528
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
529
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
530
+ string.
467
531
  """
532
+ # create copy of params dict so we can change stuff without harm
533
+ params = self.params.copy()
468
534
 
469
535
  # Get starting indent level
470
536
  startIndent = buff.indentLevel
471
537
 
472
- if self.params['stopVal'].val in ('', None, -1, 'None'):
473
- # If we have no stop time, don't write stop code
474
- return buff.indentLevel - startIndent
538
+ if params['stopVal'].val in ('', None, -1, 'None'):
539
+ if extra:
540
+ # if we have extra and no stop condition, extra is the only stop condition
541
+ params['stopType'] = params['stopType'].copy()
542
+ params['stopVal'] = params['stopVal'].copy()
543
+ params['stopType'].val = "condition"
544
+ params['stopVal'].val = "False"
545
+ else:
546
+ # if we just have no stop time, don't write stop code
547
+ return buff.indentLevel - startIndent
475
548
 
476
549
  # Newline
477
550
  buff.writeIndentedLines("\n")
478
551
 
479
- params = self.params
480
552
  # add handy comment
481
553
  code = (
482
554
  "# if %(name)s is stopping this frame...\n"
@@ -487,39 +559,54 @@ class BaseComponent:
487
559
  buff.setIndentLevel(+1, relative=True)
488
560
 
489
561
  # If start time is blank ad stop is a duration, raise alert
490
- if self.params['stopType'] in ('duration (s)', 'duration (frames)'):
491
- if ('startVal' not in self.params) or (self.params['startVal'] in ("", "None", None)):
492
- alerttools.alert(4120, strFields={'component': self.params['name']})
493
-
494
- if self.params['stopType'].val == 'time (s)':
495
- code = (f"# is it time to stop? (based on local clock)\n"
496
- f"if tThisFlip > {params['stopVal']}-frameTolerance:\n"
497
- )
562
+ if params['stopType'] in ('duration (s)', 'duration (frames)'):
563
+ if ('startVal' not in params) or (params['startVal'] in ("", "None", None)):
564
+ alerttools.alert(4120, strFields={'component': params['name']})
565
+
566
+ if params['stopType'].val == 'time (s)':
567
+ code = (
568
+ "# is it time to stop? (based on local clock)\n"
569
+ "if tThisFlip > %(stopVal)s-frameTolerance"
570
+ )
498
571
  # duration in time (s)
499
- elif (self.params['stopType'].val == 'duration (s)'):
500
- code = (f"# is it time to stop? (based on global clock, using actual start)\n"
501
- f"if tThisFlipGlobal > {params['name']}.tStartRefresh + {params['stopVal']}-frameTolerance:\n")
502
- elif self.params['stopType'].val == 'duration (frames)':
503
- code = (f"if frameN >= ({params['name']}.frameNStart + {params['stopVal']}):\n")
504
- elif self.params['stopType'].val == 'frame N':
505
- code = f"if frameN >= {params['stopVal']}:\n"
506
- elif self.params['stopType'].val == 'condition':
507
- code = f"if bool({params['stopVal']}):\n"
572
+ elif (params['stopType'].val == 'duration (s)'):
573
+ code = (
574
+ "# is it time to stop? (based on global clock, using actual start)\n"
575
+ "if tThisFlipGlobal > %(name)s.tStartRefresh + %(stopVal)s-frameTolerance"
576
+ )
577
+ elif params['stopType'].val == 'duration (frames)':
578
+ code = (
579
+ "if frameN >= (%(name)s.frameNStart + %(stopVal)s)"
580
+ )
581
+ elif params['stopType'].val == 'frame N':
582
+ code = (
583
+ "if frameN >= %(stopVal)s"
584
+ )
585
+ elif params['stopType'].val == 'condition':
586
+ code = (
587
+ "if bool(%(stopVal)s)"
588
+ )
508
589
  else:
509
- msg = (f"Didn't write any stop line for startType={params['startType']}, "
510
- f"stopType={params['stopType']}")
590
+ msg = (
591
+ "Didn't write any stop line for startType=%(startType)s, stopType=%(stopType)s"
592
+ )
511
593
  raise CodeGenerationException(msg)
512
-
513
- buff.writeIndentedLines(code)
594
+ # add any other conditions and finish the statement
595
+ if extra and not extra.startswith(" "):
596
+ extra = " " + extra
597
+ code += f"{extra}:\n"
598
+ # write if statement and indent
599
+ buff.writeIndentedLines(code % params)
514
600
  buff.setIndentLevel(+1, relative=True)
601
+
515
602
  code = (f"# keep track of stop time/frame for later\n"
516
603
  f"{params['name']}.tStop = t # not accounting for scr refresh\n"
517
604
  f"{params['name']}.tStopRefresh = tThisFlipGlobal # on global time\n"
518
605
  f"{params['name']}.frameNStop = frameN # exact frame index\n"
519
606
  )
520
- if self.params['saveStartStop']:
607
+ if params['saveStartStop']:
521
608
  code += f"# add timestamp to datafile\n"
522
- if 'syncScreenRefresh' in self.params and self.params['syncScreenRefresh']:
609
+ if 'syncScreenRefresh' in params and params['syncScreenRefresh']:
523
610
  # use the time we *detect* the flip (in the future)
524
611
  code += f"thisExp.timestampOnFlip(win, '{params['name']}.stopped')\n"
525
612
  else:
@@ -542,62 +629,94 @@ class BaseComponent:
542
629
  "# update status\n"
543
630
  "%(name)s.status = FINISHED\n"
544
631
  )
545
- buff.writeIndentedLines(code % self.params)
632
+ buff.writeIndentedLines(code % params)
546
633
 
547
634
  # Return True if stop test was written
548
635
  return buff.indentLevel - startIndent
549
636
 
550
- def writeStopTestCodeJS(self, buff):
637
+ def writeStopTestCodeJS(self, buff, extra=""):
551
638
  """Test whether we need to stop
639
+
640
+ Parameters
641
+ ----------
642
+ buff : io.StringIO
643
+ Text buffer to write code to.
644
+ extra : str
645
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
646
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
647
+ string.
552
648
  """
649
+ # create copy of params dict so we can change stuff without harm
650
+ params = self.params.copy()
651
+
553
652
  # Get starting indent level
554
653
  startIndent = buff.indentLevel
555
654
 
556
- if self.params['stopVal'].val in ('', None, -1, 'None'):
557
- # If we have no stop time, don't write stop code
558
- return buff.indentLevel - startIndent
655
+ if params['stopVal'].val in ('', None, -1, 'None'):
656
+ if extra:
657
+ # if we have extra and no stop time, extra is only stop condition
658
+ params['stopType'] = params['stopType'].copy()
659
+ params['stopVal'] = params['stopVal'].copy()
660
+ params['stopType'].val = "condition"
661
+ params['stopVal'].val = "false"
662
+ else:
663
+ # if we just have no stop time, don't write stop code
664
+ return buff.indentLevel - startIndent
559
665
 
560
- params = self.params
561
- if self.params['stopType'].val == 'time (s)':
562
- code = (f"frameRemains = {params['stopVal']} "
563
- f" - psychoJS.window.monitorFramePeriod * 0.75;"
564
- f" // most of one frame period left\n"
565
- f"if (({params['name']}.status === PsychoJS.Status.STARTED || {params['name']}.status === PsychoJS.Status.FINISHED) "
566
- f"&& t >= frameRemains) {{\n")
666
+ if params['stopType'].val == 'time (s)':
667
+ code = (
668
+ "frameRemains = %(stopVal)s - psychoJS.window.monitorFramePeriod * 0.75;"
669
+ "// most of one frame period left\n"
670
+ "if ((%(name)s.status === PsychoJS.Status.STARTED || %(name)s.status === "
671
+ "PsychoJS.Status.FINISHED) && t >= frameRemains"
672
+ )
567
673
  # duration in time (s)
568
- elif (self.params['stopType'].val == 'duration (s)' and
569
- self.params['startType'].val == 'time (s)'):
570
- code = (f"frameRemains = {params['startVal']} + {params['stopVal']}"
571
- f" - psychoJS.window.monitorFramePeriod * 0.75;"
572
- f" // most of one frame period left\n"
573
- f"if ({params['name']}.status === PsychoJS.Status.STARTED "
574
- f"&& t >= frameRemains) {{\n")
674
+ elif (
675
+ params['stopType'].val == 'duration (s)'
676
+ and params['startType'].val == 'time (s)'
677
+ ):
678
+ code = (
679
+ "frameRemains = %(startVal)s + %(stopVal)s - psychoJS.window.monitorFramePeriod "
680
+ "* 0.75;"
681
+ "// most of one frame period left\n"
682
+ "if (%(name)s.status === PsychoJS.Status.STARTED && t >= frameRemains"
683
+ )
575
684
  # start at frame and end with duratio (need to use approximate)
576
- elif self.params['stopType'].val == 'duration (s)':
577
- code = (f"if ({params['name']}.status === PsychoJS.Status.STARTED "
578
- f"&& t >= ({params['name']}.tStart + {params['stopVal']})) {{\n")
579
- elif self.params['stopType'].val == 'duration (frames)':
580
- code = (f"if ({params['name']}.status === PsychoJS.Status.STARTED "
581
- f"&& frameN >= ({params['name']}.frameNStart + {params['stopVal']})) {{\n")
582
- elif self.params['stopType'].val == 'frame N':
583
- code = (f"if ({params['name']}.status === PsychoJS.Status.STARTED "
584
- f"&& frameN >= {params['stopVal']}) {{\n")
585
- elif self.params['stopType'].val == 'condition':
586
- code = (f"if ({params['name']}.status === PsychoJS.Status.STARTED "
587
- f"&& Boolean({params['stopVal']})) {{\n")
685
+ elif params['stopType'].val == 'duration (s)':
686
+ code = (
687
+ "if (%(name)s.status === PsychoJS.Status.STARTED && t >= (%(name)s.tStart + "
688
+ "%(stopVal)s)"
689
+ )
690
+ elif params['stopType'].val == 'duration (frames)':
691
+ code = (
692
+ "if (%(name)s.status === PsychoJS.Status.STARTED && frameN >= "
693
+ "(%(name)s.frameNStart + %(stopVal)s)"
694
+ )
695
+ elif params['stopType'].val == 'frame N':
696
+ code = (
697
+ "if (%(name)s.status === PsychoJS.Status.STARTED && frameN >= %(stopVal)s"
698
+ )
699
+ elif params['stopType'].val == 'condition':
700
+ code = (
701
+ "if (%(name)s.status === PsychoJS.Status.STARTED && Boolean(%(stopVal)s)"
702
+ )
588
703
  else:
589
- msg = (f"Didn't write any stop line for startType="
590
- f"{params['startType']}, "
591
- f"stopType={params['stopType']}")
704
+ msg = (
705
+ "Didn't write any stop line for startType=%(startType)s, stopType=%(stopType)s"
706
+ )
592
707
  raise CodeGenerationException(msg)
593
-
594
- buff.writeIndentedLines(code)
708
+ # add any other conditions and finish the statement
709
+ if extra and not extra.startswith(" "):
710
+ extra = " " + extra
711
+ code += f"{extra}) {{\n"
712
+ # write if statement and indent
713
+ buff.writeIndentedLines(code % params)
595
714
  buff.setIndentLevel(+1, relative=True)
596
715
 
597
716
  # Return True if stop test was written
598
717
  return buff.indentLevel - startIndent
599
718
 
600
- def writeActiveTestCode(self, buff):
719
+ def writeActiveTestCode(self, buff, extra=""):
601
720
  """
602
721
  Test whether component is started and has not finished.
603
722
 
@@ -611,25 +730,41 @@ class BaseComponent:
611
730
  buff.writeIndentedLines(code % self.params)
612
731
  self.exitActiveTest(buff)
613
732
  ```
733
+
734
+ Parameters
735
+ ----------
736
+ buff : io.StringIO
737
+ Text buffer to write code to.
738
+ extra : str
739
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
740
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
741
+ string.
614
742
  """
743
+ # create copy of params dict so we can change stuff without harm
744
+ params = self.params.copy()
745
+
615
746
  # Newline
616
747
  buff.writeIndentedLines("\n")
617
748
  # Get starting indent level
618
749
  startIndent = buff.indentLevel
619
750
 
620
- # Write if statement
751
+ # construct if statement
621
752
  code = (
622
753
  "# if %(name)s is active this frame...\n"
623
- "if %(name)s.status == STARTED:\n"
754
+ "if %(name)s.status == STARTED"
624
755
  )
625
- buff.writeIndentedLines(code % self.params)
756
+ # add any other conditions and finish the statement
757
+ if extra and not extra.startswith(" "):
758
+ extra = " " + extra
759
+ code += f"{extra}:\n"
760
+ buff.writeIndentedLines(code % params)
626
761
  # Indent
627
762
  buff.setIndentLevel(+1, relative=True)
628
763
  # Write param updates (if needed)
629
764
  code = (
630
765
  "# update params\n"
631
766
  )
632
- buff.writeIndentedLines(code % self.params)
767
+ buff.writeIndentedLines(code % params)
633
768
  if self.checkNeedToUpdate('set every frame'):
634
769
  self.writeParamUpdates(buff, 'set every frame')
635
770
  else:
@@ -639,7 +774,7 @@ class BaseComponent:
639
774
  buff.writeIndentedLines(code)
640
775
  return buff.indentLevel - startIndent
641
776
 
642
- def writeActiveTestCodeJS(self, buff):
777
+ def writeActiveTestCodeJS(self, buff, extra=""):
643
778
  """
644
779
  Test whether component is started and has not finished.
645
780
 
@@ -652,20 +787,36 @@ class BaseComponent:
652
787
  )
653
788
  self.exitActiveTestJS(buff)
654
789
  ```
790
+
791
+ Parameters
792
+ ----------
793
+ buff : io.StringIO
794
+ Text buffer to write code to.
795
+ extra : str
796
+ Additional conditions to check, including any boolean operators (and, or, etc.). Use
797
+ `%(key)s` syntax to insert the values of any necessary params. Default is an empty
798
+ string.
655
799
  """
800
+ # create copy of params dict so we can change stuff without harm
801
+ params = self.params.copy()
802
+
656
803
  # Get starting indent level
657
804
  startIndent = buff.indentLevel
658
805
 
659
806
  # Newline
660
807
  buff.writeIndentedLines("\n")
661
808
 
662
- # Write if statement
809
+ # construct if statement
663
810
  code = (
664
811
  "// if %(name)s is active this frame...\n"
665
- "if (%(name)s.status == STARTED) {\n"
812
+ "if (%(name)s.status == STARTED"
666
813
  )
667
- buff.writeIndentedLines(code % self.params)
668
- # Indent
814
+ # add any other conditions and finish the statement
815
+ if extra and not extra.startswith(" "):
816
+ extra = " " + extra
817
+ code += f"{extra}) {{\n"
818
+ # write if statement and indent
819
+ buff.writeIndentedLines(code % params)
669
820
  buff.setIndentLevel(+1, relative=True)
670
821
 
671
822
  if self.checkNeedToUpdate('set every frame'):
@@ -673,7 +824,7 @@ class BaseComponent:
673
824
  code = (
674
825
  "// update params\n"
675
826
  )
676
- buff.writeIndentedLines(code % self.params)
827
+ buff.writeIndentedLines(code % params)
677
828
  self.writeParamUpdates(buff, 'set every frame')
678
829
 
679
830
  return buff.indentLevel - startIndent
@@ -821,11 +972,6 @@ class BaseComponent:
821
972
  elif paramName == 'lineColor':
822
973
  buff.writeIndented(f"{compName}.setLineColor(new util.Color({params['lineColor']})")
823
974
  buff.write(f"{loggingStr}){endStr}\n")
824
- elif paramName == 'sound':
825
- stopVal = params['stopVal']
826
- if stopVal in ['', None, -1, 'None']:
827
- stopVal = '-1'
828
- buff.writeIndented(f"{compName}.setSound({params['sound']}, secs={stopVal}){endStr}\n")
829
975
  elif paramName == 'emotiv_marker_label' or paramName == "emotiv_marker_value" or paramName == "emotiv_stop_marker":
830
976
  # This allows the eeg_marker to be updated by a code component or a conditions file
831
977
  # There is no setMarker_label or setMarker_value function in the eeg_marker object
@@ -886,7 +1032,7 @@ class BaseComponent:
886
1032
 
887
1033
  return duration, numericStop
888
1034
 
889
- def getStartAndDuration(self):
1035
+ def getStartAndDuration(self, params=None):
890
1036
  """Determine the start and duration of the stimulus
891
1037
  purely for Routine rendering purposes in the app (does not affect
892
1038
  actual drawing during the experiment)
@@ -895,7 +1041,16 @@ class BaseComponent:
895
1041
 
896
1042
  nonSlipSafe indicates that the component's duration is a known fixed
897
1043
  value and can be used in non-slip global clock timing (e.g for fMRI)
1044
+
1045
+ Parameters
1046
+ ----------
1047
+ params : dict[Param]
1048
+ Dict of params to use. If None, will use the values in `self.params`.
898
1049
  """
1050
+ # if not given any params, use from self
1051
+ if params is None:
1052
+ params = self.params
1053
+
899
1054
  # If has a start, calculate it
900
1055
  if 'startType' in self.params:
901
1056
  startTime, numericStart = self.getStart()