psychopy 2024.2.1__py3-none-any.whl → 2024.2.5__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 (252) hide show
  1. psychopy/.DS_Store +0 -0
  2. psychopy/CHANGELOG.txt +204 -0
  3. psychopy/GIT_SHA +1 -1
  4. psychopy/VERSION +1 -1
  5. psychopy/__init__.py +10 -1
  6. psychopy/__init__.py.orig +65 -0
  7. psychopy/app/.DS_Store +0 -0
  8. psychopy/app/Resources/.DS_Store +0 -0
  9. psychopy/app/_psychopyApp.py +11 -3
  10. psychopy/app/appData.spec +1 -1
  11. psychopy/app/builder/builder.py +1 -1
  12. psychopy/app/builder/builder.py.orig +3932 -0
  13. psychopy/app/builder/dialogs/__init__.py.orig +1679 -0
  14. psychopy/app/builder/dialogs/paramCtrls.py +1 -1
  15. psychopy/app/builder/dialogs/paramCtrls.py.orig +713 -0
  16. psychopy/app/colorpicker/__init__.py.orig +411 -0
  17. psychopy/app/cortex.log +0 -0
  18. psychopy/app/jobs.py +8 -1
  19. psychopy/app/locale/ar_001/.DS_Store +0 -0
  20. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +792 -1816
  21. psychopy/app/plugin_manager/dialog.py +9 -7
  22. psychopy/app/ribbon.py +2 -1
  23. psychopy/app/runner/runner.py +25 -13
  24. psychopy/clock.py +8 -4
  25. psychopy/core.py.orig +169 -0
  26. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +330 -0
  27. psychopy/demos/builder/Tools/.DS_Store +0 -0
  28. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  29. psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
  30. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +38 -0
  31. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +3418 -0
  32. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  33. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +2 -0
  34. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +15 -0
  35. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  36. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +562 -0
  37. psychopy/demos/coder/.DS_Store +0 -0
  38. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  39. psychopy/demos/coder/iohub/.iohpid +1 -0
  40. psychopy/demos/coder/iohub/eyetracking/.iohpid +1 -0
  41. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  42. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  43. psychopy/experiment/_experiment.py +32 -7
  44. psychopy/experiment/_experiment.py.orig +1032 -0
  45. psychopy/experiment/components/.DS_Store +0 -0
  46. psychopy/experiment/components/_base.py +13 -4
  47. psychopy/experiment/components/_base.py.orig +823 -0
  48. psychopy/experiment/components/form/.DS_Store +0 -0
  49. psychopy/experiment/components/microphone/__init__.py +10 -1
  50. psychopy/experiment/components/microphone/__init__.py.orig +490 -0
  51. psychopy/experiment/components/polygon/__init__.py +21 -22
  52. psychopy/experiment/components/settings/__init__.py +13 -14
  53. psychopy/experiment/components/settings/__init__.py.orig +1337 -0
  54. psychopy/experiment/components/textbox/__init__.py.orig +310 -0
  55. psychopy/experiment/components/webcam/.DS_Store +0 -0
  56. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  57. psychopy/experiment/flow.py +10 -8
  58. psychopy/experiment/loops.py.orig +829 -0
  59. psychopy/experiment/params.py +8 -3
  60. psychopy/experiment/params.py.orig +408 -0
  61. psychopy/experiment/routine.py.orig +503 -0
  62. psychopy/experiment/routines/_base.py +15 -6
  63. psychopy/experiment/routines/counterbalance/__init__.py +1 -0
  64. psychopy/gui/qtgui.py +14 -7
  65. psychopy/gui/util.py +10 -14
  66. psychopy/gui/wxgui.py +10 -4
  67. psychopy/hardware/.DS_Store +0 -0
  68. psychopy/hardware/brainproducts.py.orig +680 -0
  69. psychopy/hardware/iolab.py.orig +238 -0
  70. psychopy/hardware/manager.py +1 -1
  71. psychopy/hardware/photodiode.py +59 -27
  72. psychopy/hardware/speaker.py +4 -4
  73. psychopy/iohub/client/__init__.py +17 -0
  74. psychopy/iohub/client/keyboard.py +5 -0
  75. psychopy/iohub/datastore/__init__.py.orig +443 -0
  76. psychopy/iohub/datastore/util.py.orig +692 -0
  77. psychopy/iohub/devices/mouse/darwin.py.orig +427 -0
  78. psychopy/iohub/devices/mouse/linux2.py +4 -0
  79. psychopy/iohub/devices/mouse/linux2.py.orig +198 -0
  80. psychopy/iohub/devices/mouse/win32.py +5 -0
  81. psychopy/preferences/.DS_Store +0 -0
  82. psychopy/projects/pavlovia.py +10 -3
  83. psychopy/projects/pavlovia.py.orig +1295 -0
  84. psychopy/sound/backend_ptb.py +22 -5
  85. psychopy/sound/transcribe.py +24 -4
  86. psychopy/tests/.DS_Store +0 -0
  87. psychopy/tests/data/.DS_Store +0 -0
  88. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  89. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  90. psychopy/tests/data/aperture1_norm_local.png +0 -0
  91. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  92. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  93. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  94. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  95. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  96. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  97. psychopy/tests/data/blend_add_height_local.png +0 -0
  98. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  99. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  100. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  101. psychopy/tests/data/blend_add_norm_local.png +0 -0
  102. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  103. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  104. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  105. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  106. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  107. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  108. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  109. psychopy/tests/data/circleHex_height_local.png +0 -0
  110. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  111. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  112. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  113. psychopy/tests/data/circleHex_norm_local.png +0 -0
  114. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  115. psychopy/tests/data/color_comparison_local.png +0 -0
  116. psychopy/tests/data/correctScript/.DS_Store +0 -0
  117. psychopy/tests/data/dots_height_local.png +0 -0
  118. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  119. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  120. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  121. psychopy/tests/data/dots_norm_local.png +0 -0
  122. psychopy/tests/data/dots_stencil_local.png +0 -0
  123. psychopy/tests/data/elarray1_height_local.png +0 -0
  124. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  125. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  126. psychopy/tests/data/elarray1_norm_local.png +0 -0
  127. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  128. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  129. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  130. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  131. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  132. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  133. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  134. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  135. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  136. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  137. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  138. psychopy/tests/data/gabor1_height_local.png +0 -0
  139. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  140. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  141. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  142. psychopy/tests/data/gabor1_norm_local.png +0 -0
  143. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  144. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  145. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  146. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  147. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  148. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  149. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  150. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  151. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  152. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  153. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  154. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  155. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  156. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  157. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  158. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  159. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  160. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  161. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  162. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  163. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  164. psychopy/tests/data/numpyImage_height_local.png +0 -0
  165. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  166. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  167. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  168. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  169. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  170. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  171. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  172. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  173. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  174. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  175. psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
  176. psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
  177. psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
  178. psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
  179. psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
  180. psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
  181. psychopy/tests/data/text1_height_local.png +0 -0
  182. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  183. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  184. psychopy/tests/data/text1_norm_local.png +0 -0
  185. psychopy/tests/data/text1_stencil_local.png +0 -0
  186. psychopy/tests/data/wedge1_height_local.png +0 -0
  187. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  188. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  189. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  190. psychopy/tests/data/wedge1_norm_local.png +0 -0
  191. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  192. psychopy/tests/test_app/.DS_Store +0 -0
  193. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  194. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +177 -0
  195. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  196. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  197. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +168 -0
  198. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  199. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  200. psychopy/tests/test_data/.DS_Store +0 -0
  201. psychopy/tests/test_experiment/test_experiment.py +96 -0
  202. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +68 -0
  203. psychopy/tests/test_tools/test_arraytools.py +112 -0
  204. psychopy/tests/test_visual/test_image.py.orig +219 -0
  205. psychopy/tools/arraytools.py +47 -0
  206. psychopy/tools/versionchooser.py +1 -1
  207. psychopy/visual/backends/pygletbackend.py +26 -8
  208. psychopy/visual/basevisual.py.orig +1723 -0
  209. psychopy/visual/form.py.orig +1181 -0
  210. psychopy/visual/text.py.orig +752 -0
  211. psychopy/visual/textbox2/textbox2.py.orig +1315 -0
  212. psychopy/visual/window.py +13 -5
  213. psychopy/visual/windowwarp.py.orig +463 -0
  214. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/METADATA +9 -9
  215. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/RECORD +220 -84
  216. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/WHEEL +1 -1
  217. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/entry_points.txt +2 -0
  218. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  219. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  220. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  221. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  222. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  223. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  224. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  225. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  226. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  227. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  228. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  229. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  230. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  231. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  232. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  233. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  234. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  235. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  236. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  237. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  238. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  239. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  240. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  241. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  242. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  243. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  244. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  245. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  246. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  247. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  248. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  249. psychopy-2024.2.1.dist-info/licenses/AUTHORS.md +0 -138
  250. /psychopy/{app/locale/es_ES/LC_MESSAGE → demos/builder}/.DS_Store +0 -0
  251. /psychopy/{visual → demos/builder/Experiments}/.DS_Store +0 -0
  252. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/licenses/LICENSE +0 -0
@@ -101,3 +101,99 @@ class TestExperiment:
101
101
  else:
102
102
  assert case['value'] not in unhandledResources
103
103
 
104
+ def test_loaded_namespace(self):
105
+
106
+ exp = experiment.Experiment()
107
+ allRoutines = experiment.getAllStandaloneRoutines(fetchIcons=False)
108
+
109
+ """
110
+ Case structure
111
+ ==============
112
+ file : str
113
+ Experiment file to load
114
+ expectedSet : Set[str]
115
+ The expected names in the user namespace after routines are loaded and added
116
+ names : List[str]
117
+ Name of each routine to be added after experiment is loaded, paired with tags
118
+ tags : List[str]
119
+ Type of each routine to be added after experiment is loaded, paired with names
120
+ Can be 'CounterbalanceRoutine', 'EyetrackerCalibrationRoutine', 'EyetrackerValidationRoutine'
121
+ """
122
+
123
+ cases = [
124
+ {"file": "test_counterbalance.psyexp",
125
+ "expectedSet": {'trial', 'counterbalance', 'counterbalance_2', 'counterbalance_3',
126
+ 'counterbalance_4', 'counterbalance_5', 'calibration', 'calibration_2'},
127
+ "names": ['counterbalance', 'counterbalance', 'calibration', 'calibration'],
128
+ "tags": ['CounterbalanceRoutine', 'CounterbalanceRoutine', 'EyetrackerCalibrationRoutine',
129
+ 'EyetrackerCalibrationRoutine']},
130
+
131
+ {"file": "test_counterbalance.psyexp",
132
+ "expectedSet": {'trial', 'counterbalance', 'counterbalance_2', 'counterbalance_3',
133
+ 'calibration', 'counterbalance_4', 'calibration_2'},
134
+ "names": ['calibration', 'counterbalance', 'calibration'],
135
+ "tags": ['EyetrackerCalibrationRoutine', 'EyetrackerCalibrationRoutine', 'EyetrackerCalibrationRoutine']},
136
+
137
+ {"file": "test_custom_missing.psyexp",
138
+ "expectedSet": {'trial', 'custom_2', 'counterbalance_2', 'counterbalance',
139
+ 'counterbalance_3', 'calibration', 'calibration_2'},
140
+ "names": ['counterbalance', 'counterbalance', 'calibration', 'calibration'],
141
+ "tags": ['CounterbalanceRoutine', 'CounterbalanceRoutine', 'EyetrackerCalibrationRoutine',
142
+ 'EyetrackerCalibrationRoutine']},
143
+
144
+ {"file": "test_missing_counterbalance.psyexp",
145
+ "expectedSet": {'trial', 'counterbalance_2', 'counterbalance', 'counterbalance_3'},
146
+ "names": ['counterbalance', 'counterbalance'],
147
+ "tags": ['CounterbalanceRoutine', 'EyetrackerCalibrationRoutine']},
148
+
149
+ {"file": "test_mix_exp.psyexp",
150
+ "expectedSet": {'trial', 'counterbalance', 'calibration', 'counterbalance_2',
151
+ 'validation', 'counterbalance_3', 'calibration_2', 'validation_2'},
152
+ "names": ['counterbalance', 'calibration', 'validation'],
153
+ "tags": ['CounterbalanceRoutine', 'EyetrackerCalibrationRoutine', 'EyetrackerValidationRoutine']},
154
+
155
+ {"file": "test_mix_missing.psyexp",
156
+ "expectedSet": {'trial', 'calibration_2', 'counterbalance_2', 'calibration',
157
+ 'counterbalance', 'calibration_3', 'counterbalance_3'},
158
+ "names": ['calibration_2', 'counterbalance_2', 'calibration', 'counterbalance'],
159
+ "tags": ['EyetrackerCalibrationRoutine', 'CounterbalanceRoutine', 'EyetrackerCalibrationRoutine',
160
+ 'CounterbalanceRoutine']},
161
+
162
+ {"file": "test_mix_name_calibration.psyexp",
163
+ "expectedSet": {'trial', 'calibration_2', 'custom_2', 'counterbalance_2',
164
+ 'calibration', 'calibration_3', 'custom_3', 'custom'},
165
+ "names": ['calibration', 'calibration', 'custom_2', 'custom'],
166
+ "tags": ['EyetrackerCalibrationRoutine', 'CounterbalanceRoutine', 'CounterbalanceRoutine',
167
+ 'EyetrackerCalibrationRoutine']},
168
+ ]
169
+
170
+ for case in cases:
171
+ exp.loadFromXML(Path(TESTS_DATA_PATH) / "test_loaded_namespace" / case['file'])
172
+
173
+ # add new routines to the experiment and their names to namespace
174
+ namespace = exp.namespace
175
+ for (name, tag) in zip(case["names"], case["tags"]):
176
+ routine = allRoutines[tag](exp=exp, name=name)
177
+ rtGoodName = routine.params['name'].val = namespace.makeValid(
178
+ routine.params['name'].val)
179
+ namespace.add(rtGoodName)
180
+ exp.addStandaloneRoutine(routineName=rtGoodName, routine=routine)
181
+
182
+ actualSet = set(namespace.user)
183
+ expectedSet = case["expectedSet"]
184
+ print()
185
+ print(case['file'])
186
+ print(actualSet)
187
+ print(expectedSet)
188
+ print()
189
+
190
+ # check for no duplicate names in the namespace.user list
191
+ assert len(actualSet) == len(expectedSet)
192
+
193
+ # check that expected names in namespace.user list is
194
+ # equivalent to the actual names in namespace.user
195
+ assert len(actualSet) == len(actualSet.intersection(expectedSet))
196
+
197
+
198
+
199
+
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Thu May 8 10:46:41 2014
4
+
5
+ @author: jon.peirce
6
+ """
7
+ import pytest
8
+ from psychopy import visual, core
9
+ from psychopy.hardware import crs
10
+
11
+
12
+ def test_bitsSharp():
13
+ win = visual.Window(screen=1, fullscr=True, useFBO=True, autoLog=True)
14
+ win.setGamma(1.0) #make sure gfx card LUT is identity
15
+ #initialise BitsSharp
16
+ try:
17
+ bits = crs.BitsSharp(win=win, mode='color++')
18
+ except ImportError:
19
+ pytest.skip("crs.BitsSharp: could not initialize. possible:\nfrom serial.tools import list_ports\n"
20
+ "ImportError: No module named tools")
21
+
22
+ if not bits.OK:
23
+ win.close()
24
+ pytest.skip("No BitsSharp connected")
25
+
26
+ print(bits.info)
27
+
28
+ #switch to status screen (while keeping in mono 'mode')
29
+ bits.getVideoLine(lineN=1, nPixels=1)
30
+ core.wait(5) #wait for status mode to take effect
31
+
32
+ <<<<<<< HEAD
33
+ #createa stimulus to check luminance values
34
+ screenSqr = visual.GratingStim(win, tex=None, mask=None, size=2)
35
+ =======
36
+ #create a stimulus to check luminance values
37
+ screenSqr = visual.GratingStim(win,tex=None, mask=None,
38
+ size=2)
39
+ >>>>>>> release
40
+
41
+ print('\n up from zero:')
42
+ bit16 = (2.0 ** 16) - 1
43
+ for frameN in range(5):
44
+ intensity = frameN / bit16
45
+ screenSqr.color = intensity * 2 - 1 # psychopy is -1:1
46
+ screenSqr.draw()
47
+ win.flip()
48
+ pixels = bits.getVideoLine(lineN=1, nPixels=2)
49
+ print(pixels[0], pixels[1], intensity)
50
+
51
+ print('\n down from 1:')
52
+ for frameN in range(5):
53
+ intensity = 1 - (frameN / bit16)
54
+ screenSqr.color = intensity * 2 - 1 # psychopy is -1:1
55
+ screenSqr.draw()
56
+ win.flip()
57
+ pixels = bits.getVideoLine(lineN=1, nPixels=2)
58
+ print(pixels[0], pixels[1], intensity)
59
+
60
+ print('\n check the middle::')
61
+ for intensity in [0.5, 0.5 + (1 / bit16)]:
62
+ screenSqr.color = intensity * 2 - 1 # psychopy is -1:1
63
+ screenSqr.draw()
64
+ win.flip()
65
+ pixels = bits.getVideoLine(lineN=1, nPixels=2)
66
+ print(pixels[0], pixels[1], intensity)
67
+
68
+ bits.mode = "color++" #get out of status screen
@@ -128,3 +128,115 @@ def test_AliasDict():
128
128
  params2.alias("1", alias="one")
129
129
  assert "one" not in params.aliases
130
130
  assert "1" not in params.aliases
131
+
132
+
133
+ class TestIndexDict:
134
+ def setup_method(self):
135
+ self.recreateData()
136
+
137
+ def recreateData(self):
138
+ """
139
+ Recreate the test index dict from scratch, in case it changed over the course of a test.
140
+ """
141
+ self.data = at.IndexDict({
142
+ 'someKey': "abc",
143
+ 'someOtherKey': "def",
144
+ 'anotherOne': "ghi",
145
+ 1: "jkl",
146
+ })
147
+
148
+ def test_isinstance(self):
149
+ """
150
+ Check that an IndexDict is an instance of dict
151
+ """
152
+ assert isinstance(self.data, dict)
153
+
154
+ def test_length(self):
155
+ """
156
+ Check that an IndexDict reports its length as the number of explicit keys, ignoring
157
+ positional indices.
158
+ """
159
+ assert len(self.data) == 4
160
+
161
+ def test_get_item(self):
162
+ """
163
+ Check that items in an IndexDict can be got as expected, explicit keys should always take
164
+ precedence over positional indices.
165
+ """
166
+ cases = [
167
+ # positional indices
168
+ (0, "abc"),
169
+ (2, "ghi"),
170
+ (3, "jkl"),
171
+ # explicit indices
172
+ ('someKey', "abc"),
173
+ ('someOtherKey', "def"),
174
+ ('anotherOne', "ghi"),
175
+ # conflicting indices (should favour explicit)
176
+ (1, "jkl"),
177
+ ]
178
+ # iterate through cases
179
+ for key, value in cases:
180
+ # check that each key returns the correct value
181
+ assert self.data[key] == value
182
+
183
+ def test_set_item(self):
184
+ """
185
+ Check that items in an IndexDict can be set as expected, should set by position only if the
186
+ positional index is not already defined as an explicit key.
187
+ """
188
+ cases = [
189
+ # set by positional index (no conflict)
190
+ {'set': (0, "mno"), 'get': (0, "mno")},
191
+ {'set': (0, "mno"), 'get': ('someKey', "mno")},
192
+ # set by explicit key (no conflict)
193
+ {'set': ('someKey', "mno"), 'get': (0, "mno")},
194
+ {'set': ('someKey', "mno"), 'get': ('someKey', "mno")},
195
+ # set by explicit string (when its positional index is also a key)
196
+ {'set': ('someOtherKey', "pqr"), 'get': ('someOtherKey', "pqr")},
197
+ {'set': ('someOtherKey', "mno"), 'get': (1, "jkl")},
198
+ # set by positional index (when it's also a key)
199
+ {'set': (1, "pqr"), 'get': ('someOtherKey', "def")},
200
+ {'set': (1, "pqr"), 'get': (1, "pqr")},
201
+ # set by explicit key not yet in array
202
+ {'set': ('newKey', "stu"), 'get': ('newKey', "stu")},
203
+ {'set': ('newKey', "stu"), 'get': (4, "stu")},
204
+ # set by positional index not yet in array (should treat as explicit key)
205
+ {'set': (6, "stu"), 'get': (6, "stu")},
206
+ ]
207
+ # iterate through cases
208
+ for case in cases:
209
+ # recreate data to clear last changes
210
+ self.recreateData()
211
+ # get values to set and expected values to return
212
+ seti, setval = case['set']
213
+ geti, getval = case['get']
214
+ # set value
215
+ self.data[seti] = setval
216
+ # check value
217
+ assert self.data[geti] == getval, (
218
+ f"After setting data[{repr(seti)}] = {repr(setval)} expected to get data["
219
+ f"{repr(geti)}] == {repr(getval)}, but instead got data[{repr(geti)}] == "
220
+ f"{repr(self.data[geti])}"
221
+ )
222
+
223
+ def test_key_error(self):
224
+ """
225
+ Check that supplying an invalid key/index to an IndexDict still errors like a normal
226
+ dict/list
227
+ """
228
+ cases = [
229
+ # index bigger than array
230
+ (4, KeyError, "4 should be out of bounds, but got {}"),
231
+ # key not in array
232
+ ('lalala', KeyError, "There shouldn't be a value for 'lalala', but got {}"),
233
+ ]
234
+ for i, errType, msg in cases:
235
+ try:
236
+ val = self.data[i]
237
+ except errType as err:
238
+ # if it errors as expected, good!
239
+ pass
240
+ else:
241
+ # if no error, raise an assertion error with message
242
+ raise AssertionError(msg.format(val))
@@ -0,0 +1,219 @@
1
+ from pathlib import Path
2
+
3
+ from psychopy import visual, colors, core
4
+ from .test_basevisual import _TestUnitsMixin
5
+ from psychopy.tests.test_experiment.test_component_compile_python import _TestBoilerplateMixin
6
+ from .. import utils
7
+
8
+ from ..utils import TESTS_DATA_PATH
9
+
10
+
11
+ class TestImage(_TestUnitsMixin, _TestBoilerplateMixin):
12
+ """
13
+ Test that images render as expected. Note: In BaseVisual tests, image colors will look different than
14
+ seems intuitive as foreColor will be set to `"blue"`.
15
+ """
16
+ def setup(self):
17
+ self.win = visual.Window()
18
+ self.obj = visual.ImageStim(
19
+ self.win,
20
+ str(Path(TESTS_DATA_PATH) / 'testimage.jpg'),
21
+ colorSpace='rgb1',
22
+ )
23
+
24
+ def test_anchor_flip(self):
25
+ """
26
+ Check that flipping the image doesn't flip the direction of the anchor
27
+ """
28
+ # Setup obj
29
+ self.obj.units = "height"
30
+ self.obj.pos = (0, 0)
31
+ self.obj.size = (0.5, 0.5)
32
+ self.obj.anchor = "bottom left"
33
+ # Flip vertically
34
+ self.obj.flipVert = True
35
+ self.obj.flipHoriz = False
36
+ # Check
37
+ self.win.flip()
38
+ self.obj.draw()
39
+ # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / "test_image_flip_anchor_vert.png")
40
+ utils.compareScreenshot("test_image_flip_anchor_vert.png", self.win, crit=7)
41
+ # Flip horizontally
42
+ self.obj.flipVert = False
43
+ self.obj.flipHoriz = True
44
+ # Check
45
+ self.win.flip()
46
+ self.obj.draw()
47
+ # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / "test_image_flip_anchor_horiz.png")
48
+ utils.compareScreenshot("test_image_flip_anchor_horiz.png", self.win, crit=7)
49
+
50
+ <<<<<<< HEAD
51
+ def test_aspect_ratio(self):
52
+ """
53
+ Test that images set with one or both dimensions as None maintain their aspect ratio
54
+ """
55
+ cases = [
56
+ # norm 1:1
57
+ {"img": "default.png", "aspect": (1, 1),
58
+ "size": (None, 2), "units": "norm",
59
+ "tag": "default_xNone_yFull"},
60
+ {"img": "default.png", "aspect": (1, 1),
61
+ "size": (2, None), "units": "norm",
62
+ "tag": "default_xFull_yNone"},
63
+ {"img": "default.png", "aspect": (1, 1),
64
+ "size": (None, None), "units": "norm",
65
+ "tag": "default_xNone_yNone"},
66
+ {"img": "default.png", "aspect": (1, 1),
67
+ "size": None, "units": "norm",
68
+ "tag": "default_None"},
69
+ # height 1:1
70
+ {"img": "default.png", "aspect": (1, 1),
71
+ "size": (None, 1), "units": "height",
72
+ "tag": "default_xNone_yFull"},
73
+ {"img": "default.png", "aspect": (1, 1),
74
+ "size": (1 / self.win.size[1] * self.win.size[0], None), "units": "height",
75
+ "tag": "default_xFull_yNone"},
76
+ {"img": "default.png", "aspect": (1, 1),
77
+ "size": (None, None), "units": "height",
78
+ "tag": "default_xNone_yNone"},
79
+ {"img": "default.png", "aspect": (1, 1),
80
+ "size": None, "units": "height",
81
+ "tag": "default_None"},
82
+ # pix 1:1
83
+ {"img": "default.png", "aspect": (1, 1),
84
+ "size": (None, self.win.size[1]), "units": "pix",
85
+ "tag": "default_xNone_yFull"},
86
+ {"img": "default.png", "aspect": (1, 1),
87
+ "size": (self.win.size[0], None), "units": "pix",
88
+ "tag": "default_xFull_yNone"},
89
+ {"img": "default.png", "aspect": (1, 1),
90
+ "size": (None, None), "units": "pix",
91
+ "tag": "default_xNone_yNone"},
92
+ {"img": "default.png", "aspect": (1, 1),
93
+ "size": None, "units": "pix",
94
+ "tag": "default_None"},
95
+ ]
96
+ for case in cases:
97
+ # Set image
98
+ self.obj.image = case['img']
99
+ # Set size
100
+ self.obj.units = case['units']
101
+ self.obj.size = case['size']
102
+ # Check that aspect ratio is still correct
103
+ assert self.obj.aspectRatio == case['aspect']
104
+ # Check that image looks as expected
105
+ self.obj.draw()
106
+ filename = f"test_image_aspect_{case['tag']}.png"
107
+ # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / filename)
108
+ utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / filename, self.win, crit=7)
109
+ self.win.flip()
110
+ =======
111
+
112
+ class TestImageAnimation:
113
+ """
114
+ Tests for using ImageStim to create frame animations
115
+ """
116
+ @classmethod
117
+ def setup_class(cls):
118
+ nFrames = 16
119
+ # Define array of sizes/desired frame rates
120
+ cls.cases = [
121
+ {'size': 6 ** 2, 'fps': 16},
122
+ {'size': 8 ** 2, 'fps': 16},
123
+ {'size': 10 ** 2, 'fps': 8},
124
+ {'size': 12 ** 2, 'fps': 2},
125
+ ]
126
+ # Create frames
127
+ for i, case in enumerate(cls.cases):
128
+ size = case['size']
129
+ # Create window and shapes
130
+ win = visual.Window(size=(size, size), color='purple')
131
+ shape1 = visual.ShapeStim(win,
132
+ pos=(0.2, 0.2), size=(0.5, 0.5),
133
+ lineWidth=size * 0.1,
134
+ fillColor='red', lineColor='green',
135
+ )
136
+ shape2 = visual.ShapeStim(win,
137
+ pos=(-0.2, -0.2), size=(0.5, 0.5),
138
+ lineWidth=size * 0.1,
139
+ fillColor='blue', lineColor='yellow'
140
+ )
141
+
142
+ frames = []
143
+
144
+ for thisFrame in range(nFrames):
145
+ # Cycle window hue
146
+ win.color = colors.Color(
147
+ (win._color.hsv[0] + 360 * thisFrame / nFrames, win._color.hsv[1], win._color.hsv[2]),
148
+ 'hsv'
149
+ )
150
+ # Cycle shape hues
151
+ shape1._fillColor.hsv = (
152
+ shape1._fillColor.hsv[0] + 360 * thisFrame / nFrames, shape1._fillColor.hsv[1],
153
+ shape1._fillColor.hsv[2]
154
+ )
155
+ shape1._borderColor.hsv = (
156
+ shape1._borderColor.hsv[0] - 360 * thisFrame / nFrames, shape1._borderColor.hsv[1],
157
+ shape1._borderColor.hsv[2]
158
+ )
159
+ shape2._fillColor.hsv = (
160
+ shape2._fillColor.hsv[0] + 360 * thisFrame / nFrames, shape2._fillColor.hsv[1],
161
+ shape2._fillColor.hsv[2]
162
+ )
163
+ shape2._borderColor.hsv = (
164
+ shape2._borderColor.hsv[0] - 360 * thisFrame / nFrames, shape2._borderColor.hsv[1],
165
+ shape2._borderColor.hsv[2]
166
+ )
167
+ # Rotate shapes
168
+ shape1.ori = shape1.ori + 360 * thisFrame / nFrames
169
+ shape2.ori = shape2.ori - 360 * thisFrame / nFrames
170
+ # Render
171
+ win.flip()
172
+ shape1.draw()
173
+ shape2.draw()
174
+ # Get frame
175
+ frame = win.getMovieFrame(buffer='back')
176
+ frames.append(
177
+ frame
178
+ )
179
+
180
+ # Cleanup
181
+ win.close()
182
+ del shape1
183
+ del shape2
184
+ # Update case
185
+ cls.cases[i]['frames'] = frames
186
+
187
+ def test_fps(self):
188
+ """
189
+ Check that images can be updated sufficiently fast to create frame animations
190
+ """
191
+ # Create clock
192
+ clock = core.Clock()
193
+ # Try at each size
194
+ for case in self.cases:
195
+ size = case['size']
196
+ # Make window and image
197
+ win = visual.Window(size=(size, size))
198
+ img = visual.ImageStim(win, units='pix', size=(size, size))
199
+ # Iterate through frames
200
+ refr = []
201
+ for frame in case['frames']:
202
+ # Reset clock
203
+ clock.reset()
204
+ # Set image contents
205
+ img.image = frame
206
+ # Update
207
+ img.draw()
208
+ win.flip()
209
+ # Store time taken
210
+ refr.append(clock.getTime())
211
+ # Print frame rate for this size
212
+ fps = round(1 / max(refr))
213
+ assert fps > case['fps'], (
214
+ f"Max frame rate for {size}x{size} animations should be at least {case['fps']}, but was {fps}"
215
+ )
216
+ # Cleanup
217
+ win.close()
218
+ del img
219
+ >>>>>>> 14bdb82f9dac0abd734f48d14e74d67e0eb3cc50
@@ -21,6 +21,53 @@ import numpy
21
21
  import ctypes
22
22
 
23
23
 
24
+ class IndexDict(dict):
25
+ """
26
+ A dict which allows for keys to be accessed by index as well as by key. Can be initialised
27
+ from a dict, or from a set of keyword arguments.
28
+
29
+ Example
30
+ -------
31
+ ```
32
+ data = IndexDict({
33
+ 'someKey': "abc",
34
+ 'someOtherKey': "def",
35
+ 'anotherOne': "ghi",
36
+ 1: "jkl",
37
+ })
38
+ # using a numeric index will return the value for the key at that position
39
+ print(data[0]) # prints: abc
40
+ # ...unless that number is already a key
41
+ print(data[1]) # prints: jkl
42
+ ```
43
+ """
44
+ def __init__(self, arr=None, **kwargs):
45
+ # initialise dict
46
+ dict.__init__(self)
47
+ # if given no dict, use a blank one
48
+ if arr is None:
49
+ arr = {}
50
+ # if given a dict, update kwargs with it
51
+ kwargs.update(arr)
52
+ # set every key
53
+ for key, value in kwargs.items():
54
+ dict.__setitem__(self, key, value)
55
+
56
+ def __getitem__(self, key):
57
+ # if key is a valid numeric index not present as a normal key, get matching key
58
+ if isinstance(key, int) and key < len(self) and key not in self:
59
+ return list(self.values())[key]
60
+ # index like normal
61
+ return dict.__getitem__(self, key)
62
+
63
+ def __setitem__(self, key, value):
64
+ # if key is a valid numeric index not present as a normal key, get matching key
65
+ if isinstance(key, int) and key < len(self) and key not in self:
66
+ key = list(self.keys())[key]
67
+ # set like normal
68
+ return dict.__setitem__(self, key, value)
69
+
70
+
24
71
  def createXYs(x, y=None):
25
72
  """Create an Nx2 array of XY values including all combinations of the
26
73
  x and y values provided.
@@ -202,7 +202,7 @@ def getPsychoJSVersionStr(currentVersion, preferredVersion=''):
202
202
  # e.g. 2021.1.0 not 2021.1.0.dev3
203
203
  useVerStr = '.'.join(useVerStr.split('.')[:3])
204
204
  # PsychoJS doesn't have additional rc1 or dev1 releases
205
- for versionSuffix in ["rc", "dev", "a", "b"]:
205
+ for versionSuffix in ["rc", "dev", "post", "a", "b"]:
206
206
  if versionSuffix in useVerStr:
207
207
  useVerStr = useVerStr.split(versionSuffix)[0]
208
208
 
@@ -304,6 +304,23 @@ class PygletBackend(BaseBackend):
304
304
  except Exception:
305
305
  # pyglet 1.2 with 64bit python?
306
306
  win._hw_handle = self.winHandle._nswindow.windowNumber()
307
+ # Here we temporarily set the window to the bottom right corner of the
308
+ # requested screen so the correct screen is always detected for NSWindow.
309
+ # The actual location is then set below using the pyglet set_location()
310
+ # method on the CocoaWindow object that wraps the NSWindow as _nswindow.
311
+ # This is necessary because NSScreen origin is the bottom left corner of
312
+ # the unshifted main screen and positive up, while pyglet origin is the top
313
+ # left corner of the shifted main screen and positive down. If thisScreen is
314
+ # not the main screen, we need to prevent self.winHandle._nswindow.screen()
315
+ # from returning None, which can happen when the c binding returns nil if the
316
+ # window is offscreen as a result of flipped y values of origins beween pyglet
317
+ # and NSWindow coordinate systems.
318
+ from pyglet.libs.darwin import cocoapy
319
+ mainScreen_y_from_NSOrigin = allScrs[0].y + allScrs[0].height
320
+ thisScreen_y_from_NSOrigin = thisScreen.y + thisScreen.height
321
+ thisScreen_y = mainScreen_y_from_NSOrigin - thisScreen_y_from_NSOrigin
322
+ temp_origin = cocoapy.NSPoint(thisScreen.x, thisScreen_y)
323
+ self.winHandle._nswindow.setFrameOrigin_(temp_origin)
307
324
  elif sys.platform.startswith('linux'):
308
325
  win._hw_handle = self.winHandle._window
309
326
  self._frameBufferSize = win.clientSize
@@ -345,14 +362,15 @@ class PygletBackend(BaseBackend):
345
362
  # (but need to alter x,y handling then)
346
363
  self.winHandle.set_mouse_visible(False)
347
364
  if not win.pos:
348
- # work out where the centre should be
349
- if win.useRetina:
350
- win.pos = [(thisScreen.width - win.clientSize[0]/2) / 2,
351
- (thisScreen.height - win.clientSize[1]/2) / 2]
352
- else:
353
- win.pos = [(thisScreen.width - win.clientSize[0]) / 2,
354
- (thisScreen.height - win.clientSize[1]) / 2]
355
- if not win._isFullScr:
365
+ # work out the location of the top-left corner to place at the screen center
366
+ win.pos = [(thisScreen.width - win.clientSize[0]) / 2,
367
+ (thisScreen.height - win.clientSize[1]) / 2]
368
+ if sys.platform == 'darwin':
369
+ # always need to set the cocoa window location due to origin changes
370
+ screenHeight_offset = thisScreen.height - allScrs[0].height
371
+ self.winHandle.set_location(int(win.pos[0] + thisScreen.x),
372
+ int(win.pos[1] + thisScreen.y + screenHeight_offset))
373
+ elif not win._isFullScr:
356
374
  # add the necessary amount for second screen
357
375
  self.winHandle.set_location(int(win.pos[0] + thisScreen.x),
358
376
  int(win.pos[1] + thisScreen.y))