psychopy 2024.1.4__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/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 +1011 -942
- 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 +145 -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/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/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/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 +6 -5
- 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 -10
- 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 +31 -0
- 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/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-2024.1.4.dist-info → psychopy-2024.2.0.dist-info}/METADATA +8 -13
- {psychopy-2024.1.4.dist-info → psychopy-2024.2.0.dist-info}/RECORD +307 -213
- {psychopy-2024.1.4.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.4.dist-info → psychopy-2024.2.0.dist-info}/entry_points.txt +0 -0
- {psychopy-2024.1.4.dist-info → psychopy-2024.2.0.dist-info}/licenses/AUTHORS.md +0 -0
- {psychopy-2024.1.4.dist-info → psychopy-2024.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -29,18 +29,27 @@ if not importCtypesFailed:
|
|
|
29
29
|
class _SchedParams(ctypes.Structure):
|
|
30
30
|
_fields_ = [('sched_priority', ctypes.c_int)]
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
# Text that appears at the top of the dialog with provides instructions to the
|
|
33
|
+
# user.
|
|
34
|
+
_introStr = (
|
|
35
|
+
u"Could not set the thread priority with sched_setscheduler.\n"
|
|
36
|
+
u"For optimal performance on Linux, Psychtoolbox requires additional\n"
|
|
37
|
+
u"configuration changes to be made to this system by entering the\n"
|
|
38
|
+
u"following commands into your terminal:\n\n{cmdstr}"
|
|
39
|
+
)
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
If you are using the system's python (eg /usr/bin/python2.x), its highly
|
|
37
|
-
recommended to change cap_sys_nice back to normal afterwards:
|
|
38
|
-
'sudo setcap cap_sys_nice= %s'"""
|
|
39
|
-
|
|
40
|
-
warnNormal = ("Failed to set thread priority to normal with "
|
|
41
|
-
"sched_setscheduler.\n"
|
|
42
|
-
"Try: 'sudo setcap cap_sys_nice= %s'")
|
|
41
|
+
# config file path
|
|
42
|
+
_confPath = u"/etc/security/limits.d/99-psychopylimits.conf"
|
|
43
43
|
|
|
44
|
+
# these are the commands we want the user to run in their terminal
|
|
45
|
+
_cmdStr = (
|
|
46
|
+
u"sudo groupadd --force psychopy\n\n"
|
|
47
|
+
u"sudo usermod -a -G psychopy $USER\n\n"
|
|
48
|
+
u"sudo gedit {fpath}\n"
|
|
49
|
+
u"@psychopy - nice -20\n"
|
|
50
|
+
u"@psychopy - rtprio 50\n"
|
|
51
|
+
u"@psychopy - memlock unlimited")
|
|
52
|
+
warnMax = _introStr.format(cmdstr=_cmdStr.format(fpath=_confPath))
|
|
44
53
|
|
|
45
54
|
def rush(value=True, realtime=False):
|
|
46
55
|
"""Raise the priority of the current thread/process using
|
|
@@ -48,32 +57,21 @@ def rush(value=True, realtime=False):
|
|
|
48
57
|
|
|
49
58
|
realtime arg is not used in Linux implementation.
|
|
50
59
|
|
|
51
|
-
NB for rush() to work on
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
and maybe restart PsychoPy. If <sys.executable> is the system python,
|
|
55
|
-
it's important to restore it back to normal to avoid possible
|
|
56
|
-
side-effects. Alternatively, use a different python executable,
|
|
57
|
-
and change its cap_sys_nice.
|
|
60
|
+
NB for rush() to work on Linux requires that the script is run by
|
|
61
|
+
a user with sufficient permissions to raise the priority of a process.
|
|
62
|
+
In PsychoPy, we suggest adding the user to a group with these permissions.
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
see http://rt.et.redhat.com/wiki/images/8/8e/Rtprio.pdf
|
|
64
|
+
If this function returns `False`, see the log for instructions on how
|
|
65
|
+
to set up such a group.
|
|
62
66
|
"""
|
|
63
67
|
if importCtypesFailed:
|
|
64
68
|
return False
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
else: # set to RR with normal priority
|
|
73
|
-
schedParams = _SchedParams()
|
|
74
|
-
schedParams.sched_priority = c.sched_get_priority_min(SCHED_NORMAL)
|
|
75
|
-
err = c.sched_setscheduler(0, SCHED_NORMAL, ctypes.byref(schedParams))
|
|
76
|
-
if err == -1: # returns 0 if OK
|
|
77
|
-
logging.warning(warnNormal % sys.executable)
|
|
70
|
+
schedParams = _SchedParams()
|
|
71
|
+
sched = SCHED_RR if value else SCHED_NORMAL
|
|
72
|
+
schedParams.sched_priority = c.sched_get_priority_min(sched)
|
|
73
|
+
err = c.sched_setscheduler(0, sched, ctypes.byref(schedParams))
|
|
74
|
+
if err == -1:
|
|
75
|
+
logging.warning(warnMax)
|
|
78
76
|
|
|
79
|
-
return
|
|
77
|
+
return not err
|
|
@@ -18,10 +18,9 @@
|
|
|
18
18
|
# define THREAD_PRIORITY_HIGHEST 2
|
|
19
19
|
# define THREAD_PRIORITY_TIME_CRITICAL 15
|
|
20
20
|
|
|
21
|
-
import os
|
|
22
|
-
|
|
23
21
|
try:
|
|
24
22
|
from ctypes import windll
|
|
23
|
+
from ctypes.wintypes import HANDLE, DWORD, BOOL, INT, UINT
|
|
25
24
|
windll = windll.kernel32
|
|
26
25
|
importWindllFailed = False
|
|
27
26
|
except Exception:
|
|
@@ -47,6 +46,28 @@ ES_CONTINUOUS = 0x80000000
|
|
|
47
46
|
ES_DISPLAY_REQUIRED = 0x00000002
|
|
48
47
|
ES_SYSTEM_REQUIRED = 0x00000001
|
|
49
48
|
|
|
49
|
+
GetCurrentProcessId = windll.GetCurrentProcessId
|
|
50
|
+
GetCurrentProcessId.restype = HANDLE
|
|
51
|
+
|
|
52
|
+
OpenProcess = windll.OpenProcess
|
|
53
|
+
OpenProcess.restype = HANDLE
|
|
54
|
+
OpenProcess.argtypes = (DWORD, BOOL, DWORD)
|
|
55
|
+
|
|
56
|
+
GetCurrentThread = windll.GetCurrentThread
|
|
57
|
+
GetCurrentThread.restype = HANDLE
|
|
58
|
+
|
|
59
|
+
SetPriorityClass = windll.SetPriorityClass
|
|
60
|
+
SetPriorityClass.restype = BOOL
|
|
61
|
+
SetPriorityClass.argtypes = (HANDLE, DWORD)
|
|
62
|
+
|
|
63
|
+
SetThreadPriority = windll.SetThreadPriority
|
|
64
|
+
SetThreadPriority.restype = BOOL
|
|
65
|
+
SetThreadPriority.argtypes = (HANDLE, INT)
|
|
66
|
+
|
|
67
|
+
SetThreadExecutionState = windll.SetThreadExecutionState
|
|
68
|
+
SetThreadExecutionState.restype = UINT
|
|
69
|
+
SetThreadExecutionState.argtypes = (UINT,)
|
|
70
|
+
|
|
50
71
|
|
|
51
72
|
def rush(enable=True, realtime=False):
|
|
52
73
|
"""Raise the priority of the current thread/process.
|
|
@@ -62,21 +83,25 @@ def rush(enable=True, realtime=False):
|
|
|
62
83
|
return False
|
|
63
84
|
|
|
64
85
|
pr_rights = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION
|
|
65
|
-
pr = windll.OpenProcess(pr_rights, FALSE,
|
|
86
|
+
pr = windll.OpenProcess(pr_rights, FALSE, GetCurrentProcessId())
|
|
66
87
|
thr = windll.GetCurrentThread()
|
|
67
88
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
89
|
+
# In this context, non-zero is success and zero is error
|
|
90
|
+
out = 1
|
|
91
|
+
|
|
92
|
+
if enable:
|
|
93
|
+
if not realtime:
|
|
94
|
+
out = SetPriorityClass(pr, HIGH_PRIORITY_CLASS) != 0
|
|
95
|
+
out &= SetThreadPriority(thr, THREAD_PRIORITY_HIGHEST) != 0
|
|
72
96
|
else:
|
|
73
|
-
|
|
74
|
-
|
|
97
|
+
out = SetPriorityClass(pr, REALTIME_PRIORITY_CLASS) != 0
|
|
98
|
+
out &= SetThreadPriority(thr, THREAD_PRIORITY_TIME_CRITICAL) != 0
|
|
75
99
|
|
|
76
100
|
else:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
out = SetPriorityClass(pr, NORMAL_PRIORITY_CLASS) != 0
|
|
102
|
+
out &= SetThreadPriority(thr, THREAD_PRIORITY_NORMAL) != 0
|
|
103
|
+
|
|
104
|
+
return out != 0
|
|
80
105
|
|
|
81
106
|
|
|
82
107
|
def waitForVBL():
|
|
@@ -95,5 +120,5 @@ def sendStayAwake():
|
|
|
95
120
|
Currently supported on: windows, macOS.
|
|
96
121
|
"""
|
|
97
122
|
code = ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED
|
|
98
|
-
success =
|
|
123
|
+
success = SetThreadExecutionState(code)
|
|
99
124
|
return success
|
psychopy/plugins/__init__.py
CHANGED
|
@@ -29,20 +29,19 @@ import sys
|
|
|
29
29
|
import inspect
|
|
30
30
|
import collections
|
|
31
31
|
import hashlib
|
|
32
|
-
import importlib
|
|
33
|
-
import psychopy.tools.pkgtools as pkgtools
|
|
34
|
-
import pkg_resources
|
|
35
|
-
import psychopy.tools.pkgtools as pkgtools
|
|
32
|
+
import importlib, importlib.metadata
|
|
36
33
|
from psychopy import logging
|
|
37
34
|
from psychopy.preferences import prefs
|
|
38
35
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
# Configure the environment to use our custom site-packages location for
|
|
37
|
+
# user-installed packages (i.e. plugins).
|
|
38
|
+
USER_PACKAGES_PATH = str(prefs.paths['userPackages'])
|
|
39
|
+
# check if we're in a virtual environment or not
|
|
40
|
+
inVenv = hasattr(sys, 'real_prefix') or sys.prefix != sys.base_prefix
|
|
41
|
+
|
|
42
|
+
# add the plugins folder to the path
|
|
43
|
+
if not inVenv and USER_PACKAGES_PATH not in sys.path:
|
|
44
|
+
sys.path.insert(0, USER_PACKAGES_PATH) # add to path
|
|
46
45
|
|
|
47
46
|
# Keep track of plugins that have been loaded. Keys are plugin names and values
|
|
48
47
|
# are their entry point mappings.
|
|
@@ -61,6 +60,40 @@ _failed_plugins_ = []
|
|
|
61
60
|
# Functions
|
|
62
61
|
#
|
|
63
62
|
|
|
63
|
+
def getEntryPointGroup(group, subgroups=False):
|
|
64
|
+
"""
|
|
65
|
+
Get all entry points which target a specific group.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
group : str
|
|
70
|
+
Group to look for (e.g. "psychopy.experiment.components" for plugin Components)
|
|
71
|
+
subgroups : bool
|
|
72
|
+
If True, then will also look for subgroups (e.g. "psychopy.experiment" will also return
|
|
73
|
+
entry points for "psychopy.experiment.components")
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
list[importlib.metadata.Entrypoint]
|
|
78
|
+
List of EntryPoint objects for the given group
|
|
79
|
+
"""
|
|
80
|
+
# start off with no entry points or sections
|
|
81
|
+
entryPoints = []
|
|
82
|
+
|
|
83
|
+
if subgroups:
|
|
84
|
+
# if searching subgroups, iterate through entry point groups
|
|
85
|
+
for thisGroup, eps in importlib.metadata.entry_points().items():
|
|
86
|
+
# get entry points within matching group
|
|
87
|
+
if thisGroup.startswith(group):
|
|
88
|
+
# add to list of all entry points
|
|
89
|
+
entryPoints += eps
|
|
90
|
+
else:
|
|
91
|
+
# otherwise, just get the requested group
|
|
92
|
+
entryPoints += importlib.metadata.entry_points().get(group, [])
|
|
93
|
+
|
|
94
|
+
return entryPoints
|
|
95
|
+
|
|
96
|
+
|
|
64
97
|
def resolveObjectFromName(name, basename=None, resolve=True, error=True):
|
|
65
98
|
"""Get an object within a module's namespace using a fully-qualified or
|
|
66
99
|
relative dotted name.
|
|
@@ -254,7 +287,7 @@ def getBundleInstallTarget(projectName):
|
|
|
254
287
|
|
|
255
288
|
"""
|
|
256
289
|
return os.path.join(
|
|
257
|
-
prefs.paths['packages'],
|
|
290
|
+
prefs.paths['packages'], projectName)
|
|
258
291
|
|
|
259
292
|
|
|
260
293
|
def refreshBundlePaths():
|
|
@@ -285,12 +318,18 @@ def refreshBundlePaths():
|
|
|
285
318
|
pluginTopLevelDirs = os.listdir(pluginBaseDir)
|
|
286
319
|
for pluginDir in pluginTopLevelDirs:
|
|
287
320
|
fullPath = os.path.join(pluginBaseDir, pluginDir)
|
|
288
|
-
allDists =
|
|
321
|
+
allDists = importlib.metadata.distributions(path=pluginDir)
|
|
289
322
|
if not allDists: # no packages found, move on
|
|
290
323
|
continue
|
|
291
324
|
|
|
292
325
|
# does the sud-directory contain an appropriately named distribution?
|
|
293
|
-
validDist =
|
|
326
|
+
validDist = False
|
|
327
|
+
for dist in allDists:
|
|
328
|
+
if sys.version.startswith("3.8"):
|
|
329
|
+
distName = dist.metadata['name']
|
|
330
|
+
else:
|
|
331
|
+
distName = dist.name
|
|
332
|
+
validDist = validDist or distName == pluginDir
|
|
294
333
|
if not validDist:
|
|
295
334
|
continue
|
|
296
335
|
|
|
@@ -301,11 +340,41 @@ def refreshBundlePaths():
|
|
|
301
340
|
foundBundles.append(pluginDir)
|
|
302
341
|
|
|
303
342
|
# refresh package index since the working set is now stale
|
|
304
|
-
|
|
343
|
+
scanPlugins()
|
|
305
344
|
|
|
306
345
|
return foundBundles
|
|
307
346
|
|
|
308
347
|
|
|
348
|
+
def getPluginConfigPath(plugin):
|
|
349
|
+
"""Get the path to the configuration file for a plugin.
|
|
350
|
+
|
|
351
|
+
This function returns the path to folder alloted to a plugin for storing
|
|
352
|
+
configuration files. This is useful for plugins that require user settings
|
|
353
|
+
to be stored in a file.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
plugin : str
|
|
358
|
+
Name of the plugin package to get the configuration file for.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
str
|
|
363
|
+
Path to the configuration file for the plugin.
|
|
364
|
+
|
|
365
|
+
"""
|
|
366
|
+
# check if the plugin is installed first
|
|
367
|
+
if plugin not in _installed_plugins_:
|
|
368
|
+
raise ValueError("Plugin `{}` is not installed.".format(plugin))
|
|
369
|
+
|
|
370
|
+
# get the config directory
|
|
371
|
+
import pathlib
|
|
372
|
+
configDir = pathlib.Path(prefs.paths['configs']) / 'plugins' / plugin
|
|
373
|
+
configDir.mkdir(parents=True, exist_ok=True)
|
|
374
|
+
|
|
375
|
+
return configDir
|
|
376
|
+
|
|
377
|
+
|
|
309
378
|
def installPlugin(package, local=True, upgrade=False, forceReinstall=False,
|
|
310
379
|
noDeps=False):
|
|
311
380
|
"""Install a plugin package.
|
|
@@ -327,7 +396,8 @@ def installPlugin(package, local=True, upgrade=False, forceReinstall=False,
|
|
|
327
396
|
|
|
328
397
|
"""
|
|
329
398
|
# determine where to install the package
|
|
330
|
-
installWhere =
|
|
399
|
+
installWhere = USER_PACKAGES_PATH if local else None
|
|
400
|
+
import psychopy.tools.pkgtools as pkgtools
|
|
331
401
|
pkgtools.installPackage(
|
|
332
402
|
package,
|
|
333
403
|
target=installWhere,
|
|
@@ -354,30 +424,27 @@ def scanPlugins():
|
|
|
354
424
|
|
|
355
425
|
"""
|
|
356
426
|
global _installed_plugins_
|
|
357
|
-
_installed_plugins_ = {} # clear
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if dist.location not in pkg_resources.working_set.entries:
|
|
379
|
-
pkg_resources.working_set.add(dist)
|
|
380
|
-
|
|
427
|
+
_installed_plugins_ = {} # clear the cache
|
|
428
|
+
# iterate through installed packages
|
|
429
|
+
for dist in importlib.metadata.distributions(path=sys.path + [USER_PACKAGES_PATH]):
|
|
430
|
+
# map all entry points
|
|
431
|
+
for ep in dist.entry_points:
|
|
432
|
+
# skip entry points which don't target PsychoPy
|
|
433
|
+
if not ep.group.startswith("psychopy"):
|
|
434
|
+
continue
|
|
435
|
+
# make sure we have an entry for this distribution
|
|
436
|
+
if sys.version.startswith("3.8"):
|
|
437
|
+
distName = dist.metadata['name']
|
|
438
|
+
else:
|
|
439
|
+
distName = dist.name
|
|
440
|
+
if distName not in _installed_plugins_:
|
|
441
|
+
_installed_plugins_[distName] = {}
|
|
442
|
+
# make sure we have an entry for this group
|
|
443
|
+
if ep.group not in _installed_plugins_[distName]:
|
|
444
|
+
_installed_plugins_[distName][ep.group] = {}
|
|
445
|
+
# map entry point
|
|
446
|
+
_installed_plugins_[distName][ep.group][ep.name] = ep
|
|
447
|
+
|
|
381
448
|
return len(_installed_plugins_)
|
|
382
449
|
|
|
383
450
|
|
|
@@ -507,51 +574,6 @@ def isStartUpPlugin(plugin):
|
|
|
507
574
|
return plugin in listPlugins(which='startup')
|
|
508
575
|
|
|
509
576
|
|
|
510
|
-
def loadPluginBuilderElements(plugin):
|
|
511
|
-
"""
|
|
512
|
-
Load entry points from plugin which are relevant to Builder, e.g.
|
|
513
|
-
Component/Routine extensions for listing available hardware backends.
|
|
514
|
-
|
|
515
|
-
Parameters
|
|
516
|
-
----------
|
|
517
|
-
plugin : str
|
|
518
|
-
Name of the plugin package to load. This usually refers to the package
|
|
519
|
-
or project name.
|
|
520
|
-
|
|
521
|
-
Returns
|
|
522
|
-
-------
|
|
523
|
-
bool
|
|
524
|
-
`True` if successful, `False` if failed.
|
|
525
|
-
"""
|
|
526
|
-
# if plugin has already failed to load once, don't try again
|
|
527
|
-
if plugin in _failed_plugins_:
|
|
528
|
-
return False
|
|
529
|
-
# get entry points for plugin
|
|
530
|
-
ep = pluginEntryPoints(plugin)
|
|
531
|
-
# define modules in which entry points are relevant to Builder
|
|
532
|
-
modules = (
|
|
533
|
-
"psychopy.experiment.routines",
|
|
534
|
-
"psychopy.experiment.components",
|
|
535
|
-
)
|
|
536
|
-
# get any points pointing to these modules
|
|
537
|
-
relevantPoints = []
|
|
538
|
-
for mod in modules:
|
|
539
|
-
pts = ep.get(mod, {})
|
|
540
|
-
relevantPoints += list(pts.values())
|
|
541
|
-
# import all relevant classes
|
|
542
|
-
for point in relevantPoints:
|
|
543
|
-
try:
|
|
544
|
-
importlib.import_module(point.module_name)
|
|
545
|
-
return True
|
|
546
|
-
except:
|
|
547
|
-
# if import failed for any reason, log error and mark failure
|
|
548
|
-
logging.error(
|
|
549
|
-
f"Failed to load {point.module_name}.{point.name} from plugin {plugin}."
|
|
550
|
-
)
|
|
551
|
-
_failed_plugins_.append(plugin)
|
|
552
|
-
return False
|
|
553
|
-
|
|
554
|
-
|
|
555
577
|
def loadPlugin(plugin):
|
|
556
578
|
"""Load a plugin to extend PsychoPy.
|
|
557
579
|
|
|
@@ -576,10 +598,11 @@ def loadPlugin(plugin):
|
|
|
576
598
|
will continue running. This may be undesirable in some cases, since features
|
|
577
599
|
the plugin provides may be needed at some point and would lead to undefined
|
|
578
600
|
behavior if not present. If you want to halt the application if a plugin
|
|
579
|
-
fails to load, consider using :func:`requirePlugin
|
|
601
|
+
fails to load, consider using :func:`requirePlugin` to assert that a plugin
|
|
602
|
+
is loaded before continuing.
|
|
580
603
|
|
|
581
604
|
It is advised that you use this function only when using PsychoPy as a
|
|
582
|
-
library. If using the
|
|
605
|
+
library. If using the Builder or Coder GUI, it is recommended that you use
|
|
583
606
|
the plugin dialog to enable plugins for PsychoPy sessions spawned by the
|
|
584
607
|
experiment runner. However, you can still use this function if you want to
|
|
585
608
|
load additional plugins for a given experiment, having their effects
|
|
@@ -694,26 +717,43 @@ def loadPlugin(plugin):
|
|
|
694
717
|
# that the entry points are valid. This prevents plugins from being
|
|
695
718
|
# partially loaded which can cause all sorts of undefined behaviour.
|
|
696
719
|
for attr, ep in attrs.items():
|
|
720
|
+
try:
|
|
721
|
+
# parse the module name from the entry point value
|
|
722
|
+
if ':' in ep.value:
|
|
723
|
+
module_name, _ = ep.value.split(':', 1)
|
|
724
|
+
else:
|
|
725
|
+
module_name = ep.value
|
|
726
|
+
module_name = module_name.split(".")[0]
|
|
727
|
+
except ValueError:
|
|
728
|
+
logging.error(
|
|
729
|
+
"Plugin `{}` entry point `{}` is not formatted correctly. "
|
|
730
|
+
"Skipping.".format(plugin, ep))
|
|
731
|
+
|
|
732
|
+
if plugin not in _failed_plugins_:
|
|
733
|
+
_failed_plugins_.append(plugin)
|
|
734
|
+
|
|
735
|
+
return False
|
|
736
|
+
|
|
697
737
|
# Load the module the entry point belongs to, this happens
|
|
698
738
|
# anyways when .load() is called, but we get to access it before
|
|
699
739
|
# we start binding. If the module has already been loaded, don't
|
|
700
740
|
# do this again.
|
|
701
|
-
if
|
|
741
|
+
if module_name not in sys.modules:
|
|
702
742
|
# Do stuff before loading entry points here, any executable code
|
|
703
743
|
# in the module will run to configure it.
|
|
704
744
|
try:
|
|
705
|
-
imp = importlib.import_module(
|
|
745
|
+
imp = importlib.import_module(module_name)
|
|
706
746
|
except (ModuleNotFoundError, ImportError):
|
|
707
747
|
importSuccess = False
|
|
708
748
|
logging.error(
|
|
709
749
|
"Plugin `{}` entry point requires module `{}`, but it "
|
|
710
|
-
"cannot be imported.".format(plugin,
|
|
750
|
+
"cannot be imported.".format(plugin, module_name))
|
|
711
751
|
except:
|
|
712
752
|
importSuccess = False
|
|
713
753
|
logging.error(
|
|
714
754
|
"Plugin `{}` entry point requires module `{}`, but an "
|
|
715
755
|
"error occurred while loading it.".format(
|
|
716
|
-
plugin,
|
|
756
|
+
plugin, module_name))
|
|
717
757
|
else:
|
|
718
758
|
importSuccess = True
|
|
719
759
|
|
|
@@ -742,6 +782,13 @@ def loadPlugin(plugin):
|
|
|
742
782
|
# return False
|
|
743
783
|
try:
|
|
744
784
|
ep = ep.load() # load the entry point
|
|
785
|
+
|
|
786
|
+
# Raise a warning if the plugin is being loaded from a zip file.
|
|
787
|
+
if '.zip' in inspect.getfile(ep):
|
|
788
|
+
logging.warning(
|
|
789
|
+
"Plugin `{}` is being loaded from a zip file. This may "
|
|
790
|
+
"cause issues with the plugin's functionality.".format(plugin))
|
|
791
|
+
|
|
745
792
|
except ImportError as e:
|
|
746
793
|
logging.error(
|
|
747
794
|
"Failed to load entry point `{}` of plugin `{}`. "
|
|
@@ -751,21 +798,11 @@ def loadPlugin(plugin):
|
|
|
751
798
|
if plugin not in _failed_plugins_:
|
|
752
799
|
_failed_plugins_.append(plugin)
|
|
753
800
|
|
|
754
|
-
return False
|
|
755
|
-
except pkg_resources.DistributionNotFound:
|
|
756
|
-
logging.error(
|
|
757
|
-
"Failed to load entry point `{}` of plugin `{}` due to "
|
|
758
|
-
"missing distribution required by the application."
|
|
759
|
-
"Skipping.".format(str(ep), plugin))
|
|
760
|
-
|
|
761
|
-
if plugin not in _failed_plugins_:
|
|
762
|
-
_failed_plugins_.append(plugin)
|
|
763
|
-
|
|
764
801
|
return False
|
|
765
802
|
except Exception: # catch everything else
|
|
766
803
|
logging.error(
|
|
767
804
|
"Failed to load entry point `{}` of plugin `{}` for unknown"
|
|
768
|
-
"reasons. Skipping.".format(str(ep), plugin))
|
|
805
|
+
" reasons. Skipping.".format(str(ep), plugin))
|
|
769
806
|
|
|
770
807
|
if plugin not in _failed_plugins_:
|
|
771
808
|
_failed_plugins_.append(plugin)
|
|
@@ -784,10 +821,16 @@ def loadPlugin(plugin):
|
|
|
784
821
|
# add the object to the module or unbound class
|
|
785
822
|
setattr(targObj, attr, ep)
|
|
786
823
|
logging.debug(
|
|
787
|
-
"Assigning
|
|
824
|
+
"Assigning the entry point `{}` to `{}`.".format(
|
|
788
825
|
ep.__name__, fqn + '.' + attr))
|
|
789
826
|
|
|
790
827
|
# --- handle special cases ---
|
|
828
|
+
# Note - We're going to handle special cases here for now, but
|
|
829
|
+
# this will eventually be handled by special functions in the
|
|
830
|
+
# target modules (e.g. `getAllPhotometers()` in
|
|
831
|
+
# `psychopy.hardware.photometer`) which can detect the loaded
|
|
832
|
+
# attribute inside the module and add it to a collection.
|
|
833
|
+
|
|
791
834
|
if fqn == 'psychopy.visual.backends': # if window backend
|
|
792
835
|
_registerWindowBackend(attr, ep)
|
|
793
836
|
elif fqn == 'psychopy.experiment.components': # if component
|
|
@@ -990,18 +1033,8 @@ def pluginMetadata(plugin):
|
|
|
990
1033
|
"Plugin `{}` is not installed or does not have entry points for "
|
|
991
1034
|
"PsychoPy.".format(plugin))
|
|
992
1035
|
|
|
993
|
-
pkg =
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
metadict = {}
|
|
997
|
-
for line in metadata.split('\n'):
|
|
998
|
-
if not line:
|
|
999
|
-
continue
|
|
1000
|
-
|
|
1001
|
-
line = line.strip().split(': ')
|
|
1002
|
-
if len(line) == 2:
|
|
1003
|
-
field, value = line
|
|
1004
|
-
metadict[field] = value
|
|
1036
|
+
pkg = importlib.metadata.distribution(plugin)
|
|
1037
|
+
metadict = dict(pkg.metadata)
|
|
1005
1038
|
|
|
1006
1039
|
return metadict
|
|
1007
1040
|
|
|
@@ -1072,7 +1105,6 @@ def activatePlugins(which='all'):
|
|
|
1072
1105
|
# load each plugin and apply any changes to Builder
|
|
1073
1106
|
for plugin in listPlugins(which):
|
|
1074
1107
|
loadPlugin(plugin)
|
|
1075
|
-
loadPluginBuilderElements(plugin)
|
|
1076
1108
|
|
|
1077
1109
|
|
|
1078
1110
|
# Keep track of currently installed window backends. When a window is loaded,
|
psychopy/plugins/util.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def getEntryPoints(module, submodules=True, flatten=True):
|
|
5
|
+
"""
|
|
6
|
+
Get entry points which target a particular module.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
module : str
|
|
11
|
+
Import string for the target module (e.g.
|
|
12
|
+
`"psychopy.iohub.devices"`)
|
|
13
|
+
submodules : bool, optional
|
|
14
|
+
If True, will also get entry points which target a
|
|
15
|
+
submodule of the given module. By default True.
|
|
16
|
+
flatten : bool, optional
|
|
17
|
+
If True, will return a flat list of entry points. If
|
|
18
|
+
False, will return a dict arranged by target group. By
|
|
19
|
+
default True.
|
|
20
|
+
"""
|
|
21
|
+
# start off with a blank list/dict
|
|
22
|
+
entryPointsList = []
|
|
23
|
+
entryPointsDict = {}
|
|
24
|
+
# iterate through groups
|
|
25
|
+
for group, points in importlib.metadata.entry_points().items():
|
|
26
|
+
# does this group correspond to the requested module?
|
|
27
|
+
if submodules:
|
|
28
|
+
targeted = group.startswith(module)
|
|
29
|
+
else:
|
|
30
|
+
targeted = group == module
|
|
31
|
+
# if group is targeted, add entry points
|
|
32
|
+
if targeted:
|
|
33
|
+
entryPointsList += points
|
|
34
|
+
entryPointsDict[group] = points
|
|
35
|
+
# return list or dict according to flatten arg
|
|
36
|
+
if flatten:
|
|
37
|
+
return entryPointsList
|
|
38
|
+
else:
|
|
39
|
+
return entryPointsDict
|
psychopy/preferences/Darwin.spec
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
# display tips when starting PsychoPy
|
|
57
57
|
showStartupTips = boolean(default='True')
|
|
58
58
|
# what windows to display when PsychoPy starts
|
|
59
|
-
defaultView = option('builder', 'coder', 'runner', 'all', default='
|
|
59
|
+
defaultView = option('last', 'builder', 'coder', 'runner', 'all', default='last')
|
|
60
60
|
# reset preferences to defaults on next restart of PsychoPy
|
|
61
61
|
resetPrefs = boolean(default='False') # default must be False!
|
|
62
62
|
# save any unsaved preferences before closing the window
|
|
@@ -149,8 +149,10 @@
|
|
|
149
149
|
forceWindowed = boolean(default=True)
|
|
150
150
|
# What window size to use when forced to windowed mode
|
|
151
151
|
forcedWindowSize = list(default=list(800, 600))
|
|
152
|
-
# How much output to include in the log
|
|
152
|
+
# How much output to include in the log file when piloting ('error' is fewest messages, 'debug' is most)
|
|
153
153
|
pilotLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='debug')
|
|
154
|
+
# How much output to display in the console / app when piloting ('error' is fewest messages, 'debug' is most).
|
|
155
|
+
pilotConsoleLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='warning')
|
|
154
156
|
# Show an orange border around the window when in piloting mode
|
|
155
157
|
showPilotingIndicator = boolean(default=True)
|
|
156
158
|
# Prevent experiment from enabling rush mode when piloting
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
# display tips when starting PsychoPy
|
|
57
57
|
showStartupTips = boolean(default='True')
|
|
58
58
|
# what windows to display when PsychoPy starts
|
|
59
|
-
defaultView = option('builder', 'coder', 'runner', 'all', default='
|
|
59
|
+
defaultView = option('last', 'builder', 'coder', 'runner', 'all', default='last')
|
|
60
60
|
# reset preferences to defaults on next restart of PsychoPy
|
|
61
61
|
resetPrefs = boolean(default='False') # default must be False!
|
|
62
62
|
# save any unsaved preferences before closing the window
|
|
@@ -149,8 +149,10 @@
|
|
|
149
149
|
forceWindowed = boolean(default=True)
|
|
150
150
|
# What window size to use when forced to windowed mode
|
|
151
151
|
forcedWindowSize = list(default=list(800, 600))
|
|
152
|
-
# How much output to include in the log
|
|
152
|
+
# How much output to include in the log file when piloting ('error' is fewest messages, 'debug' is most)
|
|
153
153
|
pilotLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='debug')
|
|
154
|
+
# How much output to display in the console / app when piloting ('error' is fewest messages, 'debug' is most).
|
|
155
|
+
pilotConsoleLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='warning')
|
|
154
156
|
# Show an orange border around the window when in piloting mode
|
|
155
157
|
showPilotingIndicator = boolean(default=True)
|
|
156
158
|
# Prevent experiment from enabling rush mode when piloting
|
psychopy/preferences/Linux.spec
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
# display tips when starting PsychoPy
|
|
57
57
|
showStartupTips = boolean(default='True')
|
|
58
58
|
# what windows to display when PsychoPy starts
|
|
59
|
-
defaultView = option('builder', 'coder', 'runner', 'all', default='
|
|
59
|
+
defaultView = option('last', 'builder', 'coder', 'runner', 'all', default='last')
|
|
60
60
|
# reset preferences to defaults on next restart of PsychoPy
|
|
61
61
|
resetPrefs = boolean(default='False') # default must be False!
|
|
62
62
|
# save any unsaved preferences before closing the window
|
|
@@ -149,8 +149,10 @@
|
|
|
149
149
|
forceWindowed = boolean(default=True)
|
|
150
150
|
# What window size to use when forced to windowed mode
|
|
151
151
|
forcedWindowSize = list(default=list(800, 600))
|
|
152
|
-
# How much output to include in the log
|
|
152
|
+
# How much output to include in the log file when piloting ('error' is fewest messages, 'debug' is most)
|
|
153
153
|
pilotLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='debug')
|
|
154
|
+
# How much output to display in the console / app when piloting ('error' is fewest messages, 'debug' is most).
|
|
155
|
+
pilotConsoleLoggingLevel = option('error', 'warning', 'data', 'exp', 'info', 'debug', default='warning')
|
|
154
156
|
# Show an orange border around the window when in piloting mode
|
|
155
157
|
showPilotingIndicator = boolean(default=True)
|
|
156
158
|
# Prevent experiment from enabling rush mode when piloting
|