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.
- psychopy/.DS_Store +0 -0
- psychopy/CHANGELOG.txt +206 -0
- psychopy/GIT_SHA +1 -0
- psychopy/VERSION +1 -0
- psychopy/__init__.py +77 -15
- psychopy/app/Resources/classic/plugin16.png +0 -0
- psychopy/app/Resources/classic/plugin16@2x.png +0 -0
- psychopy/app/Resources/dark/plugin16.png +0 -0
- psychopy/app/Resources/dark/plugin16@2x.png +0 -0
- psychopy/app/Resources/light/plugin16.png +0 -0
- psychopy/app/Resources/light/plugin16@2x.png +0 -0
- psychopy/app/__init__.py +76 -2
- psychopy/app/_psychopyApp.py +126 -101
- psychopy/app/builder/builder.py +14 -10
- psychopy/app/builder/dialogs/__init__.py +8 -8
- psychopy/app/builder/dialogs/dlgsConditions.py +12 -13
- psychopy/app/builder/dialogs/paramCtrls.py +24 -57
- psychopy/app/builder/localizedStrings.py +11 -9
- psychopy/app/builder/validators.py +2 -2
- psychopy/app/coder/codeEditorBase.py +8 -8
- psychopy/app/coder/coder.py +4 -4
- psychopy/app/connections/sendusage.py +2 -2
- psychopy/app/connections/updates.py +9 -9
- psychopy/app/dialogs.py +34 -2
- psychopy/app/idle.py +31 -0
- psychopy/app/jobs.py +21 -3
- psychopy/app/linuxconfig/__init__.py +9 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +4602 -2540
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +56 -54
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +53 -43
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.po +56 -54
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1258 -1176
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +9415 -5
- psychopy/app/pavlovia_ui/_base.py +33 -3
- psychopy/app/pavlovia_ui/search.py +0 -1
- psychopy/app/plugin_manager/dialog.py +104 -51
- psychopy/app/plugin_manager/packages.py +5 -0
- psychopy/app/plugin_manager/plugins.py +152 -67
- psychopy/app/preferencesDlg.py +8 -8
- psychopy/app/psychopyApp.py +11 -5
- psychopy/app/ribbon.py +124 -14
- psychopy/app/runner/runner.py +6 -1
- psychopy/app/stdout/stdOutRich.py +27 -11
- psychopy/app/themes/icons.py +52 -2
- psychopy/assets/__init__.py +0 -0
- psychopy/assets/click.png +0 -0
- psychopy/assets/clicknext.png +0 -0
- psychopy/assets/next.png +0 -0
- psychopy/assets/psychopy.ico +0 -0
- psychopy/assets/psychopy.png +0 -0
- psychopy/assets/templates/__init__.py +0 -0
- psychopy/assets/touch.png +0 -0
- psychopy/assets/touchnext.png +0 -0
- psychopy/assets/window.ico +0 -0
- psychopy/changes/2023.1.0.md +9 -0
- psychopy/changes/2024.1.0.md +16 -0
- psychopy/changes/__init__.py +0 -0
- psychopy/clock.py +2 -2
- psychopy/colors.py +2 -1
- psychopy/compatibility.py +53 -1
- psychopy/contrib/.DS_Store +0 -0
- psychopy/contrib/configobj/__init__.py +10 -8
- psychopy/data/__init__.py +3 -2
- psychopy/data/base.py +5 -5
- psychopy/data/experiment.py +130 -4
- psychopy/data/routine.py +56 -0
- psychopy/data/staircase.py +2 -2
- psychopy/data/trial.py +559 -97
- psychopy/data/utils.py +56 -21
- psychopy/demos/.DS_Store +0 -0
- psychopy/demos/builder/.DS_Store +0 -0
- psychopy/demos/builder/Design Templates/.DS_Store +0 -0
- psychopy/demos/builder/Experiments/.DS_Store +0 -0
- psychopy/demos/builder/Feature Demos/.DS_Store +0 -0
- psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +375 -0
- psychopy/demos/builder/Feature Demos/buttonBox/readme.md +5 -0
- psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +433 -0
- psychopy/demos/builder/Feature Demos/pilotMode/readme.md +7 -0
- psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +4 -4
- psychopy/demos/builder/Hardware/.DS_Store +0 -0
- psychopy/demos/builder/Helper Tools/.DS_Store +0 -0
- psychopy/demos/coder/.DS_Store +0 -0
- psychopy/demos/coder/hardware/testSoundLatency.py +2 -2
- psychopy/demos/coder/iohub/.DS_Store +0 -0
- psychopy/demos/coder/misc/hdf5_2_csv +33 -0
- psychopy/event.py +30 -29
- psychopy/experiment/.DS_Store +0 -0
- psychopy/experiment/_experiment.py +6 -6
- psychopy/experiment/components/.DS_Store +0 -0
- psychopy/experiment/components/__init__.py +6 -3
- psychopy/experiment/components/_base.py +286 -131
- psychopy/experiment/components/aperture/.DS_Store +0 -0
- psychopy/experiment/components/brush/.DS_Store +0 -0
- psychopy/experiment/components/button/.DS_Store +0 -0
- psychopy/experiment/components/button/__init__.py +5 -1
- psychopy/experiment/components/buttonBox/.DS_Store +0 -0
- psychopy/experiment/components/buttonBox/__init__.py +21 -12
- psychopy/experiment/components/camera/.DS_Store +0 -0
- psychopy/experiment/components/code/.DS_Store +0 -0
- psychopy/experiment/components/dots/.DS_Store +0 -0
- psychopy/experiment/components/eyetracker_record/.DS_Store +0 -0
- psychopy/experiment/components/eyetracker_record/__init__.py +92 -30
- psychopy/experiment/components/form/.DS_Store +0 -0
- psychopy/experiment/components/form/__init__.py +6 -2
- psychopy/experiment/components/grating/.DS_Store +0 -0
- psychopy/experiment/components/grating/__init__.py +14 -3
- psychopy/experiment/components/image/.DS_Store +0 -0
- psychopy/experiment/components/image/__init__.py +14 -3
- psychopy/experiment/components/joyButtons/.DS_Store +0 -0
- psychopy/experiment/components/joystick/.DS_Store +0 -0
- psychopy/experiment/components/keyboard/.DS_Store +0 -0
- psychopy/experiment/components/keyboard/__init__.py +22 -10
- psychopy/experiment/components/microphone/.DS_Store +0 -0
- psychopy/experiment/components/microphone/__init__.py +59 -39
- psychopy/experiment/components/mouse/.DS_Store +0 -0
- psychopy/experiment/components/mouse/__init__.py +44 -29
- psychopy/experiment/components/movie/.DS_Store +0 -0
- psychopy/experiment/components/movie/__init__.py +1 -1
- psychopy/experiment/components/panorama/.DS_Store +0 -0
- psychopy/experiment/components/parallelOut/.DS_Store +0 -0
- psychopy/experiment/components/patch/.DS_Store +0 -0
- psychopy/experiment/components/polygon/.DS_Store +0 -0
- psychopy/experiment/components/polygon/__init__.py +26 -6
- psychopy/experiment/components/progress/.DS_Store +0 -0
- psychopy/experiment/components/progress/__init__.py +1 -1
- psychopy/experiment/components/ratingScale/.DS_Store +0 -0
- psychopy/experiment/components/resourceManager/.DS_Store +0 -0
- psychopy/experiment/components/roi/.DS_Store +0 -0
- psychopy/experiment/components/roi/__init__.py +5 -0
- psychopy/experiment/components/routineSettings/.DS_Store +0 -0
- psychopy/experiment/components/routineSettings/__init__.py +57 -10
- psychopy/experiment/components/serialOut/.DS_Store +0 -0
- psychopy/experiment/components/settings/.DS_Store +0 -0
- psychopy/experiment/components/settings/__init__.py +117 -42
- psychopy/experiment/components/slider/.DS_Store +0 -0
- psychopy/experiment/components/sound/.DS_Store +0 -0
- psychopy/experiment/components/sound/__init__.py +54 -19
- psychopy/experiment/components/static/.DS_Store +0 -0
- psychopy/experiment/components/static/__init__.py +1 -1
- psychopy/experiment/components/text/.DS_Store +0 -0
- psychopy/experiment/components/text/__init__.py +28 -3
- psychopy/experiment/components/textbox/.DS_Store +0 -0
- psychopy/experiment/components/textbox/__init__.py +12 -2
- psychopy/experiment/components/unknown/.DS_Store +0 -0
- psychopy/experiment/components/unknown/__init__.py +1 -2
- psychopy/experiment/components/unknownPlugin/.DS_Store +0 -0
- psychopy/experiment/components/unknownPlugin/__init__.py +2 -2
- psychopy/experiment/components/variable/.DS_Store +0 -0
- psychopy/experiment/flow.py +11 -4
- psychopy/experiment/loops.py +85 -37
- psychopy/experiment/params.py +74 -32
- psychopy/experiment/py2js_transpiler.py +8 -1
- psychopy/experiment/routines/.DS_Store +0 -0
- psychopy/experiment/routines/_base.py +102 -22
- psychopy/experiment/routines/counterbalance/.DS_Store +0 -0
- psychopy/experiment/routines/counterbalance/__init__.py +5 -1
- psychopy/experiment/routines/eyetracker_calibrate/.DS_Store +0 -0
- psychopy/experiment/routines/eyetracker_validate/.DS_Store +0 -0
- psychopy/experiment/routines/pavlovia_survey/.DS_Store +0 -0
- psychopy/experiment/routines/photodiodeValidator/.DS_Store +0 -0
- psychopy/experiment/routines/photodiodeValidator/__init__.py +7 -6
- psychopy/experiment/routines/unknown/.DS_Store +0 -0
- psychopy/gui/wxgui.py +4 -4
- psychopy/hardware/.DS_Store +0 -0
- psychopy/hardware/__init__.py +1 -1
- psychopy/hardware/base.py +12 -0
- psychopy/hardware/camera/__init__.py +1 -15
- psychopy/hardware/cedrus.py +10 -11
- psychopy/hardware/crs/colorcal.py +13 -22
- psychopy/hardware/crs/optical.py +10 -20
- psychopy/hardware/emulator.py +17 -14
- psychopy/hardware/eyetracker.py +42 -118
- psychopy/hardware/gammasci.py +4 -15
- psychopy/hardware/keyboard.py +102 -11
- psychopy/hardware/listener.py +3 -0
- psychopy/hardware/microphone.py +148 -18
- psychopy/hardware/minolta.py +8 -15
- psychopy/hardware/photodiode.py +191 -16
- psychopy/hardware/photometer/__init__.py +11 -19
- psychopy/hardware/pr.py +8 -15
- psychopy/hardware/speaker.py +39 -4
- psychopy/info.py +0 -71
- psychopy/iohub/.DS_Store +0 -0
- psychopy/iohub/__init__.py +1 -1
- psychopy/iohub/client/__init__.py +30 -20
- psychopy/iohub/client/keyboard.py +24 -24
- psychopy/iohub/datastore/__init__.py +2 -2
- psychopy/iohub/datastore/util.py +2 -2
- psychopy/iohub/default_config.yaml +1 -1
- psychopy/iohub/devices/.DS_Store +0 -0
- psychopy/iohub/devices/__init__.py +112 -25
- psychopy/iohub/devices/deviceConfigValidation.py +2 -1
- psychopy/iohub/devices/experiment/default_experiment.yaml +12 -1
- psychopy/iohub/devices/experiment/supported_config_settings.yaml +5 -1
- psychopy/iohub/devices/eyetracker/.DS_Store +0 -0
- psychopy/iohub/devices/eyetracker/__init__.py +46 -0
- psychopy/iohub/devices/eyetracker/calibration/procedure.py +2 -2
- psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +14 -2
- psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +3 -4
- psychopy/iohub/server.py +2 -2
- psychopy/iohub/start_iohub_process.py +3 -0
- psychopy/iohub/util/__init__.py +62 -70
- psychopy/layout.py +5 -5
- psychopy/logging.py +8 -1
- psychopy/microphone.py +10 -37
- psychopy/platform_specific/__init__.py +0 -2
- psychopy/platform_specific/darwin.py +1 -3
- psychopy/platform_specific/linux.py +31 -33
- psychopy/platform_specific/win32.py +38 -13
- psychopy/plugins/__init__.py +148 -116
- psychopy/plugins/util.py +39 -0
- psychopy/preferences/Darwin.spec +4 -2
- psychopy/preferences/FreeBSD.spec +4 -2
- psychopy/preferences/Linux.spec +4 -2
- psychopy/preferences/Windows.spec +4 -2
- psychopy/preferences/baseNoArch.spec +4 -2
- psychopy/preferences/preferences.py +47 -24
- psychopy/projects/pavlovia.py +47 -4
- psychopy/scripts/psyexpCompile.py +0 -4
- psychopy/session.py +153 -21
- psychopy/sound/__init__.py +31 -21
- psychopy/sound/_base.py +20 -3
- psychopy/sound/audioclip.py +320 -33
- psychopy/sound/backend_ptb.py +47 -58
- psychopy/sound/backend_pygame.py +1 -1
- psychopy/sound/backend_pysound.py +6 -15
- psychopy/sound/transcribe.py +53 -0
- psychopy/tests/.DS_Store +0 -0
- psychopy/tests/data/.DS_Store +0 -0
- psychopy/tests/data/TestUnknownPluginComponent_load_resave.psyexp +135 -0
- psychopy/tests/data/Test_textbox/test_ori_0_bottom right.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_0_center.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_0_top left.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_120_bottom right.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_120_center.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_120_top left.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_180_bottom right.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_180_center.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_180_top left.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_240_bottom right.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_240_center.png +0 -0
- psychopy/tests/data/Test_textbox/test_ori_240_top left.png +0 -0
- psychopy/tests/data/correctScript/.DS_Store +0 -0
- psychopy/tests/data/test_components/testClearKeyboard/testClearKeyboard.psyexp +200 -0
- psychopy/tests/data/test_session/.DS_Store +0 -0
- psychopy/tests/data/test_session/root/testFutureTrials/testFutureTrials.psyexp +155 -0
- psychopy/tests/data/test_session/root/testTrialNav/trialNav.psyexp +158 -0
- psychopy/tests/test_app/.DS_Store +0 -0
- psychopy/tests/test_app/conftest.py +2 -2
- psychopy/tests/test_app/test_speed.py +4 -1
- psychopy/tests/test_data/test_TrialHandler2.py +146 -1
- psychopy/tests/test_experiment/.DS_Store +0 -0
- psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +3 -3
- psychopy/tests/test_experiment/needs_wx/test_components.py +2 -2
- psychopy/tests/test_experiment/test_components/test_KeyboardComponent.py +28 -0
- psychopy/tests/test_experiment/test_components/test_UnknownPluginComponent.py +27 -0
- psychopy/tests/test_experiment/test_components/test_base_components.py +58 -0
- psychopy/tests/test_experiment/test_py2js.py +1 -1
- psychopy/tests/test_hardware/test_keyboard.py +184 -16
- psychopy/tests/test_hardware/test_ports.py +1 -11
- psychopy/tests/test_liaison/test_Liaison.py +47 -0
- psychopy/tests/test_misc/test_core.py +5 -0
- psychopy/tests/test_session/test_Session.py +5 -1
- psychopy/tests/test_tools/test_versionchooser.py +39 -8
- psychopy/tests/test_visual/test_all_stimuli.py +0 -97
- psychopy/tests/test_visual/test_image.py +6 -5
- psychopy/tests/test_visual/test_textbox.py +36 -0
- psychopy/tests/utils.py +4 -0
- psychopy/tools/filetools.py +1 -1
- psychopy/tools/pkgtools.py +160 -137
- psychopy/tools/versionchooser.py +10 -10
- psychopy/tools/wizard.py +3 -3
- psychopy/visual/.DS_Store +0 -0
- psychopy/visual/backends/pygletbackend.py +24 -13
- psychopy/visual/basevisual.py +5 -11
- psychopy/visual/button.py +2 -14
- psychopy/visual/helpers.py +5 -5
- psychopy/visual/line.py +1 -2
- psychopy/visual/movie2.py +7 -816
- psychopy/visual/movie3.py +7 -589
- psychopy/visual/movies/__init__.py +8 -11
- psychopy/visual/movies/frame.py +5 -2
- psychopy/visual/movies/players/ffpyplayer_player.py +5 -2
- psychopy/visual/noise.py +8 -7
- psychopy/visual/patch.py +7 -16
- psychopy/visual/progress.py +1 -1
- psychopy/visual/radial.py +9 -7
- psychopy/visual/ratingscale.py +8 -1415
- psychopy/visual/secondorder.py +10 -9
- psychopy/visual/shape.py +7 -2
- psychopy/visual/text.py +1 -1
- psychopy/visual/textbox2/textbox2.py +28 -5
- psychopy/web.py +5 -2
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/METADATA +8 -13
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/RECORD +313 -219
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/WHEEL +1 -1
- psychopy/app/Resources/click.png +0 -0
- psychopy/app/Resources/next.png +0 -0
- psychopy/experiment/components/patch/__init__.py +0 -121
- psychopy/experiment/components/patch/classic/patch.png +0 -0
- psychopy/experiment/components/patch/dark/patch.png +0 -0
- psychopy/experiment/components/patch/dark/patch@2x.png +0 -0
- psychopy/experiment/components/patch/light/patch.png +0 -0
- psychopy/experiment/components/patch/light/patch@2x.png +0 -0
- psychopy/experiment/components/ratingScale/__init__.py +0 -337
- psychopy/experiment/components/ratingScale/classic/ratingscale.png +0 -0
- psychopy/experiment/components/ratingScale/classic/ratingscale@2x.png +0 -0
- psychopy/experiment/components/ratingScale/dark/ratingScale@2x.png +0 -0
- psychopy/experiment/components/ratingScale/dark/ratingscale.png +0 -0
- psychopy/experiment/components/ratingScale/light/ratingScale@2x.png +0 -0
- psychopy/experiment/components/ratingScale/light/ratingscale.png +0 -0
- psychopy/platform_specific/posix.py +0 -16
- psychopy/tests/test_sound/test_microphone.py +0 -217
- psychopy/tests/test_visual/test_ratingScale.py +0 -299
- /psychopy/{app/Resources → assets}/Psychopy Window Favicon@16w.png +0 -0
- /psychopy/{app/Resources → assets}/Psychopy Window Favicon@32w.png +0 -0
- /psychopy/{app/Resources → assets}/USB-C.png +0 -0
- /psychopy/{app/Resources → assets}/USB.png +0 -0
- /psychopy/{app/Resources → assets}/creditCard.png +0 -0
- /psychopy/{app/Resources → assets}/default.mp3 +0 -0
- /psychopy/{app/Resources → assets}/default.mp4 +0 -0
- /psychopy/{app/Resources → assets}/default.png +0 -0
- /psychopy/{app/Resources → assets/templates}/instruct1.png +0 -0
- /psychopy/{app/Resources → assets/templates}/instruct2.png +0 -0
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/entry_points.txt +0 -0
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/AUTHORS.md +0 -0
- {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,6 +9,8 @@ from psychopy import experiment
|
|
|
9
9
|
from psychopy.experiment.loops import TrialHandler
|
|
10
10
|
from psychopy.experiment.components import BaseComponent
|
|
11
11
|
from psychopy.experiment.exports import IndentingBuffer
|
|
12
|
+
from psychopy.constants import FOREVER
|
|
13
|
+
from psychopy.tests import utils
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def _find_global_resource_in_js_experiment(script, resource):
|
|
@@ -147,6 +149,62 @@ class BaseComponentTests:
|
|
|
147
149
|
assert buff.indentLevel == 0, errMsg.format(
|
|
148
150
|
"experiment end", buff.indentLevel
|
|
149
151
|
)
|
|
152
|
+
|
|
153
|
+
def test_blank_timing(self):
|
|
154
|
+
"""
|
|
155
|
+
Check that this Component can handle blank start/stop values.
|
|
156
|
+
"""
|
|
157
|
+
# make minimal experiment just for this test
|
|
158
|
+
comp, rt, exp = self.make_minimal_experiment()
|
|
159
|
+
# skip if Component doesn't have the relevant params (e.g. Code Component)
|
|
160
|
+
for key in ("startVal", "startType", "stopVal", "stopType"):
|
|
161
|
+
if key not in comp.params:
|
|
162
|
+
pytest.skip()
|
|
163
|
+
# StaticComponent has entirely bespoke start/stop tests so skip it here
|
|
164
|
+
if type(comp).__name__ == "StaticComponent":
|
|
165
|
+
pytest.skip()
|
|
166
|
+
# make sure start and stop are as times
|
|
167
|
+
comp.params['startType'].val = "time (s)"
|
|
168
|
+
comp.params['stopType'].val = "duration (s)"
|
|
169
|
+
# define cases and expected start/dur
|
|
170
|
+
cases = [
|
|
171
|
+
# blank start
|
|
172
|
+
{'startVal': "", 'stopVal': "1", 'startTime': None, 'duration': 1},
|
|
173
|
+
# blank stop
|
|
174
|
+
{'startVal': "0", 'stopVal': "", 'startTime': 0, 'duration': FOREVER},
|
|
175
|
+
# blank both
|
|
176
|
+
{'startVal': "", 'stopVal': "", 'startTime': None, 'duration': FOREVER},
|
|
177
|
+
]
|
|
178
|
+
# run all cases
|
|
179
|
+
for case in cases:
|
|
180
|
+
# apply values from case
|
|
181
|
+
comp.params['startVal'].val = case['startVal']
|
|
182
|
+
comp.params['stopVal'].val = case['stopVal']
|
|
183
|
+
# get values from start and duration method
|
|
184
|
+
startTime, duration, nonSlipSafe = comp.getStartAndDuration()
|
|
185
|
+
# check against expected
|
|
186
|
+
assert startTime == case['startTime']
|
|
187
|
+
assert duration == case['duration']
|
|
188
|
+
# check that it's never non-slip safe
|
|
189
|
+
assert not nonSlipSafe
|
|
190
|
+
# temp file to save scripts to for flake
|
|
191
|
+
import tempfile
|
|
192
|
+
file = Path(tempfile.gettempdir()) / "test_blank_timing_script.py"
|
|
193
|
+
# write the script
|
|
194
|
+
script = exp.writeScript(target="PsychoPy")
|
|
195
|
+
file.write_text(script, encoding="utf-8")
|
|
196
|
+
# check for syntax errors
|
|
197
|
+
try:
|
|
198
|
+
compile(script, str(file), "exec")
|
|
199
|
+
except Exception as err:
|
|
200
|
+
# save script
|
|
201
|
+
case['fail'] = Path(utils.TESTS_FAILS_PATH) / "test_blank_timing_script.py"
|
|
202
|
+
case['fail'].write_text(script, encoding="utf-8")
|
|
203
|
+
# raise error
|
|
204
|
+
raise AssertionError(
|
|
205
|
+
"Syntax error in compiled Builder code when startVal was '%(startVal)s' and "
|
|
206
|
+
"stopVal was '%(stopVal)s'. Failed script saved in '%(fail)s'" % case
|
|
207
|
+
)
|
|
150
208
|
|
|
151
209
|
def test_disabled_default_val(self):
|
|
152
210
|
"""
|
|
@@ -67,7 +67,7 @@ class TestTranspiler:
|
|
|
67
67
|
{'py': "a.upper()", 'js': "a.toUpperCase();\n"},
|
|
68
68
|
{'py': "a.extend([4, 5, 6])", 'js': "a.concat([4, 5, 6]);\n"},
|
|
69
69
|
{'py': "a.pop(0)", 'js': "a.splice(0, 1);\n"},
|
|
70
|
-
{'py': "a.pop()", 'js': "a.splice((- 1), 1);\n"},
|
|
70
|
+
{'py': "a.pop()", 'js': "a.splice((a.length - 1), 1);\n"},
|
|
71
71
|
]
|
|
72
72
|
# Try each case
|
|
73
73
|
for case in cases:
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from psychopy.hardware import keyboard
|
|
3
|
-
|
|
3
|
+
import pytest
|
|
4
4
|
import time
|
|
5
|
+
from psychopy import logging
|
|
6
|
+
from psychopy.tests.utils import RUNNING_IN_VM
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
class
|
|
8
|
-
|
|
9
|
+
class _TestBaseKeyboard:
|
|
10
|
+
"""
|
|
11
|
+
Tests for the general functionality which should exist across Keyboard backends.
|
|
12
|
+
"""
|
|
13
|
+
def testReceiveGeneratedKeypress(self):
|
|
14
|
+
"""
|
|
15
|
+
Test that `KeyboardDevice.receiveMessage` can receive and interpret a KeyPress object
|
|
16
|
+
correctly, even when that object isn't linked to a physical keypress.
|
|
17
|
+
"""
|
|
9
18
|
# list of events to fake, time to fake them and state of getKeys outputs afterwards
|
|
10
19
|
cases = [
|
|
11
20
|
{'val': "a", 't': 0.1, 'len': 1},
|
|
@@ -18,16 +27,43 @@ class _TestKeyboard:
|
|
|
18
27
|
assert keys[-1] is evt
|
|
19
28
|
assert keys[-1].value == case['val']
|
|
20
29
|
|
|
30
|
+
def testAcceptDuplicateResponses(self):
|
|
31
|
+
"""
|
|
32
|
+
Test that KeyboardDevice can receive multiple presses of the same key without accepting
|
|
33
|
+
genuine duplicates (e.g. KeyPress objects added twice, or the same object added for press
|
|
34
|
+
and release)
|
|
35
|
+
"""
|
|
36
|
+
# clear
|
|
37
|
+
self.kb.clearEvents()
|
|
38
|
+
# press space twice and don't release
|
|
39
|
+
resp1 = self.kb.makeResponse(tDown=0.1, code="space")
|
|
40
|
+
resp2 = self.kb.makeResponse(tDown=0.2, code="space")
|
|
41
|
+
# make sure we only have 2 press objects
|
|
42
|
+
keys = self.kb.getKeys(waitRelease=False, clear=False)
|
|
43
|
+
assert len(keys) == 2
|
|
44
|
+
# simulate a release
|
|
45
|
+
resp1.duration = 0.2
|
|
46
|
+
resp2.duration = 0.1
|
|
47
|
+
self.kb.responses += [resp1, resp2]
|
|
48
|
+
# we should still only have 2 press objects (as these are duplicates)
|
|
49
|
+
keys = self.kb.getKeys(waitRelease=True, clear=False)
|
|
50
|
+
assert len(keys) == 2
|
|
51
|
+
# add the same objects again for no good reason
|
|
52
|
+
self.kb.responses += [resp1, resp2]
|
|
53
|
+
# we should STILL only have 2 press objects
|
|
54
|
+
keys = self.kb.getKeys(waitRelease=True, clear=False)
|
|
55
|
+
assert len(keys) == 2
|
|
56
|
+
|
|
21
57
|
def testMuteOutsidePsychopyNotSlower(self):
|
|
22
58
|
"""
|
|
23
59
|
Test that responses aren't worryingly slower when using muteOutsidePsychopy
|
|
24
60
|
"""
|
|
25
61
|
# skip this test on Linux (as MOP *is* slower due to having to use subprocess)
|
|
26
62
|
if sys.platform == "linux":
|
|
27
|
-
skip()
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
pytest.skip()
|
|
64
|
+
# skip speed tests under vm
|
|
65
|
+
if RUNNING_IN_VM:
|
|
66
|
+
pytest.skip()
|
|
31
67
|
|
|
32
68
|
# array to store times
|
|
33
69
|
times = {}
|
|
@@ -36,38 +72,170 @@ class _TestKeyboard:
|
|
|
36
72
|
|
|
37
73
|
for mop in (True, False):
|
|
38
74
|
# make new keyboard with muteOutsidePsychopy set as desired
|
|
39
|
-
kb =
|
|
75
|
+
self.kb.muteOutsidePsychopy = mop
|
|
40
76
|
# start timer
|
|
41
77
|
start = time.time()
|
|
42
78
|
# make 10000 responses
|
|
43
79
|
for n in range(nResps):
|
|
44
|
-
kb.makeResponse(
|
|
80
|
+
self.kb.makeResponse(
|
|
45
81
|
code="a", tDown=0
|
|
46
82
|
)
|
|
47
83
|
# get time
|
|
48
84
|
times[mop] = time.time() - start
|
|
49
|
-
# delete keyboard
|
|
50
|
-
del kb
|
|
51
85
|
|
|
52
86
|
# work out average difference per-response
|
|
53
87
|
avg = (times[True] - times[False]) / nResps
|
|
54
88
|
# make sure average difference will still be less than a frame on high performance monitors
|
|
55
89
|
assert avg < 1/240
|
|
56
90
|
|
|
57
|
-
|
|
58
|
-
|
|
91
|
+
def teardown_method(self):
|
|
92
|
+
# clear any keypresses
|
|
93
|
+
self.kb.getKeys(clear=True)
|
|
94
|
+
# set mute outside psychopy back to False
|
|
95
|
+
self.kb.muteOutsidePsychopy = False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class _MillikeyMixin:
|
|
99
|
+
"""
|
|
100
|
+
Mixin to add tests which use a Millikey device to generate keypresses. If no such device is
|
|
101
|
+
connected, these tests will all skip.
|
|
102
|
+
"""
|
|
103
|
+
# this attribute should be overwritten when setup_class is called
|
|
104
|
+
millikey = None
|
|
105
|
+
|
|
106
|
+
def setup_class(self):
|
|
107
|
+
"""
|
|
108
|
+
Create a serial object to interface with a Millikey device,
|
|
109
|
+
"""
|
|
110
|
+
from psychopy.hardware.serialdevice import SerialDevice
|
|
111
|
+
from psychopy.tools import systemtools as st
|
|
112
|
+
|
|
113
|
+
# systemtools only works on Windows so skip millikey-dependent tests on other OS's
|
|
114
|
+
if sys.platform != "win32":
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
# use systemtools to find millikey port
|
|
118
|
+
for profile in st.systemProfilerWindowsOS(classname="Ports", connected=True):
|
|
119
|
+
# identify by driver name
|
|
120
|
+
if "usbser.inf" not in profile['Driver Name']:
|
|
121
|
+
continue
|
|
122
|
+
# find "COM" in profile description
|
|
123
|
+
desc = profile['Device Description']
|
|
124
|
+
start = desc.find("COM") + 3
|
|
125
|
+
end = desc.find(")", start)
|
|
126
|
+
# if there's no reference to a COM port, skip
|
|
127
|
+
if -1 in (start, end):
|
|
128
|
+
continue
|
|
129
|
+
# get COM port number
|
|
130
|
+
num = desc[start:end]
|
|
131
|
+
# if we've got this far, create device
|
|
132
|
+
self.millikey = SerialDevice(f"COM{num}", baudrate=128000)
|
|
133
|
+
# stop looking once we've got one
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
def teardown_class(self):
|
|
137
|
+
"""
|
|
138
|
+
Close Millikey before finishing tests.
|
|
139
|
+
"""
|
|
140
|
+
if self.millikey is not None:
|
|
141
|
+
self.millikey.close()
|
|
142
|
+
|
|
143
|
+
def assertMillikey(self):
|
|
144
|
+
"""
|
|
145
|
+
Make sure a Millikey device is connected (and skip the current test if not)
|
|
146
|
+
"""
|
|
147
|
+
# if we didn't find a device, skip current test
|
|
148
|
+
if self.millikey is None:
|
|
149
|
+
pytest.skip()
|
|
150
|
+
|
|
151
|
+
def makeMillikeyKeypress(self, key, duration, delay=0):
|
|
152
|
+
"""
|
|
153
|
+
Send a trigger to the Millikey device telling to to press a particular key.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
key : str
|
|
158
|
+
Key to press
|
|
159
|
+
duration : int
|
|
160
|
+
Duration (ms) of the keypress
|
|
161
|
+
delay : int
|
|
162
|
+
Delay (ms) before making the keypress
|
|
163
|
+
"""
|
|
164
|
+
# make sure we have a millikey device
|
|
165
|
+
self.assertMillikey()
|
|
166
|
+
# construct command (see https://blog.labhackers.com/?p=285)
|
|
167
|
+
cmd = "KGEN {} {} {}".format(key, duration, delay)
|
|
168
|
+
# send
|
|
169
|
+
self.millikey.sendMessage(cmd)
|
|
170
|
+
# read any resp1
|
|
171
|
+
return self.millikey.getResponse()
|
|
172
|
+
|
|
173
|
+
def testReceivePhysicalKeypress(self):
|
|
174
|
+
"""
|
|
175
|
+
Test that physical keypresses are detected.
|
|
176
|
+
|
|
177
|
+
Requires a Millikey device to run.
|
|
178
|
+
"""
|
|
179
|
+
self.makeMillikeyKeypress(key="a", duration=10, delay=10)
|
|
180
|
+
# get last message
|
|
181
|
+
resp = self.kb.getKeys()[-1]
|
|
182
|
+
# check whether the created press was received
|
|
183
|
+
assert resp.name == "a"
|
|
184
|
+
|
|
185
|
+
def testPhysicalKeypressTiming(self):
|
|
186
|
+
"""
|
|
187
|
+
Test that timing (tDown, rt, etc.) on KeyPress objects received via a physical key press
|
|
188
|
+
are correct.
|
|
189
|
+
|
|
190
|
+
Requires a Millikey device to run.
|
|
191
|
+
"""
|
|
192
|
+
# define tolerance (ms) for this test
|
|
193
|
+
tolerance = 4
|
|
194
|
+
# try a few times to make sure times aren't cumulative
|
|
195
|
+
for ans, dur, rt in [
|
|
196
|
+
("a", 123, 456),
|
|
197
|
+
("b", 456, 123),
|
|
198
|
+
("c", 123, 123),
|
|
199
|
+
("d", 456, 456),
|
|
200
|
+
]:
|
|
201
|
+
# reset keyboard clock
|
|
202
|
+
self.kb.clock.reset()
|
|
203
|
+
# wait for rt
|
|
204
|
+
time.sleep(rt / 1000)
|
|
205
|
+
# store start time
|
|
206
|
+
start = logging.defaultClock.getTime()
|
|
207
|
+
# make a keypress
|
|
208
|
+
self.makeMillikeyKeypress(key=ans, duration=dur)
|
|
209
|
+
# wait for press to finish
|
|
210
|
+
time.sleep(dur / 500)
|
|
211
|
+
# get last message
|
|
212
|
+
resp = self.kb.getKeys()[-1]
|
|
213
|
+
# check correct key
|
|
214
|
+
assert resp.name == ans
|
|
215
|
+
# check correct rt
|
|
216
|
+
assert abs(resp.rt - rt / 1000) <= tolerance / 1000
|
|
217
|
+
# check correct duration
|
|
218
|
+
assert abs(resp.duration - dur / 1000) <= tolerance / 1000
|
|
219
|
+
# check correct start time
|
|
220
|
+
assert abs(resp.tDown - start) <= tolerance / 1000
|
|
59
221
|
|
|
60
222
|
|
|
61
|
-
class TestIohubKeyboard(
|
|
223
|
+
class TestIohubKeyboard(_TestBaseKeyboard, _MillikeyMixin):
|
|
62
224
|
def setup_method(self):
|
|
63
225
|
self.kb = keyboard.KeyboardDevice(backend="iohub", muteOutsidePsychopy=False)
|
|
64
226
|
|
|
65
227
|
|
|
66
|
-
class TestPtbKeyboard(
|
|
228
|
+
class TestPtbKeyboard(_TestBaseKeyboard, _MillikeyMixin):
|
|
67
229
|
def setup_method(self):
|
|
68
230
|
self.kb = keyboard.KeyboardDevice(backend="ptb", muteOutsidePsychopy=False)
|
|
69
231
|
|
|
232
|
+
def teardown_method(self):
|
|
233
|
+
self.kb.getKeys(clear=True)
|
|
70
234
|
|
|
71
|
-
|
|
235
|
+
|
|
236
|
+
class TestEventKeyboard(_TestBaseKeyboard, _MillikeyMixin):
|
|
72
237
|
def setup_method(self):
|
|
73
238
|
self.kb = keyboard.KeyboardDevice(backend="event", muteOutsidePsychopy=False)
|
|
239
|
+
|
|
240
|
+
def teardown_method(self):
|
|
241
|
+
self.kb.getKeys(clear=True)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import psychopy.hardware as hw
|
|
2
3
|
import pytest
|
|
3
4
|
try:
|
|
@@ -109,17 +110,6 @@ def test_getCRSPhotometers():
|
|
|
109
110
|
photoms = list(hw.getAllPhotometers())
|
|
110
111
|
assert faked in photoms
|
|
111
112
|
|
|
112
|
-
def test_getPhotometers():
|
|
113
|
-
photoms = hw.getAllPhotometers()
|
|
114
|
-
|
|
115
|
-
# Always iterable
|
|
116
|
-
assert isinstance(photoms, Iterable)
|
|
117
|
-
|
|
118
|
-
photoms = list(photoms)
|
|
119
|
-
|
|
120
|
-
assert len(photoms) > 0
|
|
121
|
-
|
|
122
|
-
|
|
123
113
|
# I wish our PR650 would behave like this ;-)
|
|
124
114
|
_MockPhotometer = type("MockPhotometer",(),{"OK": True,"type": "MockPhotometer"})
|
|
125
115
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import threading
|
|
1
2
|
from psychopy import liaison, session, hardware
|
|
2
3
|
from psychopy.hardware import DeviceManager
|
|
3
4
|
from psychopy.tests import utils, skip_under_vm
|
|
@@ -88,6 +89,52 @@ class TestLiaison:
|
|
|
88
89
|
self.server, self.protocol, "session", "runExperiment",
|
|
89
90
|
"exp1"
|
|
90
91
|
)
|
|
92
|
+
|
|
93
|
+
def test_future_trials(self):
|
|
94
|
+
# add experiment
|
|
95
|
+
runInLiaison(
|
|
96
|
+
self.server, self.protocol, "session", "addExperiment",
|
|
97
|
+
"testFutureTrials/testFutureTrials.psyexp", "testFutureTrials"
|
|
98
|
+
)
|
|
99
|
+
time.sleep(1)
|
|
100
|
+
# define a threaded task to run alongside experiment
|
|
101
|
+
def _thread():
|
|
102
|
+
# wait for first meaningful result
|
|
103
|
+
resp = None
|
|
104
|
+
i = 0
|
|
105
|
+
while resp is None and i < 24:
|
|
106
|
+
# get future trial
|
|
107
|
+
runInLiaison(
|
|
108
|
+
self.server, self.protocol, "session", "getFutureTrial",
|
|
109
|
+
"1", "True"
|
|
110
|
+
)
|
|
111
|
+
# get result
|
|
112
|
+
resp = json.loads(self.protocol.messages[-1]["result"])
|
|
113
|
+
# wait 0.1s
|
|
114
|
+
time.sleep(0.1)
|
|
115
|
+
# iterate towards limit
|
|
116
|
+
i += 1
|
|
117
|
+
# if we hit iteration limit, fail
|
|
118
|
+
assert i < 24, "Timed out waiting for a non-None result from getFutureTrial"
|
|
119
|
+
# does response have all the keys we expect?
|
|
120
|
+
expectedKeys = (
|
|
121
|
+
"type", "thisN", "thisRepN", "thisTrialN", "thisIndex", "data"
|
|
122
|
+
)
|
|
123
|
+
keysPresent = [key in resp for key in expectedKeys]
|
|
124
|
+
assert all(keysPresent), "Trial object missing key(s): {}".format(
|
|
125
|
+
expectedKeys[i] for i, val in enumerate(keysPresent) if not val
|
|
126
|
+
)
|
|
127
|
+
# resp should have type "trial_data"
|
|
128
|
+
assert resp['type'] == "trial_data", (
|
|
129
|
+
f"First non-None result from getFutureTrial doesn't look like a Trial object: {resp}"
|
|
130
|
+
)
|
|
131
|
+
# start thread
|
|
132
|
+
threading.Thread(target=_thread).start()
|
|
133
|
+
# run experiment
|
|
134
|
+
runInLiaison(
|
|
135
|
+
self.server, self.protocol, "session", "runExperiment",
|
|
136
|
+
"testFutureTrials"
|
|
137
|
+
)
|
|
91
138
|
|
|
92
139
|
def test_experiment_error(self):
|
|
93
140
|
"""
|
|
@@ -25,6 +25,7 @@ import pytest
|
|
|
25
25
|
|
|
26
26
|
import psychopy
|
|
27
27
|
import psychopy.logging as logging
|
|
28
|
+
from psychopy.tests.utils import RUNNING_IN_VM
|
|
28
29
|
from psychopy.visual import Window
|
|
29
30
|
from psychopy.core import (getTime, MonotonicClock, Clock, CountdownTimer, wait,
|
|
30
31
|
StaticPeriod, shellCall)
|
|
@@ -346,6 +347,10 @@ def test_LoggingDefaultClock():
|
|
|
346
347
|
|
|
347
348
|
@pytest.mark.staticperiod
|
|
348
349
|
def test_StaticPeriod():
|
|
350
|
+
# this test is speed sensitive, so skip under VM
|
|
351
|
+
if RUNNING_IN_VM:
|
|
352
|
+
pytest.skip()
|
|
353
|
+
|
|
349
354
|
static = StaticPeriod()
|
|
350
355
|
static.start(0.1)
|
|
351
356
|
wait(0.05)
|
|
@@ -22,7 +22,8 @@ class TestSession:
|
|
|
22
22
|
'exp2': "exp2/exp2.psyexp",
|
|
23
23
|
'testCtrls': "testCtrls/testCtrls.psyexp",
|
|
24
24
|
'error': "error/error.psyexp",
|
|
25
|
-
'annotation': "annotation/annotation.psyexp"
|
|
25
|
+
'annotation': "annotation/annotation.psyexp",
|
|
26
|
+
'trialNav': "testTrialNav/trialNav.psyexp"
|
|
26
27
|
}
|
|
27
28
|
)
|
|
28
29
|
# setup devices
|
|
@@ -45,6 +46,9 @@ class TestSession:
|
|
|
45
46
|
self.sess.runExperiment("exp2")
|
|
46
47
|
self.sess.runExperiment("exp1")
|
|
47
48
|
|
|
49
|
+
def test_trial_navigation(self):
|
|
50
|
+
self.sess.runExperiment("trialNav")
|
|
51
|
+
|
|
48
52
|
def test_ctrls(self):
|
|
49
53
|
"""
|
|
50
54
|
Check that experiments check Session often enough for pause/resume commands sent asynchronously will still work.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
"""Tests for psychopy.tools.versionchooser"""
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
+
import unittest
|
|
6
|
+
import subprocess
|
|
7
|
+
import shutil
|
|
5
8
|
from pathlib import Path
|
|
6
9
|
|
|
7
10
|
import psychopy
|
|
@@ -168,11 +171,39 @@ class TestVersionRange:
|
|
|
168
171
|
)
|
|
169
172
|
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
""
|
|
174
|
+
class TestGitInstallation(unittest.TestCase):
|
|
175
|
+
def test_git_installed(self):
|
|
176
|
+
# Test if Git is installed on this system.
|
|
177
|
+
try:
|
|
178
|
+
# Attempt to get the Git version
|
|
179
|
+
result = subprocess.run(["git", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
180
|
+
# Check if the command was successful
|
|
181
|
+
self.assertTrue(result.returncode == 0, "Git is not installed or not in the PATH.")
|
|
182
|
+
except subprocess.CalledProcessError:
|
|
183
|
+
# If an error occurs, the test should fail
|
|
184
|
+
self.fail("Git is not installed or not in the PATH.")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestGitClone(unittest.TestCase):
|
|
188
|
+
def test_git_can_clone_repo(self):
|
|
189
|
+
# Test that Git can clone a repository
|
|
190
|
+
repo_url = "https://github.com/git/git" # Using a reliable repo that is always available
|
|
191
|
+
target_dir = "temp_repo"
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Ensure the target directory does not exist before cloning
|
|
195
|
+
if os.path.exists(target_dir):
|
|
196
|
+
shutil.rmtree(target_dir)
|
|
197
|
+
|
|
198
|
+
# Run 'git clone' and capture output
|
|
199
|
+
subprocess.run(['git', 'clone', repo_url, target_dir], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
200
|
+
except subprocess.CalledProcessError as e:
|
|
201
|
+
# If Git clone fails for any reason
|
|
202
|
+
self.fail(f"Git clone command failed: {e}")
|
|
203
|
+
except FileNotFoundError:
|
|
204
|
+
# If the 'git' command is not found
|
|
205
|
+
self.fail("Git is not installed on this system.")
|
|
206
|
+
finally:
|
|
207
|
+
# Clean up by removing the cloned directory if it exists
|
|
208
|
+
if os.path.exists(target_dir):
|
|
209
|
+
shutil.rmtree(target_dir)
|
|
@@ -100,21 +100,6 @@ class _baseVisualTest():
|
|
|
100
100
|
#make sure we start with a clean window
|
|
101
101
|
self.win.flip()
|
|
102
102
|
|
|
103
|
-
def test_auto_draw(self):
|
|
104
|
-
win = self.win
|
|
105
|
-
stims=[]
|
|
106
|
-
stims.append(visual.PatchStim(win))
|
|
107
|
-
stims.append(visual.ShapeStim(win))
|
|
108
|
-
stims.append(visual.TextStim(win))
|
|
109
|
-
for stim in stims:
|
|
110
|
-
assert stim.status==constants.NOT_STARTED
|
|
111
|
-
stim.autoDraw = True
|
|
112
|
-
assert stim.status==constants.STARTED
|
|
113
|
-
stim.autoDraw = False
|
|
114
|
-
assert stim.status==constants.FINISHED
|
|
115
|
-
assert stim.status==constants.STOPPED
|
|
116
|
-
"{}".format(stim) #check that str(xxx) is working
|
|
117
|
-
|
|
118
103
|
def test_imageAndGauss(self):
|
|
119
104
|
win = self.win
|
|
120
105
|
fileName = os.path.join(utils.TESTS_DATA_PATH, 'testimage.jpg')
|
|
@@ -219,47 +204,6 @@ class _baseVisualTest():
|
|
|
219
204
|
utils.compareScreenshot('circleHex_%s.png' %(self.contextName), win)
|
|
220
205
|
win.flip()
|
|
221
206
|
|
|
222
|
-
|
|
223
|
-
def test_gabor(self):
|
|
224
|
-
win = self.win
|
|
225
|
-
#using init
|
|
226
|
-
gabor = visual.PatchStim(win, mask='gauss', ori=-45,
|
|
227
|
-
pos=[0.6 * self.scaleFactor, -0.6 * self.scaleFactor],
|
|
228
|
-
sf=2.0 / self.scaleFactor, size=2 * self.scaleFactor,
|
|
229
|
-
interpolate=True)
|
|
230
|
-
gabor.draw()
|
|
231
|
-
utils.compareScreenshot('gabor1_%s.png' %(self.contextName), win)
|
|
232
|
-
win.flip()#AFTER compare screenshot
|
|
233
|
-
|
|
234
|
-
#using .set()
|
|
235
|
-
gabor.ori = 45
|
|
236
|
-
gabor.size -= 0.2 * self.scaleFactor
|
|
237
|
-
gabor.setColor([45, 30, 0.3], colorSpace='dkl')
|
|
238
|
-
gabor.sf += 0.2 / self.scaleFactor
|
|
239
|
-
gabor.pos += [-0.5*self.scaleFactor, 0.5*self.scaleFactor]
|
|
240
|
-
gabor.contrast = 0.8
|
|
241
|
-
gabor.opacity = 0.8
|
|
242
|
-
gabor.draw()
|
|
243
|
-
utils.compareScreenshot('gabor2_%s.png' %(self.contextName), win)
|
|
244
|
-
win.flip()
|
|
245
|
-
"{}".format(gabor) #check that str(xxx) is working
|
|
246
|
-
|
|
247
|
-
@pytest.mark.bufferimage
|
|
248
|
-
def test_bufferImage(self):
|
|
249
|
-
"""BufferImage inherits from ImageStim, so test .ori. .pos etc there not here
|
|
250
|
-
"""
|
|
251
|
-
win = self.win
|
|
252
|
-
gabor = visual.PatchStim(win, mask='gauss', ori=-45,
|
|
253
|
-
pos=[0.6*self.scaleFactor, -0.6*self.scaleFactor],
|
|
254
|
-
sf=2.0/self.scaleFactor, size=2*self.scaleFactor,
|
|
255
|
-
interpolate=True)
|
|
256
|
-
|
|
257
|
-
bufferImgStim = visual.BufferImageStim(self.win, stim=[gabor],
|
|
258
|
-
interpolate=True)
|
|
259
|
-
bufferImgStim.draw()
|
|
260
|
-
utils.compareScreenshot('bufferimg_gabor_%s.png' %(self.contextName), win, crit=8)
|
|
261
|
-
win.flip()
|
|
262
|
-
|
|
263
207
|
#def testMaskMatrix(self):
|
|
264
208
|
# #aims to draw the exact same stimulus as in testGabor, but using filters
|
|
265
209
|
# win=self.win
|
|
@@ -334,33 +278,6 @@ class _baseVisualTest():
|
|
|
334
278
|
utils.compareScreenshot('blend_add_%s.png' %self.contextName,
|
|
335
279
|
win, crit=20)
|
|
336
280
|
|
|
337
|
-
def test_mov(self):
|
|
338
|
-
win = self.win
|
|
339
|
-
if self.win.winType == 'pygame':
|
|
340
|
-
pytest.skip("movies only available for pyglet backend")
|
|
341
|
-
|
|
342
|
-
win.flip()
|
|
343
|
-
#construct full path to the movie file
|
|
344
|
-
fileName = os.path.join(utils.TESTS_DATA_PATH, 'testMovie.mp4')
|
|
345
|
-
#check if present
|
|
346
|
-
if not os.path.isfile(fileName):
|
|
347
|
-
raise IOError('Could not find movie file: %s'
|
|
348
|
-
% os.path.abspath(fileName))
|
|
349
|
-
#then do actual drawing
|
|
350
|
-
pos = [0.6*self.scaleFactor, -0.6*self.scaleFactor]
|
|
351
|
-
mov = visual.MovieStim3(win, fileName, pos=pos, noAudio=True)
|
|
352
|
-
mov.setFlipVert(True)
|
|
353
|
-
mov.setFlipHoriz(True)
|
|
354
|
-
threshold = 30
|
|
355
|
-
for frameN in range(10):
|
|
356
|
-
mov.draw()
|
|
357
|
-
|
|
358
|
-
if frameN==0:
|
|
359
|
-
utils.compareScreenshot('movFrame1_%s.png' %self.contextName,
|
|
360
|
-
win, crit=threshold)
|
|
361
|
-
win.flip()
|
|
362
|
-
"{}".format(mov) #check that str(xxx) is working
|
|
363
|
-
|
|
364
281
|
def test_rect(self):
|
|
365
282
|
win = self.win
|
|
366
283
|
rect = visual.Rect(win)
|
|
@@ -550,20 +467,6 @@ class _baseVisualTest():
|
|
|
550
467
|
win, crit=30)
|
|
551
468
|
#aperture should automatically disable on exit
|
|
552
469
|
|
|
553
|
-
def test_rating_scale(self):
|
|
554
|
-
if self.win.winType=='pygame':
|
|
555
|
-
pytest.skip("RatingScale not available on pygame")
|
|
556
|
-
# try to avoid text; avoid default / 'triangle' because it does not display on win XP
|
|
557
|
-
win = self.win
|
|
558
|
-
win.flip()
|
|
559
|
-
rs = visual.RatingScale(win, low=0, high=1, precision=100, size=3, pos=(0,-.4),
|
|
560
|
-
labels=[' ', ' '], scale=' ',
|
|
561
|
-
marker='glow', markerStart=0.7, markerColor='darkBlue', autoLog=False)
|
|
562
|
-
"{}".format(rs) #check that str(xxx) is working
|
|
563
|
-
rs.draw()
|
|
564
|
-
utils.compareScreenshot('ratingscale1_%s.png' %(self.contextName), win, crit=40.0)
|
|
565
|
-
win.flip()#AFTER compare screenshot
|
|
566
|
-
|
|
567
470
|
@skip_under_vm
|
|
568
471
|
def test_refresh_rate(self):
|
|
569
472
|
if self.win.winType=='pygame':
|
|
@@ -3,10 +3,8 @@ from pathlib import Path
|
|
|
3
3
|
from psychopy import visual, colors, core
|
|
4
4
|
from .test_basevisual import _TestUnitsMixin
|
|
5
5
|
from psychopy.tests.test_experiment.test_component_compile_python import _TestBoilerplateMixin
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from ..utils import TESTS_DATA_PATH
|
|
9
|
-
|
|
6
|
+
from psychopy.tests import utils
|
|
7
|
+
import pytest
|
|
10
8
|
|
|
11
9
|
class TestImage(_TestUnitsMixin, _TestBoilerplateMixin):
|
|
12
10
|
"""
|
|
@@ -17,7 +15,7 @@ class TestImage(_TestUnitsMixin, _TestBoilerplateMixin):
|
|
|
17
15
|
self.win = visual.Window()
|
|
18
16
|
self.obj = visual.ImageStim(
|
|
19
17
|
self.win,
|
|
20
|
-
str(Path(TESTS_DATA_PATH) / 'testimage.jpg'),
|
|
18
|
+
str(Path(utils.TESTS_DATA_PATH) / 'testimage.jpg'),
|
|
21
19
|
colorSpace='rgb1',
|
|
22
20
|
)
|
|
23
21
|
|
|
@@ -187,6 +185,9 @@ class TestImageAnimation:
|
|
|
187
185
|
"""
|
|
188
186
|
Check that images can be updated sufficiently fast to create frame animations
|
|
189
187
|
"""
|
|
188
|
+
# skip speed tests under vm
|
|
189
|
+
if utils.RUNNING_IN_VM:
|
|
190
|
+
pytest.skip()
|
|
190
191
|
# Create clock
|
|
191
192
|
clock = core.Clock()
|
|
192
193
|
# Try at each size
|