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.
- psychopy/.DS_Store +0 -0
- psychopy/CHANGELOG.txt +204 -0
- psychopy/GIT_SHA +1 -1
- psychopy/VERSION +1 -1
- psychopy/__init__.py +10 -1
- psychopy/__init__.py.orig +65 -0
- psychopy/app/.DS_Store +0 -0
- psychopy/app/Resources/.DS_Store +0 -0
- psychopy/app/_psychopyApp.py +11 -3
- psychopy/app/appData.spec +1 -1
- psychopy/app/builder/builder.py +1 -1
- psychopy/app/builder/builder.py.orig +3932 -0
- psychopy/app/builder/dialogs/__init__.py.orig +1679 -0
- psychopy/app/builder/dialogs/paramCtrls.py +1 -1
- psychopy/app/builder/dialogs/paramCtrls.py.orig +713 -0
- psychopy/app/colorpicker/__init__.py.orig +411 -0
- psychopy/app/cortex.log +0 -0
- psychopy/app/jobs.py +8 -1
- psychopy/app/locale/ar_001/.DS_Store +0 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +792 -1816
- psychopy/app/plugin_manager/dialog.py +9 -7
- psychopy/app/ribbon.py +2 -1
- psychopy/app/runner/runner.py +25 -13
- psychopy/clock.py +8 -4
- psychopy/core.py.orig +169 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +330 -0
- psychopy/demos/builder/Tools/.DS_Store +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +38 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +3418 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +2 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +15 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +562 -0
- psychopy/demos/coder/.DS_Store +0 -0
- psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
- psychopy/demos/coder/iohub/.iohpid +1 -0
- psychopy/demos/coder/iohub/eyetracking/.iohpid +1 -0
- psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
- psychopy/demos/coder/stimuli/.DS_Store +0 -0
- psychopy/experiment/_experiment.py +32 -7
- psychopy/experiment/_experiment.py.orig +1032 -0
- psychopy/experiment/components/.DS_Store +0 -0
- psychopy/experiment/components/_base.py +13 -4
- psychopy/experiment/components/_base.py.orig +823 -0
- psychopy/experiment/components/form/.DS_Store +0 -0
- psychopy/experiment/components/microphone/__init__.py +10 -1
- psychopy/experiment/components/microphone/__init__.py.orig +490 -0
- psychopy/experiment/components/polygon/__init__.py +21 -22
- psychopy/experiment/components/settings/__init__.py +13 -14
- psychopy/experiment/components/settings/__init__.py.orig +1337 -0
- psychopy/experiment/components/textbox/__init__.py.orig +310 -0
- psychopy/experiment/components/webcam/.DS_Store +0 -0
- psychopy/experiment/components/webcam/light/.DS_Store +0 -0
- psychopy/experiment/flow.py +10 -8
- psychopy/experiment/loops.py.orig +829 -0
- psychopy/experiment/params.py +8 -3
- psychopy/experiment/params.py.orig +408 -0
- psychopy/experiment/routine.py.orig +503 -0
- psychopy/experiment/routines/_base.py +15 -6
- psychopy/experiment/routines/counterbalance/__init__.py +1 -0
- psychopy/gui/qtgui.py +14 -7
- psychopy/gui/util.py +10 -14
- psychopy/gui/wxgui.py +10 -4
- psychopy/hardware/.DS_Store +0 -0
- psychopy/hardware/brainproducts.py.orig +680 -0
- psychopy/hardware/iolab.py.orig +238 -0
- psychopy/hardware/manager.py +1 -1
- psychopy/hardware/photodiode.py +59 -27
- psychopy/hardware/speaker.py +4 -4
- psychopy/iohub/client/__init__.py +17 -0
- psychopy/iohub/client/keyboard.py +5 -0
- psychopy/iohub/datastore/__init__.py.orig +443 -0
- psychopy/iohub/datastore/util.py.orig +692 -0
- psychopy/iohub/devices/mouse/darwin.py.orig +427 -0
- psychopy/iohub/devices/mouse/linux2.py +4 -0
- psychopy/iohub/devices/mouse/linux2.py.orig +198 -0
- psychopy/iohub/devices/mouse/win32.py +5 -0
- psychopy/preferences/.DS_Store +0 -0
- psychopy/projects/pavlovia.py +10 -3
- psychopy/projects/pavlovia.py.orig +1295 -0
- psychopy/sound/backend_ptb.py +22 -5
- psychopy/sound/transcribe.py +24 -4
- psychopy/tests/.DS_Store +0 -0
- psychopy/tests/data/.DS_Store +0 -0
- psychopy/tests/data/TestCircle_fill_local.png +0 -0
- psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
- psychopy/tests/data/aperture1_norm_local.png +0 -0
- psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
- psychopy/tests/data/beatandrcos_height_local.png +0 -0
- psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/beatandrcos_norm_local.png +0 -0
- psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
- psychopy/tests/data/blend_add_height_local.png +0 -0
- psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
- psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
- psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
- psychopy/tests/data/blend_add_norm_local.png +0 -0
- psychopy/tests/data/blend_add_stencil_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
- psychopy/tests/data/circleHex_height_local.png +0 -0
- psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
- psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
- psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
- psychopy/tests/data/circleHex_norm_local.png +0 -0
- psychopy/tests/data/circleHex_stencil_local.png +0 -0
- psychopy/tests/data/color_comparison_local.png +0 -0
- psychopy/tests/data/correctScript/.DS_Store +0 -0
- psychopy/tests/data/dots_height_local.png +0 -0
- psychopy/tests/data/dots_normAddBlend_local.png +0 -0
- psychopy/tests/data/dots_normHexbackground_local.png +0 -0
- psychopy/tests/data/dots_normNoShade_local.png +0 -0
- psychopy/tests/data/dots_norm_local.png +0 -0
- psychopy/tests/data/dots_stencil_local.png +0 -0
- psychopy/tests/data/elarray1_height_local.png +0 -0
- psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
- psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
- psychopy/tests/data/elarray1_norm_local.png +0 -0
- psychopy/tests/data/elarray1_stencil_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
- psychopy/tests/data/gabor1_height_local.png +0 -0
- psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
- psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
- psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
- psychopy/tests/data/gabor1_norm_local.png +0 -0
- psychopy/tests/data/gabor1_stencil_local.png +0 -0
- psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
- psychopy/tests/data/imageAndGauss_height_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
- psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
- psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
- psychopy/tests/data/movFrame1_stencil_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
- psychopy/tests/data/numpyImage_height_local.png +0 -0
- psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
- psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
- psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
- psychopy/tests/data/numpyImage_norm_local.png +0 -0
- psychopy/tests/data/numpyImage_stencil_local.png +0 -0
- psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
- psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
- psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
- psychopy/tests/data/shape2_1_norm_local.png +0 -0
- psychopy/tests/data/shape2_1_stencil_local.png +0 -0
- psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
- psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
- psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
- psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
- psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
- psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
- psychopy/tests/data/text1_height_local.png +0 -0
- psychopy/tests/data/text1_normAddBlend_local.png +0 -0
- psychopy/tests/data/text1_normHexbackground_local.png +0 -0
- psychopy/tests/data/text1_norm_local.png +0 -0
- psychopy/tests/data/text1_stencil_local.png +0 -0
- psychopy/tests/data/wedge1_height_local.png +0 -0
- psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
- psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
- psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
- psychopy/tests/data/wedge1_norm_local.png +0 -0
- psychopy/tests/data/wedge1_stencil_local.png +0 -0
- psychopy/tests/test_app/.DS_Store +0 -0
- psychopy/tests/test_app/test_builder/.DS_Store +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +177 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +168 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
- psychopy/tests/test_data/.DS_Store +0 -0
- psychopy/tests/test_experiment/test_experiment.py +96 -0
- psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +68 -0
- psychopy/tests/test_tools/test_arraytools.py +112 -0
- psychopy/tests/test_visual/test_image.py.orig +219 -0
- psychopy/tools/arraytools.py +47 -0
- psychopy/tools/versionchooser.py +1 -1
- psychopy/visual/backends/pygletbackend.py +26 -8
- psychopy/visual/basevisual.py.orig +1723 -0
- psychopy/visual/form.py.orig +1181 -0
- psychopy/visual/text.py.orig +752 -0
- psychopy/visual/textbox2/textbox2.py.orig +1315 -0
- psychopy/visual/window.py +13 -5
- psychopy/visual/windowwarp.py.orig +463 -0
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/METADATA +9 -9
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/RECORD +220 -84
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/WHEEL +1 -1
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/entry_points.txt +2 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
- psychopy-2024.2.1.dist-info/licenses/AUTHORS.md +0 -138
- /psychopy/{app/locale/es_ES/LC_MESSAGE → demos/builder}/.DS_Store +0 -0
- /psychopy/{visual → demos/builder/Experiments}/.DS_Store +0 -0
- {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
|
psychopy/tools/arraytools.py
CHANGED
|
@@ -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.
|
psychopy/tools/versionchooser.py
CHANGED
|
@@ -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
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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))
|