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
|
@@ -102,6 +102,42 @@ class Test_textbox(_TestColorMixin, _TestUnitsMixin, _TestBoilerplateMixin):
|
|
|
102
102
|
#self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / filename)
|
|
103
103
|
utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / filename, self.win, crit=20)
|
|
104
104
|
|
|
105
|
+
def test_ori(self):
|
|
106
|
+
# setup textbox
|
|
107
|
+
self.textbox.color = "black"
|
|
108
|
+
self.textbox.fillColor = "white"
|
|
109
|
+
self.textbox.units = "pix"
|
|
110
|
+
self.textbox.size = (100, 50)
|
|
111
|
+
self.textbox.pos = (0, 0)
|
|
112
|
+
self.textbox.letterHeight = 5
|
|
113
|
+
# define params to use
|
|
114
|
+
orientations = [
|
|
115
|
+
0, 120, 180, 240,
|
|
116
|
+
]
|
|
117
|
+
anchors = [
|
|
118
|
+
"top left", "center", "bottom right",
|
|
119
|
+
]
|
|
120
|
+
# try each combination
|
|
121
|
+
for ori in orientations:
|
|
122
|
+
for anchor in anchors:
|
|
123
|
+
# flip
|
|
124
|
+
self.win.flip()
|
|
125
|
+
# set params
|
|
126
|
+
self.textbox.ori = ori
|
|
127
|
+
self.textbox.anchor = anchor
|
|
128
|
+
self.textbox._layout()
|
|
129
|
+
# draw
|
|
130
|
+
self.textbox.draw()
|
|
131
|
+
# construct exemplar filename
|
|
132
|
+
exemplar = f"test_ori_{ori}_{anchor}.png"
|
|
133
|
+
# check/make exemplar
|
|
134
|
+
# self.win.getMovieFrame(buffer='back').save(
|
|
135
|
+
# Path(utils.TESTS_DATA_PATH) / "Test_textbox" / exemplar
|
|
136
|
+
# )
|
|
137
|
+
utils.compareScreenshot(
|
|
138
|
+
Path(utils.TESTS_DATA_PATH) / "Test_textbox" / exemplar, self.win, crit=20
|
|
139
|
+
)
|
|
140
|
+
|
|
105
141
|
def test_colors(self):
|
|
106
142
|
# Do base tests
|
|
107
143
|
_TestColorMixin.test_colors(self)
|
psychopy/tests/utils.py
CHANGED
|
@@ -6,6 +6,7 @@ import shutil
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import io
|
|
8
8
|
from psychopy import logging, colors
|
|
9
|
+
from psychopy.tools import systemtools
|
|
9
10
|
|
|
10
11
|
try:
|
|
11
12
|
from PIL import Image
|
|
@@ -14,6 +15,9 @@ except ImportError:
|
|
|
14
15
|
|
|
15
16
|
import pytest
|
|
16
17
|
|
|
18
|
+
# boolean indicating whether tests are running in a VM
|
|
19
|
+
RUNNING_IN_VM = systemtools.isVM_CI() is not None
|
|
20
|
+
|
|
17
21
|
# define the path where to find testing data
|
|
18
22
|
# so tests could be ran from any location
|
|
19
23
|
TESTS_PATH = abspath(dirname(__file__))
|
psychopy/tools/filetools.py
CHANGED
|
@@ -74,7 +74,7 @@ def _synonymiseExtensions(assets):
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
# Names accepted by stimulus classes & the filename of the default stimulus to use
|
|
77
|
-
defaultStimRoot = Path(__file__).parent.parent / "
|
|
77
|
+
defaultStimRoot = Path(__file__).parent.parent / "assets"
|
|
78
78
|
defaultStim = {
|
|
79
79
|
# Image stimuli
|
|
80
80
|
"default.png": "default.png",
|
psychopy/tools/pkgtools.py
CHANGED
|
@@ -26,8 +26,7 @@ import subprocess as sp
|
|
|
26
26
|
from psychopy.preferences import prefs
|
|
27
27
|
from psychopy.localization import _translate
|
|
28
28
|
import psychopy.logging as logging
|
|
29
|
-
import importlib
|
|
30
|
-
import pkg_resources
|
|
29
|
+
import importlib, importlib.metadata, importlib.resources
|
|
31
30
|
import sys
|
|
32
31
|
import os
|
|
33
32
|
import os.path
|
|
@@ -37,23 +36,69 @@ import site
|
|
|
37
36
|
|
|
38
37
|
# On import we want to configure the user site-packages dir and add it to the
|
|
39
38
|
# import path.
|
|
40
|
-
|
|
41
39
|
# set user site-packages dir
|
|
42
|
-
|
|
43
|
-
site.
|
|
44
|
-
|
|
40
|
+
if os.environ.get('PSYCHOPYNOPACKAGES', '0') == '1':
|
|
41
|
+
site.ENABLE_USER_SITE = True
|
|
42
|
+
site.USER_SITE = str(prefs.paths['userPackages'])
|
|
43
|
+
site.USER_BASE = None
|
|
44
|
+
logging.debug(
|
|
45
|
+
'User site-packages dir set to: %s' % site.getusersitepackages())
|
|
46
|
+
|
|
47
|
+
# add paths from main plugins/packages (installed by plugins manager)
|
|
48
|
+
site.addsitedir(prefs.paths['userPackages']) # user site-packages
|
|
49
|
+
site.addsitedir(prefs.paths['userInclude']) # user include
|
|
50
|
+
site.addsitedir(prefs.paths['packages']) # base package dir
|
|
45
51
|
|
|
46
52
|
if not site.USER_SITE in sys.path:
|
|
47
53
|
site.addsitedir(site.getusersitepackages())
|
|
48
54
|
|
|
49
|
-
# add packages dir to import path
|
|
50
|
-
if prefs.paths['packages'] not in pkg_resources.working_set.entries:
|
|
51
|
-
pkg_resources.working_set.add_entry(prefs.paths['packages'])
|
|
52
|
-
|
|
53
55
|
# cache list of packages to speed up checks
|
|
54
56
|
_installedPackageCache = []
|
|
55
57
|
_installedPackageNamesCache = []
|
|
56
58
|
|
|
59
|
+
# reference the user packages path
|
|
60
|
+
USER_PACKAGES_PATH = str(prefs.paths['userPackages'])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PluginStub:
|
|
64
|
+
"""
|
|
65
|
+
Class to handle classes which have moved out to plugins.
|
|
66
|
+
|
|
67
|
+
Example
|
|
68
|
+
-------
|
|
69
|
+
```
|
|
70
|
+
class NoiseStim(PluginStub, plugin="psychopy-visionscience", doclink="https://psychopy.github.io/psychopy-visionscience/builder/components/NoiseStimComponent/):
|
|
71
|
+
```
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init_subclass__(cls, plugin, doclink="https://plugins.psychopy.org/directory.html"):
|
|
75
|
+
"""
|
|
76
|
+
Subclassing PluginStub will create documentation pointing to the new documentation for the replacement class.
|
|
77
|
+
"""
|
|
78
|
+
# store ref to plugin and docs link
|
|
79
|
+
cls.plugin = plugin
|
|
80
|
+
cls.doclink = doclink
|
|
81
|
+
# create doc string point to new location
|
|
82
|
+
cls.__doc__ = (
|
|
83
|
+
"`{mro}` is now located within the `{plugin}` plugin. You can find the documentation for it `here <{doclink}>`_."
|
|
84
|
+
).format(
|
|
85
|
+
mro=cls.__mro__,
|
|
86
|
+
plugin=plugin,
|
|
87
|
+
doclink=doclink
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def __call__(self, *args, **kwargs):
|
|
91
|
+
"""
|
|
92
|
+
When initialised, rather than creating an object, will log an error.
|
|
93
|
+
"""
|
|
94
|
+
raise NameError(
|
|
95
|
+
"Support for `{mro}` is not available this session. Please install "
|
|
96
|
+
"`{plugin}` and restart the session to enable support."
|
|
97
|
+
).format(
|
|
98
|
+
mro=type(self).__mro__,
|
|
99
|
+
plugin=self.plugin,
|
|
100
|
+
)
|
|
101
|
+
|
|
57
102
|
|
|
58
103
|
def refreshPackages():
|
|
59
104
|
"""Refresh the packaging system.
|
|
@@ -61,28 +106,27 @@ def refreshPackages():
|
|
|
61
106
|
This needs to be called after adding and removing packages, or making any
|
|
62
107
|
changes to `sys.path`. Functions `installPackages` and `uninstallPackages`
|
|
63
108
|
calls this everytime.
|
|
64
|
-
|
|
65
|
-
Warnings
|
|
66
|
-
--------
|
|
67
|
-
Calling this forces a reload of `pkg_resources`. This can cause side-effects
|
|
68
|
-
for other modules using it!
|
|
69
|
-
|
|
70
109
|
"""
|
|
71
110
|
global _installedPackageCache
|
|
72
111
|
global _installedPackageNamesCache
|
|
73
112
|
|
|
74
113
|
_installedPackageCache.clear()
|
|
75
114
|
_installedPackageNamesCache.clear()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
|
|
116
|
+
# iterate through installed packages in the user folder
|
|
117
|
+
for dist in importlib.metadata.distributions(path=sys.path + [USER_PACKAGES_PATH]):
|
|
118
|
+
# get name if in 3.8
|
|
119
|
+
if sys.version.startswith("3.8"):
|
|
120
|
+
distName = dist.metadata['name']
|
|
121
|
+
else:
|
|
122
|
+
distName = dist.name
|
|
123
|
+
# mark as installed
|
|
82
124
|
_installedPackageCache.append(
|
|
83
|
-
(
|
|
84
|
-
|
|
85
|
-
|
|
125
|
+
(distName, dist.version)
|
|
126
|
+
)
|
|
127
|
+
_installedPackageNamesCache.append(
|
|
128
|
+
distName
|
|
129
|
+
)
|
|
86
130
|
|
|
87
131
|
|
|
88
132
|
def getUserPackagesPath():
|
|
@@ -111,10 +155,12 @@ def getDistributions():
|
|
|
111
155
|
plugins can be found.
|
|
112
156
|
|
|
113
157
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
158
|
+
logging.error(
|
|
159
|
+
"`pkgtools.getDistributions` is now deprecated as packages are detected via "
|
|
160
|
+
"`importlib.metadata`, which doesn't need a separate working set from the system path. "
|
|
161
|
+
"Please use `sys.path` instead."
|
|
162
|
+
)
|
|
163
|
+
return sys.path
|
|
118
164
|
|
|
119
165
|
|
|
120
166
|
def addDistribution(distPath):
|
|
@@ -130,8 +176,13 @@ def addDistribution(distPath):
|
|
|
130
176
|
file (e.g. ZIP).
|
|
131
177
|
|
|
132
178
|
"""
|
|
133
|
-
|
|
134
|
-
|
|
179
|
+
logging.error(
|
|
180
|
+
"`pkgtools.addDistribution` is now deprecated as packages are detected via "
|
|
181
|
+
"`importlib.metadata`, which doesn't need a separate working set from the system path. "
|
|
182
|
+
"Please use `sys.path.append` instead."
|
|
183
|
+
)
|
|
184
|
+
if distPath not in sys.path:
|
|
185
|
+
sys.path.append(distPath)
|
|
135
186
|
|
|
136
187
|
|
|
137
188
|
def installPackage(package, target=None, upgrade=False, forceReinstall=False,
|
|
@@ -169,7 +220,7 @@ def installPackage(package, target=None, upgrade=False, forceReinstall=False,
|
|
|
169
220
|
|
|
170
221
|
"""
|
|
171
222
|
if target is None:
|
|
172
|
-
target = prefs.paths['
|
|
223
|
+
target = prefs.paths['userPackages']
|
|
173
224
|
|
|
174
225
|
# check the directory exists before installing
|
|
175
226
|
if not os.path.exists(target):
|
|
@@ -194,17 +245,33 @@ def installPackage(package, target=None, upgrade=False, forceReinstall=False,
|
|
|
194
245
|
if noDeps:
|
|
195
246
|
cmd.append('--no-deps')
|
|
196
247
|
|
|
248
|
+
# check if we are in a virtual environment, if so, dont use --user
|
|
249
|
+
if hasattr(sys, 'real_prefix') or (
|
|
250
|
+
hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
|
251
|
+
# we are in a venv
|
|
252
|
+
logging.warning(
|
|
253
|
+
"You are installing a package inside a virtual environment. "
|
|
254
|
+
"The package will be installed in the user site-packages directory."
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
cmd.append('--user')
|
|
258
|
+
|
|
259
|
+
cmd.append('--prefer-binary') # use binary wheels if available
|
|
197
260
|
cmd.append('--no-input') # do not prompt, we cannot accept input
|
|
198
261
|
cmd.append('--no-color') # no color for console, not supported
|
|
199
262
|
cmd.append('--no-warn-conflicts') # silence non-fatal errors
|
|
200
263
|
|
|
264
|
+
# get the environment for the subprocess
|
|
265
|
+
env = os.environ.copy()
|
|
266
|
+
|
|
201
267
|
# run command in subprocess
|
|
202
268
|
output = sp.Popen(
|
|
203
269
|
cmd,
|
|
204
270
|
stdout=sp.PIPE,
|
|
205
271
|
stderr=sp.PIPE,
|
|
206
272
|
shell=False,
|
|
207
|
-
universal_newlines=True
|
|
273
|
+
universal_newlines=True,
|
|
274
|
+
env=env)
|
|
208
275
|
stdout, stderr = output.communicate() # blocks until process exits
|
|
209
276
|
|
|
210
277
|
sys.stdout.write(stdout)
|
|
@@ -272,14 +339,18 @@ def _isUserPackage(package):
|
|
|
272
339
|
`True` if the package is present in the user's PsychoPy directory.
|
|
273
340
|
|
|
274
341
|
"""
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
342
|
+
# get packages in the user path
|
|
343
|
+
userPackages = []
|
|
344
|
+
for dist in importlib.metadata.distributions(path=[USER_PACKAGES_PATH]):
|
|
345
|
+
# substitute name if using 3.8
|
|
346
|
+
if sys.version.startswith("3.8"):
|
|
347
|
+
distName = dist.metadata['name']
|
|
348
|
+
else:
|
|
349
|
+
distName = dist.name
|
|
350
|
+
# get name
|
|
351
|
+
userPackages.append(distName)
|
|
352
|
+
|
|
353
|
+
return package in userPackages
|
|
283
354
|
|
|
284
355
|
|
|
285
356
|
def _uninstallUserPackage(package):
|
|
@@ -315,95 +386,47 @@ def _uninstallUserPackage(package):
|
|
|
315
386
|
logging.info(msg)
|
|
316
387
|
stdout += msg + "\n"
|
|
317
388
|
|
|
318
|
-
#
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
safeToRemove = True
|
|
350
|
-
for otherPkg, otherTopLevels in allTopLevelPackages.items():
|
|
351
|
-
if pkgTopLevel in otherTopLevels:
|
|
352
|
-
# check if another version of this package is sharing the dir
|
|
353
|
-
if otherPkg.startswith(pathHead):
|
|
354
|
-
msg = (
|
|
355
|
-
'Found metadata for an older version of package `{}` in '
|
|
356
|
-
'`{}`. This will also be removed.'
|
|
357
|
-
).format(pkgName, otherPkg)
|
|
358
|
-
logging.warning(msg)
|
|
359
|
-
stdout += msg + "\n"
|
|
360
|
-
toRemove.append(otherPkg)
|
|
361
|
-
else:
|
|
362
|
-
# unrelated package
|
|
363
|
-
msg = (
|
|
364
|
-
'Found matching top-level directory `{}` in metadata '
|
|
365
|
-
'for `{}`. Can not safely remove this directory since '
|
|
366
|
-
'another package appears to use it.'
|
|
367
|
-
).format(pkgTopLevel, otherPkg)
|
|
368
|
-
logging.warning(msg)
|
|
369
|
-
stdout += msg + "\n"
|
|
370
|
-
safeToRemove = False
|
|
371
|
-
break
|
|
372
|
-
|
|
373
|
-
if safeToRemove:
|
|
374
|
-
toRemove.append(pkgTopLevel)
|
|
375
|
-
|
|
376
|
-
# delete modules from the paths we found
|
|
377
|
-
for rmDir in toRemove:
|
|
378
|
-
if os.path.isfile(rmDir):
|
|
379
|
-
msg = (
|
|
380
|
-
'Removing file `{}` from user package directory.'
|
|
381
|
-
).format(rmDir)
|
|
382
|
-
logging.info(msg)
|
|
383
|
-
stdout += msg + "\n"
|
|
384
|
-
os.remove(rmDir)
|
|
385
|
-
elif os.path.isdir(rmDir):
|
|
386
|
-
msg = (
|
|
387
|
-
'Removing directory `{}` from user package '
|
|
388
|
-
'directory.'
|
|
389
|
-
).format(rmDir)
|
|
390
|
-
logging.info(msg)
|
|
391
|
-
stdout += msg + "\n"
|
|
392
|
-
shutil.rmtree(rmDir)
|
|
393
|
-
|
|
394
|
-
# cleanup by also deleting the metadata path
|
|
395
|
-
shutil.rmtree(metaPath)
|
|
396
|
-
|
|
389
|
+
# get distribution object
|
|
390
|
+
thisPkg = importlib.metadata.distribution(package)
|
|
391
|
+
# iterate through its files
|
|
392
|
+
for file in thisPkg.files:
|
|
393
|
+
# get absolute path (not relative to package dir)
|
|
394
|
+
absPath = thisPkg.locate_file(file)
|
|
395
|
+
# skip pycache
|
|
396
|
+
if absPath.stem == "__pycache__":
|
|
397
|
+
continue
|
|
398
|
+
# delete file
|
|
399
|
+
if absPath.is_file():
|
|
400
|
+
try:
|
|
401
|
+
absPath.unlink()
|
|
402
|
+
except PermissionError as err:
|
|
403
|
+
stdout += _translate(
|
|
404
|
+
"Could not remove {absPath}, reason: {err}".format(absPath=absPath, err=err)
|
|
405
|
+
)
|
|
406
|
+
# skip pycache
|
|
407
|
+
if absPath.parent.stem == "__pycache__":
|
|
408
|
+
continue
|
|
409
|
+
# delete folder if empty
|
|
410
|
+
if absPath.parent.is_dir() and not [f for f in absPath.parent.glob("*")]:
|
|
411
|
+
# delete file
|
|
412
|
+
try:
|
|
413
|
+
absPath.parent.unlink()
|
|
414
|
+
except PermissionError as err:
|
|
415
|
+
stdout += _translate(
|
|
416
|
+
"Could not remove {absPath}, reason: {err}".format(absPath=absPath, err=err)
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# log success
|
|
397
420
|
msg = 'Uninstalled package `{}`.'.format(package)
|
|
398
421
|
logging.info(msg)
|
|
399
422
|
stdout += msg + "\n"
|
|
400
|
-
|
|
401
|
-
#
|
|
423
|
+
|
|
424
|
+
# return the return code and a dict of information from the console
|
|
402
425
|
return True, {
|
|
403
426
|
"cmd": cmd,
|
|
404
427
|
"stdout": stdout,
|
|
405
|
-
"stderr": ""
|
|
406
|
-
|
|
428
|
+
"stderr": ""
|
|
429
|
+
}
|
|
407
430
|
|
|
408
431
|
def uninstallPackage(package):
|
|
409
432
|
"""Uninstall a package from the current distribution.
|
|
@@ -437,7 +460,6 @@ def uninstallPackage(package):
|
|
|
437
460
|
|
|
438
461
|
# setup the environment to use the user's site-packages
|
|
439
462
|
env = os.environ.copy()
|
|
440
|
-
env["PYTHONUSERBASE"] = site.USER_BASE
|
|
441
463
|
|
|
442
464
|
# run command in subprocess
|
|
443
465
|
output = sp.Popen(
|
|
@@ -505,10 +527,16 @@ def getInstalledPackages():
|
|
|
505
527
|
"""
|
|
506
528
|
# this is like calling `pip freeze` and parsing the output, but faster!
|
|
507
529
|
installedPackages = []
|
|
508
|
-
for
|
|
509
|
-
|
|
530
|
+
for dist in importlib.metadata.distributions(path=[USER_PACKAGES_PATH]):
|
|
531
|
+
# substitute name if using 3.8
|
|
532
|
+
if sys.version.startswith("3.8"):
|
|
533
|
+
distName = dist.metadata['name']
|
|
534
|
+
else:
|
|
535
|
+
distName = dist.name
|
|
536
|
+
# get name and version
|
|
510
537
|
installedPackages.append(
|
|
511
|
-
(
|
|
538
|
+
(distName, dist.version)
|
|
539
|
+
)
|
|
512
540
|
|
|
513
541
|
return installedPackages
|
|
514
542
|
|
|
@@ -523,7 +551,7 @@ def isInstalled(packageName):
|
|
|
523
551
|
|
|
524
552
|
"""
|
|
525
553
|
# installed packages are given as keys in the resulting dicts
|
|
526
|
-
return
|
|
554
|
+
return packageName in _installedPackageNamesCache
|
|
527
555
|
|
|
528
556
|
|
|
529
557
|
def getPackageMetadata(packageName):
|
|
@@ -544,16 +572,11 @@ def getPackageMetadata(packageName):
|
|
|
544
572
|
import email.parser
|
|
545
573
|
|
|
546
574
|
try:
|
|
547
|
-
dist =
|
|
548
|
-
except
|
|
575
|
+
dist = importlib.metadata.distribution(packageName)
|
|
576
|
+
except importlib.metadata.PackageNotFoundError:
|
|
549
577
|
return # do nothing
|
|
550
578
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
# parse the metadata using
|
|
554
|
-
metadict = dict()
|
|
555
|
-
for key, val in email.message_from_string(metadata).raw_items():
|
|
556
|
-
metadict[key] = val
|
|
579
|
+
metadict = dict(dist.metadata)
|
|
557
580
|
|
|
558
581
|
return metadict
|
|
559
582
|
|
psychopy/tools/versionchooser.py
CHANGED
|
@@ -17,7 +17,7 @@ import psychopy # for currently loaded version
|
|
|
17
17
|
from psychopy import prefs
|
|
18
18
|
# the following will all have been imported so import here and reload later
|
|
19
19
|
from psychopy import logging, tools, web, constants, preferences, __version__
|
|
20
|
-
from
|
|
20
|
+
from packaging.version import Version
|
|
21
21
|
from importlib import reload
|
|
22
22
|
from packaging.version import Version, InvalidVersion, VERSION_PATTERN
|
|
23
23
|
|
|
@@ -194,7 +194,7 @@ def getPsychoJSVersionStr(currentVersion, preferredVersion=''):
|
|
|
194
194
|
|
|
195
195
|
# do we shorten minor versions ('3.4.2' to '3.4')?
|
|
196
196
|
# only from 3.2 onwards
|
|
197
|
-
if (
|
|
197
|
+
if (Version('3.2')) <= Version(useVerStr) < Version('2021') \
|
|
198
198
|
and len(useVerStr.split('.')) > 2:
|
|
199
199
|
# e.g. 2020.2 not 2021.2.5
|
|
200
200
|
useVerStr = '.'.join(useVerStr.split('.')[:2])
|
|
@@ -385,9 +385,9 @@ def versionOptions(local=True):
|
|
|
385
385
|
majorMinor = sorted(
|
|
386
386
|
list({'.'.join(v.split('.')[:2])
|
|
387
387
|
for v in availableVersions(local=local)}),
|
|
388
|
-
key=
|
|
388
|
+
key=Version,
|
|
389
389
|
reverse=True)
|
|
390
|
-
major = sorted(list({v.split('.')[0] for v in majorMinor}), key=
|
|
390
|
+
major = sorted(list({v.split('.')[0] for v in majorMinor}), key=Version, reverse=True)
|
|
391
391
|
special = ['latest']
|
|
392
392
|
return special + major + majorMinor
|
|
393
393
|
|
|
@@ -402,7 +402,7 @@ def _localVersions(forceCheck=False):
|
|
|
402
402
|
tagInfo = subprocess.check_output(cmd.split(), cwd=VERSIONSDIR,
|
|
403
403
|
env=constants.ENVIRON).decode('UTF-8')
|
|
404
404
|
allTags = tagInfo.splitlines()
|
|
405
|
-
_localVersionsCache = sorted(allTags, key=
|
|
405
|
+
_localVersionsCache = sorted(allTags, key=Version, reverse=True)
|
|
406
406
|
return _localVersionsCache
|
|
407
407
|
|
|
408
408
|
|
|
@@ -421,7 +421,7 @@ def _remoteVersions(forceCheck=False):
|
|
|
421
421
|
for line in tagInfo.decode().splitlines()
|
|
422
422
|
if '^{}' not in line]
|
|
423
423
|
# ensure most recent (i.e., highest) first
|
|
424
|
-
_remoteVersionsCache = sorted(allTags, key=
|
|
424
|
+
_remoteVersionsCache = sorted(allTags, key=Version, reverse=True)
|
|
425
425
|
return _remoteVersionsCache
|
|
426
426
|
|
|
427
427
|
|
|
@@ -443,19 +443,19 @@ def _versionFilter(versions, wxVersion):
|
|
|
443
443
|
# logging.info(msg)
|
|
444
444
|
versions = [ver for ver in versions
|
|
445
445
|
if ver == 'latest'
|
|
446
|
-
or
|
|
446
|
+
or Version(ver) >= Version('1.90')
|
|
447
447
|
and len(ver) > 1]
|
|
448
448
|
|
|
449
449
|
# Get WX Compatibility
|
|
450
450
|
compatibleWX = '4.0'
|
|
451
|
-
if wxVersion is not None and
|
|
451
|
+
if wxVersion is not None and Version(wxVersion) >= Version(compatibleWX):
|
|
452
452
|
# msg = _translate("wx version: {}. Filtering versions of "
|
|
453
453
|
# "PsychoPy only compatible with wx >= version {}".format(wxVersion,
|
|
454
454
|
# compatibleWX))
|
|
455
455
|
# logging.info(msg)
|
|
456
456
|
return [ver for ver in versions
|
|
457
457
|
if ver == 'latest'
|
|
458
|
-
or
|
|
458
|
+
or Version(ver) > Version('1.85.04')
|
|
459
459
|
and len(ver) > 1]
|
|
460
460
|
return versions
|
|
461
461
|
|
|
@@ -474,7 +474,7 @@ def availableVersions(local=True, forceCheck=False):
|
|
|
474
474
|
return sorted(
|
|
475
475
|
list(set([psychopy.__version__] + _localVersions(forceCheck) + _remoteVersions(
|
|
476
476
|
forceCheck))),
|
|
477
|
-
key=
|
|
477
|
+
key=Version,
|
|
478
478
|
reverse=True)
|
|
479
479
|
except subprocess.CalledProcessError:
|
|
480
480
|
return []
|
psychopy/tools/wizard.py
CHANGED
|
@@ -17,9 +17,9 @@ import wx
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
import platform
|
|
19
19
|
import codecs
|
|
20
|
-
from
|
|
20
|
+
from packaging.version import Version
|
|
21
21
|
|
|
22
|
-
if
|
|
22
|
+
if Version(wx.__version__) < Version('2.9'):
|
|
23
23
|
tmpApp = wx.PySimpleApp()
|
|
24
24
|
else:
|
|
25
25
|
tmpApp = wx.App(False)
|
|
@@ -88,7 +88,7 @@ class BaseWizard():
|
|
|
88
88
|
'prefs.html#application-settings-app">Preferences -> App</a>')
|
|
89
89
|
report.append(('locale', items['systemLocale'], msg, False))
|
|
90
90
|
msg = ''
|
|
91
|
-
v =
|
|
91
|
+
v = Version
|
|
92
92
|
thisV = v(items['pythonVersion'])
|
|
93
93
|
if (thisV < v('2.7') or (v('3.0') <= thisV < v('3.6'))
|
|
94
94
|
):
|
|
Binary file
|
|
@@ -179,20 +179,27 @@ class PygletBackend(BaseBackend):
|
|
|
179
179
|
'integer greater than two. Disabling.')
|
|
180
180
|
win.multiSample = False
|
|
181
181
|
|
|
182
|
+
skip_screen_warn = False
|
|
182
183
|
if platform.system() == 'Linux':
|
|
183
|
-
|
|
184
|
+
from pyglet.canvas.xlib import NoSuchDisplayException
|
|
185
|
+
try:
|
|
186
|
+
display = pyglet.canvas.Display(x_screen=win.screen)
|
|
187
|
+
# in this case, we'll only get a single x-screen back
|
|
188
|
+
skip_screen_warn = True
|
|
189
|
+
except NoSuchDisplayException:
|
|
190
|
+
# Maybe xinerama? Try again and get the specified screen later
|
|
191
|
+
display = pyglet.canvas.Display(x_screen=0)
|
|
192
|
+
|
|
184
193
|
allScrs = display.get_screens()
|
|
185
194
|
else:
|
|
186
|
-
|
|
187
|
-
allScrs = _default_display_.get_screens()
|
|
188
|
-
else:
|
|
189
|
-
allScrs = _default_display_.get_screens()
|
|
195
|
+
allScrs = _default_display_.get_screens()
|
|
190
196
|
|
|
191
|
-
# Screen (from Exp Settings) is
|
|
197
|
+
# Screen (from Exp Settings) is 0-indexed,
|
|
192
198
|
# so the second screen is Screen 1
|
|
193
199
|
if len(allScrs) < int(win.screen) + 1:
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
if not skip_screen_warn:
|
|
201
|
+
logging.warn("Requested an unavailable screen number - "
|
|
202
|
+
"using first available.")
|
|
196
203
|
thisScreen = allScrs[0]
|
|
197
204
|
else:
|
|
198
205
|
thisScreen = allScrs[win.screen]
|
|
@@ -247,7 +254,7 @@ class PygletBackend(BaseBackend):
|
|
|
247
254
|
self.winHandle = pyglet.window.Window(
|
|
248
255
|
width=w, height=h,
|
|
249
256
|
caption="PsychoPy",
|
|
250
|
-
fullscreen=
|
|
257
|
+
fullscreen=win._isFullScr,
|
|
251
258
|
config=config,
|
|
252
259
|
screen=thisScreen,
|
|
253
260
|
style=style)
|
|
@@ -258,8 +265,12 @@ class PygletBackend(BaseBackend):
|
|
|
258
265
|
"graphics card and/or graphics drivers.")
|
|
259
266
|
try:
|
|
260
267
|
icns = [
|
|
261
|
-
pyglet.image.load(
|
|
262
|
-
|
|
268
|
+
pyglet.image.load(
|
|
269
|
+
prefs.paths['assets'] + os.sep + "Psychopy Window Favicon@16w.png"
|
|
270
|
+
),
|
|
271
|
+
pyglet.image.load(
|
|
272
|
+
prefs.paths['assets'] + os.sep + "Psychopy Window Favicon@32w.png"
|
|
273
|
+
),
|
|
263
274
|
]
|
|
264
275
|
self.winHandle.set_icon(*icns)
|
|
265
276
|
except BaseException:
|
|
@@ -347,8 +358,8 @@ class PygletBackend(BaseBackend):
|
|
|
347
358
|
int(win.pos[1] + thisScreen.y))
|
|
348
359
|
|
|
349
360
|
try: # to load an icon for the window
|
|
350
|
-
iconFile = os.path.join(psychopy.prefs.paths['
|
|
351
|
-
'
|
|
361
|
+
iconFile = os.path.join(psychopy.prefs.paths['assets'],
|
|
362
|
+
'window.ico')
|
|
352
363
|
icon = pyglet.image.load(filename=iconFile)
|
|
353
364
|
self.winHandle.set_icon(icon)
|
|
354
365
|
except Exception:
|