psychopy 2025.1.0__py3-none-any.whl → 2025.2.1__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/VERSION +1 -1
- psychopy/alerts/alertsCatalogue/4810.yaml +19 -0
- psychopy/alerts/alertsCatalogue/alertCategories.yaml +4 -0
- psychopy/alerts/alertsCatalogue/alertmsg.py +15 -1
- psychopy/alerts/alertsCatalogue/generateAlertmsg.py +2 -2
- psychopy/app/Resources/classic/add_many.png +0 -0
- psychopy/app/Resources/classic/add_many@2x.png +0 -0
- psychopy/app/Resources/classic/devices.png +0 -0
- psychopy/app/Resources/classic/devices@2x.png +0 -0
- psychopy/app/Resources/classic/photometer.png +0 -0
- psychopy/app/Resources/classic/photometer@2x.png +0 -0
- psychopy/app/Resources/dark/add_many.png +0 -0
- psychopy/app/Resources/dark/add_many@2x.png +0 -0
- psychopy/app/Resources/dark/devices.png +0 -0
- psychopy/app/Resources/dark/devices@2x.png +0 -0
- psychopy/app/Resources/dark/photometer.png +0 -0
- psychopy/app/Resources/dark/photometer@2x.png +0 -0
- psychopy/app/Resources/light/add_many.png +0 -0
- psychopy/app/Resources/light/add_many@2x.png +0 -0
- psychopy/app/Resources/light/devices.png +0 -0
- psychopy/app/Resources/light/devices@2x.png +0 -0
- psychopy/app/Resources/light/photometer.png +0 -0
- psychopy/app/Resources/light/photometer@2x.png +0 -0
- psychopy/app/_psychopyApp.py +35 -13
- psychopy/app/builder/builder.py +88 -35
- psychopy/app/builder/dialogs/__init__.py +69 -220
- psychopy/app/builder/dialogs/dlgsCode.py +29 -8
- psychopy/app/builder/dialogs/paramCtrls.py +1468 -904
- psychopy/app/builder/validators.py +25 -17
- psychopy/app/coder/coder.py +12 -1
- psychopy/app/coder/repl.py +5 -2
- psychopy/app/colorpicker/__init__.py +1 -1
- psychopy/app/deviceManager/__init__.py +1 -0
- psychopy/app/deviceManager/addDialog.py +218 -0
- psychopy/app/deviceManager/dialog.py +185 -0
- psychopy/app/deviceManager/panel.py +191 -0
- psychopy/app/deviceManager/utils.py +60 -0
- psychopy/app/idle.py +7 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +12695 -10592
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +11221 -9712
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.po +10195 -18
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +11917 -9101
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +11924 -9103
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.po +11917 -9101
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +11084 -9569
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +11590 -5806
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +11091 -9577
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +11072 -9549
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +11071 -9559
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +11072 -9560
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1485 -1137
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +11463 -8757
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +11288 -9434
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +10200 -25
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +10199 -24
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +11441 -8747
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +11069 -9545
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +12085 -8268
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +11929 -8022
- psychopy/app/plugin_manager/dialog.py +12 -3
- psychopy/app/plugin_manager/packageIndex.py +303 -0
- psychopy/app/plugin_manager/packages.py +203 -63
- psychopy/app/plugin_manager/plugins.py +120 -240
- psychopy/app/preferencesDlg.py +6 -1
- psychopy/app/psychopyApp.py +16 -4
- psychopy/app/runner/runner.py +10 -2
- psychopy/app/runner/scriptProcess.py +8 -3
- psychopy/app/stdout/stdOutRich.py +11 -4
- psychopy/app/themes/icons.py +3 -0
- psychopy/app/utils.py +61 -0
- psychopy/colors.py +10 -5
- psychopy/data/experiment.py +133 -23
- psychopy/data/routine.py +12 -0
- psychopy/data/staircase.py +42 -20
- psychopy/data/trial.py +20 -12
- psychopy/data/utils.py +43 -3
- psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +22 -5
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
- psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +2 -12
- psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -8
- psychopy/demos/builder/Feature Demos/movies/movie.psyexp +220 -0
- psychopy/demos/builder/Feature Demos/movies/readme.md +3 -0
- psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +1 -2
- psychopy/demos/builder/Hardware/camera/camera.psyexp +3 -16
- psychopy/demos/builder/Hardware/microphone/microphone.psyexp +3 -16
- psychopy/demos/coder/hardware/hdf5_extract.py +133 -0
- psychopy/event.py +20 -15
- psychopy/experiment/_experiment.py +86 -10
- psychopy/experiment/components/__init__.py +3 -10
- psychopy/experiment/components/_base.py +9 -20
- psychopy/experiment/components/button/__init__.py +1 -1
- psychopy/experiment/components/buttonBox/__init__.py +50 -54
- psychopy/experiment/components/camera/__init__.py +137 -359
- psychopy/experiment/components/keyboard/__init__.py +17 -24
- psychopy/experiment/components/microphone/__init__.py +61 -110
- psychopy/experiment/components/movie/__init__.py +2 -3
- psychopy/experiment/components/serialOut/__init__.py +192 -93
- psychopy/experiment/components/settings/__init__.py +45 -27
- psychopy/experiment/components/sound/__init__.py +82 -73
- psychopy/experiment/components/soundsensor/__init__.py +43 -80
- psychopy/experiment/devices.py +303 -0
- psychopy/experiment/exports.py +20 -18
- psychopy/experiment/flow.py +7 -0
- psychopy/experiment/loops.py +47 -29
- psychopy/experiment/monitor.py +74 -0
- psychopy/experiment/params.py +48 -10
- psychopy/experiment/plugins.py +28 -108
- psychopy/experiment/py2js_transpiler.py +1 -1
- psychopy/experiment/routines/__init__.py +1 -1
- psychopy/experiment/routines/_base.py +59 -24
- psychopy/experiment/routines/audioValidator/__init__.py +19 -155
- psychopy/experiment/routines/visualValidator/__init__.py +25 -25
- psychopy/hardware/__init__.py +20 -57
- psychopy/hardware/button.py +15 -2
- psychopy/hardware/camera/__init__.py +2237 -1394
- psychopy/hardware/joystick/__init__.py +1 -1
- psychopy/hardware/keyboard.py +5 -8
- psychopy/hardware/listener.py +4 -1
- psychopy/hardware/manager.py +75 -35
- psychopy/hardware/microphone.py +53 -7
- psychopy/hardware/monitor.py +144 -0
- psychopy/hardware/photometer/__init__.py +156 -117
- psychopy/hardware/serialdevice.py +16 -2
- psychopy/hardware/soundsensor.py +4 -1
- psychopy/iohub/devices/deviceConfigValidation.py +2 -1
- psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +2 -2
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +1 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +10 -0
- psychopy/iohub/devices/keyboard/darwin.py +8 -5
- psychopy/iohub/util/__init__.py +7 -8
- psychopy/localization/generateTranslationTemplate.py +208 -116
- psychopy/localization/messages.pot +4305 -3502
- psychopy/monitors/MonitorCenter.py +174 -74
- psychopy/plugins/__init__.py +6 -4
- psychopy/preferences/devices.py +80 -0
- psychopy/preferences/generateHints.py +2 -1
- psychopy/preferences/preferences.py +35 -11
- psychopy/scripts/psychopy-pkgutil.py +969 -0
- psychopy/scripts/psyexpCompile.py +1 -1
- psychopy/session.py +34 -38
- psychopy/sound/__init__.py +6 -260
- psychopy/sound/audioclip.py +164 -0
- psychopy/sound/backend_ptb.py +8 -0
- psychopy/sound/backend_pygame.py +10 -0
- psychopy/sound/backend_pysound.py +9 -0
- psychopy/sound/backends/__init__.py +0 -0
- psychopy/sound/microphone.py +3 -0
- psychopy/sound/sound.py +58 -0
- psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +1 -1
- psychopy/tests/data/duplicateHeaders.csv +2 -0
- psychopy/tests/test_app/test_builder/test_BuilderFrame.py +22 -7
- psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +0 -2
- psychopy/tests/test_data/test_utils.py +5 -1
- psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py +22 -2
- psychopy/tests/test_hardware/test_ports.py +0 -12
- psychopy/tests/test_tools/test_stringtools.py +1 -1
- psychopy/tools/attributetools.py +12 -5
- psychopy/tools/fontmanager.py +17 -14
- psychopy/tools/gltools.py +4 -2
- psychopy/tools/movietools.py +43 -2
- psychopy/tools/stringtools.py +33 -8
- psychopy/tools/versionchooser.py +1 -1
- psychopy/validation/audio.py +5 -1
- psychopy/validation/visual.py +5 -1
- psychopy/visual/basevisual.py +8 -7
- psychopy/visual/circle.py +2 -2
- psychopy/visual/helpers.py +3 -1
- psychopy/visual/image.py +29 -109
- psychopy/visual/movies/__init__.py +1800 -313
- psychopy/visual/polygon.py +4 -0
- psychopy/visual/shape.py +2 -2
- psychopy/visual/window.py +35 -12
- psychopy/voicekey/__init__.py +41 -669
- psychopy/voicekey/labjack_vks.py +7 -48
- psychopy/voicekey/parallel_vks.py +7 -42
- psychopy/voicekey/vk_tools.py +114 -263
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/METADATA +20 -13
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/RECORD +222 -190
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/WHEEL +1 -1
- psychopy/visual/movies/players/__init__.py +0 -62
- psychopy/visual/movies/players/ffpyplayer_player.py +0 -1401
- psychopy/voicekey/demo_vks.py +0 -12
- psychopy/voicekey/signal.py +0 -42
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/entry_points.txt +0 -0
- {psychopy-2025.1.0.dist-info → psychopy-2025.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -372,28 +372,6 @@ class MainFrame(wx.Frame):
|
|
|
372
372
|
calibBox = wx.StaticBoxSizer(boxLabel)
|
|
373
373
|
|
|
374
374
|
photometerBox = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)
|
|
375
|
-
# com port entry number
|
|
376
|
-
self.comPortLabel = wx.StaticText(parent, -1, " ", size=(150, 20))
|
|
377
|
-
# photometer button
|
|
378
|
-
# photom type choices should not need localization:
|
|
379
|
-
self._photomTypeItems = list([p.longName for p in hardware.getAllPhotometers()] + ["Get more..."])
|
|
380
|
-
self.ctrlPhotomType = wx.Choice(parent, -1, name="Type:",
|
|
381
|
-
choices=self._photomTypeItems)
|
|
382
|
-
|
|
383
|
-
_ports = list(hardware.getSerialPorts())
|
|
384
|
-
self._photomChoices = [_translate("Scan all ports")] + _ports
|
|
385
|
-
_size = self.ctrlPhotomType.GetSize() + [0, 5]
|
|
386
|
-
self.ctrlPhotomPort = wx.ComboBox(parent, -1, name="Port:",
|
|
387
|
-
value=self._photomChoices[0],
|
|
388
|
-
choices=self._photomChoices,
|
|
389
|
-
size=_size)
|
|
390
|
-
|
|
391
|
-
self.ctrlPhotomType.Bind(wx.EVT_CHOICE, self.onChangePhotomType)
|
|
392
|
-
self.btnFindPhotometer = wx.Button(parent, -1,
|
|
393
|
-
_translate("Get Photometer"))
|
|
394
|
-
self.Bind(wx.EVT_BUTTON,
|
|
395
|
-
self.onBtnFindPhotometer, self.btnFindPhotometer)
|
|
396
|
-
|
|
397
375
|
# gamma controls
|
|
398
376
|
self.btnCalibrateGamma = wx.Button(
|
|
399
377
|
parent, -1, _translate("Gamma Calibration..."))
|
|
@@ -419,12 +397,11 @@ class MainFrame(wx.Frame):
|
|
|
419
397
|
parent, -1, _translate("Plot spectra"))
|
|
420
398
|
self.Bind(wx.EVT_BUTTON,
|
|
421
399
|
self.plotSpectra, self.btnPlotSpectra)
|
|
422
|
-
photometerBox.AddMany([
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
self.btnCalibrateColor, self.btnPlotSpectra])
|
|
400
|
+
photometerBox.AddMany([
|
|
401
|
+
self.btnCalibrateGamma, (0, 0),
|
|
402
|
+
self.btnTestGamma, self.btnPlotGamma,
|
|
403
|
+
self.btnCalibrateColor, self.btnPlotSpectra
|
|
404
|
+
])
|
|
428
405
|
|
|
429
406
|
# ----GAMMA------------
|
|
430
407
|
# calibration grid
|
|
@@ -787,54 +764,56 @@ class MainFrame(wx.Frame):
|
|
|
787
764
|
self.unSavedMonitor = True
|
|
788
765
|
|
|
789
766
|
def onCalibGammaBtn(self, event):
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
lumsPre = self.currentMon.getLumsPre()
|
|
793
|
-
lumLevels = self.currentMon.getLevelsPre()
|
|
794
|
-
else:
|
|
795
|
-
# present a dialogue to get details for calibration
|
|
796
|
-
calibDlg = GammaDlg(self, self.currentMon)
|
|
797
|
-
if calibDlg.ShowModal() != wx.ID_OK:
|
|
798
|
-
calibDlg.Destroy()
|
|
799
|
-
return 1
|
|
800
|
-
nPoints = int(calibDlg.ctrlNPoints.GetValue())
|
|
801
|
-
stimSize = unicodeToFloat(calibDlg.ctrlStimSize.GetValue())
|
|
802
|
-
useBits = calibDlg.ctrlUseBits.GetValue()
|
|
803
|
-
calibDlg.Destroy()
|
|
804
|
-
autoMode = calibDlg.methodChoiceBx.GetStringSelection()
|
|
805
|
-
# lib starts at zero but here we allow 1
|
|
806
|
-
screen = int(calibDlg.ctrlScrN.GetValue()) - 1
|
|
767
|
+
from psychopy.hardware import monitor, DeviceManager
|
|
768
|
+
from psychopy.visual import Window
|
|
807
769
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
lumLevels=lumLevels,
|
|
813
|
-
useBits=useBits,
|
|
814
|
-
autoMode=autoMode,
|
|
815
|
-
winSize=_size,
|
|
816
|
-
stimSize=stimSize,
|
|
817
|
-
monitor=self.currentMon,
|
|
818
|
-
screen=screen)
|
|
819
|
-
|
|
820
|
-
# allow user to type in values
|
|
821
|
-
if autoMode == 'semi':
|
|
822
|
-
inputDlg = GammaLumValsDlg(parent=self, levels=lumLevels)
|
|
823
|
-
lumsPre = inputDlg.show() # will be [] if user cancels
|
|
824
|
-
inputDlg.Destroy()
|
|
825
|
-
|
|
826
|
-
# fit the gamma curves
|
|
827
|
-
if lumsPre is None or len(lumsPre) > 1:
|
|
828
|
-
self.onCopyCalib(1) # create a new dated calibration
|
|
829
|
-
self.currentMon.setLumsPre(lumsPre) # save for future
|
|
830
|
-
self.currentMon.setLevelsPre(lumLevels) # save for future
|
|
831
|
-
self.btnPlotGamma.Enable(True)
|
|
832
|
-
self.choiceLinearMethod.Enable()
|
|
833
|
-
|
|
834
|
-
# do the fits
|
|
835
|
-
self.doGammaFits(lumLevels, lumsPre)
|
|
770
|
+
# get calibration setup params
|
|
771
|
+
dlg = CalibrationSetupDlg(self)
|
|
772
|
+
if dlg.ShowModal() == wx.ID_OK:
|
|
773
|
+
params = dlg.getParams()
|
|
836
774
|
else:
|
|
837
|
-
|
|
775
|
+
# abort if cancelled
|
|
776
|
+
return
|
|
777
|
+
|
|
778
|
+
# setup calibration procedure window
|
|
779
|
+
win = Window(
|
|
780
|
+
screen=int(params['screen']),
|
|
781
|
+
checkTiming=False,
|
|
782
|
+
fullscr=True
|
|
783
|
+
)
|
|
784
|
+
# setup calibration procedure device
|
|
785
|
+
device = None
|
|
786
|
+
for profile in DeviceManager.getAvailableDevices(params['photometer']):
|
|
787
|
+
# instantiate from profile
|
|
788
|
+
device = DeviceManager.addDevice(**profile)
|
|
789
|
+
break
|
|
790
|
+
# catch error if no device found
|
|
791
|
+
if device is None:
|
|
792
|
+
win.close()
|
|
793
|
+
wx.MessageBox(
|
|
794
|
+
"Could not find any device matching '{photometer}', aborting calibration.".format(**params),
|
|
795
|
+
parent=self,
|
|
796
|
+
style=wx.ICON_ERROR | wx.OK
|
|
797
|
+
)
|
|
798
|
+
return
|
|
799
|
+
|
|
800
|
+
# run calibration
|
|
801
|
+
try:
|
|
802
|
+
gammaGrid = monitor.calibrateGamma(
|
|
803
|
+
win,
|
|
804
|
+
profile['deviceName'],
|
|
805
|
+
patchSize=float(params['patchSize']),
|
|
806
|
+
nPoints=float(params['nPoints'])
|
|
807
|
+
)
|
|
808
|
+
except Exception as err:
|
|
809
|
+
win.close()
|
|
810
|
+
raise err
|
|
811
|
+
# if gamma was got, set values
|
|
812
|
+
if gammaGrid is not None:
|
|
813
|
+
self.gammaGrid.setData(numpy.array(gammaGrid))
|
|
814
|
+
# teardown
|
|
815
|
+
win.close()
|
|
816
|
+
DeviceManager.removeDevice(profile['deviceName'])
|
|
838
817
|
|
|
839
818
|
def doGammaFits(self, levels, lums):
|
|
840
819
|
linMethod = self.currentMon.getLinearizeMethod()
|
|
@@ -1102,6 +1081,127 @@ class MainFrame(wx.Frame):
|
|
|
1102
1081
|
plotWindow.addCanvas(figureCanvas)
|
|
1103
1082
|
|
|
1104
1083
|
|
|
1084
|
+
class CalibrationSetupDlg(wx.Dialog):
|
|
1085
|
+
"""
|
|
1086
|
+
Dialog getting params for a gamma calibration, which also runs said calibration
|
|
1087
|
+
"""
|
|
1088
|
+
def __init__(self, parent):
|
|
1089
|
+
from psychopy.app.builder.validators import WarningManager
|
|
1090
|
+
from psychopy.experiment.params import Param
|
|
1091
|
+
from psychopy.app.builder.dialogs.paramCtrls import ParamCtrl, EVT_PARAM_CHANGED
|
|
1092
|
+
|
|
1093
|
+
wx.Dialog.__init__(
|
|
1094
|
+
self,
|
|
1095
|
+
parent,
|
|
1096
|
+
title=_translate("Calibrate gamma"),
|
|
1097
|
+
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
|
|
1098
|
+
)
|
|
1099
|
+
# setup sizer
|
|
1100
|
+
self.border = wx.BoxSizer(wx.VERTICAL)
|
|
1101
|
+
self.SetSizer(self.border)
|
|
1102
|
+
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
|
1103
|
+
self.border.Add(
|
|
1104
|
+
self.sizer, border=12, proportion=1, flag=wx.EXPAND | wx.ALL
|
|
1105
|
+
)
|
|
1106
|
+
# create warnings handler
|
|
1107
|
+
self.warnings = WarningManager(self)
|
|
1108
|
+
# define params
|
|
1109
|
+
self.params = {}
|
|
1110
|
+
self.params['screen'] = Param(
|
|
1111
|
+
1, valType="code", inputType="single",
|
|
1112
|
+
label=_translate("Screen"),
|
|
1113
|
+
hint=_translate(
|
|
1114
|
+
"Screen to run the calibration on"
|
|
1115
|
+
)
|
|
1116
|
+
)
|
|
1117
|
+
def getPhotometers():
|
|
1118
|
+
from psychopy.experiment.monitor import BasePhotometerDeviceBackend
|
|
1119
|
+
from psychopy.preferences import prefs
|
|
1120
|
+
# start with nothing
|
|
1121
|
+
devices = [(None, "")]
|
|
1122
|
+
# iterate through known photometer device backends
|
|
1123
|
+
for cls in BasePhotometerDeviceBackend.__subclasses__():
|
|
1124
|
+
# add their device class against their label
|
|
1125
|
+
devices.append(
|
|
1126
|
+
(cls.deviceClass, cls.backendLabel)
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
return devices
|
|
1130
|
+
def getPhotometerValues():
|
|
1131
|
+
return [val[0] for val in getPhotometers()]
|
|
1132
|
+
def getPhotometerLabels():
|
|
1133
|
+
return [val[1] for val in getPhotometers()]
|
|
1134
|
+
self.params['photometer'] = Param(
|
|
1135
|
+
getPhotometerValues()[0], valType="str", inputType="choice",
|
|
1136
|
+
allowedVals=getPhotometerValues, allowedLabels=getPhotometerLabels,
|
|
1137
|
+
label=_translate("Photometer"),
|
|
1138
|
+
hint=_translate(
|
|
1139
|
+
"Photometer device, from the device manager, to use for this calibration"
|
|
1140
|
+
)
|
|
1141
|
+
)
|
|
1142
|
+
self.params['patchSize'] = Param(
|
|
1143
|
+
0.3, valType="code", inputType="single",
|
|
1144
|
+
label=_translate("Patch size"),
|
|
1145
|
+
hint=_translate(
|
|
1146
|
+
"How much of the screen (0-1) the calibration patch should occupy"
|
|
1147
|
+
)
|
|
1148
|
+
)
|
|
1149
|
+
self.params['nPoints'] = Param(
|
|
1150
|
+
8, valType="code", inputType="single",
|
|
1151
|
+
label=_translate("Calibration points"),
|
|
1152
|
+
hint=_translate(
|
|
1153
|
+
"How many calibration points to use"
|
|
1154
|
+
)
|
|
1155
|
+
)
|
|
1156
|
+
# add param ctrls
|
|
1157
|
+
self.paramCtrls = {}
|
|
1158
|
+
for key, param in self.params.items():
|
|
1159
|
+
# add label
|
|
1160
|
+
lbl = wx.StaticText(
|
|
1161
|
+
self, label=param.label
|
|
1162
|
+
)
|
|
1163
|
+
self.sizer.Add(
|
|
1164
|
+
lbl, border=6, flag=wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT
|
|
1165
|
+
)
|
|
1166
|
+
# add ctrl
|
|
1167
|
+
self.paramCtrls[key] = ParamCtrl(
|
|
1168
|
+
self,
|
|
1169
|
+
'photometer',
|
|
1170
|
+
param,
|
|
1171
|
+
element=None,
|
|
1172
|
+
warnings=self.warnings
|
|
1173
|
+
)
|
|
1174
|
+
self.sizer.Add(
|
|
1175
|
+
self.paramCtrls[key], border=6, flag=wx.EXPAND | wx.ALL
|
|
1176
|
+
)
|
|
1177
|
+
# add
|
|
1178
|
+
self.paramCtrls['photometer'].Bind(EVT_PARAM_CHANGED, self.onPhotometerChosen)
|
|
1179
|
+
# add buttons
|
|
1180
|
+
btns = self.CreateStdDialogButtonSizer(flags=wx.OK | wx.CANCEL)
|
|
1181
|
+
self.border.Add(
|
|
1182
|
+
btns, border=6, flag=wx.EXPAND | wx.ALL
|
|
1183
|
+
)
|
|
1184
|
+
for btn in btns.GetChildren():
|
|
1185
|
+
if btn.Window is not None and btn.Window.Id == wx.ID_OK:
|
|
1186
|
+
self.okBtn = btn.Window
|
|
1187
|
+
# start off with OK disabled
|
|
1188
|
+
self.okBtn.Disable()
|
|
1189
|
+
|
|
1190
|
+
self.Layout()
|
|
1191
|
+
self.Fit()
|
|
1192
|
+
|
|
1193
|
+
def onPhotometerChosen(self, evt=None):
|
|
1194
|
+
self.okBtn.Enable(
|
|
1195
|
+
bool(self.paramCtrls['photometer'].getValue())
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
def getParams(self):
|
|
1199
|
+
return {
|
|
1200
|
+
key: ctrl.getValue()
|
|
1201
|
+
for key, ctrl in self.paramCtrls.items()
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
|
|
1105
1205
|
class GammaLumValsDlg(wx.Dialog):
|
|
1106
1206
|
"""A dialogue to manually get the luminance values recorded for each level
|
|
1107
1207
|
"""
|
psychopy/plugins/__init__.py
CHANGED
|
@@ -437,7 +437,7 @@ def scanPlugins():
|
|
|
437
437
|
if not ep.group.startswith("psychopy"):
|
|
438
438
|
continue
|
|
439
439
|
# make sure we have an entry for this distribution
|
|
440
|
-
if sys.version.startswith("3.8"):
|
|
440
|
+
if sys.version.startswith("3.8") or sys.version.startswith("3.9"):
|
|
441
441
|
distName = dist.metadata['name']
|
|
442
442
|
else:
|
|
443
443
|
distName = dist.name
|
|
@@ -789,10 +789,10 @@ def loadPlugin(plugin):
|
|
|
789
789
|
f"Registering entry point {ep.value} (from {plugin}) to {ep.group}:{ep.name}"
|
|
790
790
|
)
|
|
791
791
|
try:
|
|
792
|
-
|
|
792
|
+
mod = ep.load() # load the entry point
|
|
793
793
|
|
|
794
794
|
# Raise a warning if the plugin is being loaded from a zip file.
|
|
795
|
-
if '.zip' in inspect.getfile(
|
|
795
|
+
if '.zip' in inspect.getfile(mod):
|
|
796
796
|
logging.warning(
|
|
797
797
|
"Plugin `{}` is being loaded from a zip file. This may "
|
|
798
798
|
"cause issues with the plugin's functionality.".format(plugin))
|
|
@@ -801,7 +801,7 @@ def loadPlugin(plugin):
|
|
|
801
801
|
msg = f"Skipping entry point {ep.value} (from {plugin}) to {ep.group}:{ep.name}"
|
|
802
802
|
# append reason
|
|
803
803
|
if isinstance(err, ImportError):
|
|
804
|
-
msg += f" as {ep.value} cannot be imported."
|
|
804
|
+
msg += f" as {ep.value} cannot be imported ({err})."
|
|
805
805
|
else:
|
|
806
806
|
msg += f", reason: {err}"
|
|
807
807
|
# log message
|
|
@@ -811,6 +811,8 @@ def loadPlugin(plugin):
|
|
|
811
811
|
_failed_plugins_.append(plugin)
|
|
812
812
|
|
|
813
813
|
continue
|
|
814
|
+
else:
|
|
815
|
+
ep = mod
|
|
814
816
|
|
|
815
817
|
# If we get here, the entry point is valid and we can safely add it
|
|
816
818
|
# to PsychoPy's namespace.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DeviceConfig(dict):
|
|
7
|
+
"""
|
|
8
|
+
Dict linked to a JSON file, used to store configuration parameters for devices in the Device
|
|
9
|
+
Manager.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
file : str or pathlib.Path
|
|
14
|
+
JSON file this dict is linked to
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, file):
|
|
17
|
+
# load file on init
|
|
18
|
+
self.load(file)
|
|
19
|
+
|
|
20
|
+
def copy(self):
|
|
21
|
+
"""
|
|
22
|
+
Create a copy of this DeviceConfig, linked to the same file. Useful for making temporary
|
|
23
|
+
changes which can then be made permenant by calling `save`
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
DeviceConfig
|
|
28
|
+
A new DeviceConfig loaded from the same file as this one.
|
|
29
|
+
"""
|
|
30
|
+
return DeviceConfig(self.file)
|
|
31
|
+
|
|
32
|
+
def reload(self):
|
|
33
|
+
"""
|
|
34
|
+
Load this DeviceConfig again from its file, overwriting any local changes. Useful if you
|
|
35
|
+
have made and saved changes in a copy and want to make sure this DeviceConfig object
|
|
36
|
+
is up to date with the file.
|
|
37
|
+
"""
|
|
38
|
+
self.load(self.file)
|
|
39
|
+
|
|
40
|
+
def load(self, file):
|
|
41
|
+
"""
|
|
42
|
+
Load configuration from a given file. Doing so will also set the file that this
|
|
43
|
+
DeviceConfig object is linked to.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
file : str or pathlib.Path
|
|
48
|
+
JSON file to load from
|
|
49
|
+
"""
|
|
50
|
+
# start off empty
|
|
51
|
+
self.clear()
|
|
52
|
+
# store file path
|
|
53
|
+
self.file = Path(file)
|
|
54
|
+
# read file
|
|
55
|
+
data = json.loads(
|
|
56
|
+
self.file.read_text()
|
|
57
|
+
)
|
|
58
|
+
# apply
|
|
59
|
+
for key, val in data.items():
|
|
60
|
+
# get class from stored data
|
|
61
|
+
mod = ".".join(
|
|
62
|
+
val['__cls__'].split(".")[:-1]
|
|
63
|
+
)
|
|
64
|
+
name = val['__cls__'].split(".")[-1]
|
|
65
|
+
cls = getattr(importlib.import_module(mod), name)
|
|
66
|
+
# initialise class with profile from stored data
|
|
67
|
+
self[key] = cls.fromJSON(val)
|
|
68
|
+
# make sure device name and device key line up
|
|
69
|
+
self[key].name = key
|
|
70
|
+
|
|
71
|
+
def save(self):
|
|
72
|
+
"""
|
|
73
|
+
Save the contents of this DeviceConfig object to its linked JSON file.
|
|
74
|
+
"""
|
|
75
|
+
# save
|
|
76
|
+
self.file.write_text(
|
|
77
|
+
json.dumps({
|
|
78
|
+
key: device.toJSON() for key, device in self.items()
|
|
79
|
+
}, indent=True)
|
|
80
|
+
)
|
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
from psychopy import core
|
|
12
12
|
|
|
13
13
|
write_mode = 'w'
|
|
14
|
+
new_line = '\n'
|
|
14
15
|
|
|
15
16
|
hintsFile = 'hints.py'
|
|
16
17
|
comments_all = []
|
|
@@ -68,7 +69,7 @@ for specfile in specfiles:
|
|
|
68
69
|
|
|
69
70
|
# Output hint.py
|
|
70
71
|
try:
|
|
71
|
-
fp = open(hintsFile, write_mode)
|
|
72
|
+
fp = open(hintsFile, write_mode, newline=new_line)
|
|
72
73
|
fp.write('#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n')
|
|
73
74
|
fp.write('# This file was generated by generateHints.py.\n')
|
|
74
75
|
fp.write('# Following strings are used to localize hints in '
|
|
@@ -7,6 +7,7 @@ import sys
|
|
|
7
7
|
import platform
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from psychopy import logging
|
|
10
|
+
from . import devices
|
|
10
11
|
from .. import __version__
|
|
11
12
|
|
|
12
13
|
from packaging.version import Version
|
|
@@ -71,7 +72,7 @@ class Preferences:
|
|
|
71
72
|
self.paths = {} # this will remain a dictionary
|
|
72
73
|
self.keys = {} # does not remain a dictionary
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
# Only call loadAll, which will handle getPaths
|
|
75
76
|
self.loadAll()
|
|
76
77
|
# setting locale is now handled in psychopy.localization.init
|
|
77
78
|
# as called upon import by the app
|
|
@@ -101,7 +102,7 @@ class Preferences:
|
|
|
101
102
|
print(msg % userCfg)
|
|
102
103
|
self.loadAll() # reloads, now getting all from .spec
|
|
103
104
|
|
|
104
|
-
def getPaths(self):
|
|
105
|
+
def getPaths(self, userDir=None):
|
|
105
106
|
"""Get the paths to various directories and files used by PsychoPy.
|
|
106
107
|
|
|
107
108
|
If the paths are not found, they are created. Usually, this is only
|
|
@@ -130,6 +131,7 @@ class Preferences:
|
|
|
130
131
|
self.paths['resources'] = dirResources
|
|
131
132
|
self.paths['assets'] = join(dirPsychoPy, "assets")
|
|
132
133
|
self.paths['tests'] = join(dirPsychoPy, 'tests')
|
|
134
|
+
self.paths['scripts'] = join(dirPsychoPy, 'scripts')
|
|
133
135
|
# path to libs/frameworks
|
|
134
136
|
if 'PsychoPy.app/Contents' in exePath:
|
|
135
137
|
self.paths['libs'] = exePath.replace("MacOS/python", "Frameworks")
|
|
@@ -139,16 +141,25 @@ class Preferences:
|
|
|
139
141
|
# if there isn't an app folder at all then this is a lib-only psychopy
|
|
140
142
|
# so don't try to load app prefs etc
|
|
141
143
|
NO_APP = True
|
|
144
|
+
# get user dir
|
|
145
|
+
if userDir is not None and os.path.isdir(userDir):
|
|
146
|
+
self.paths['userPrefsDir'] = join(
|
|
147
|
+
userDir, '.psychopy3'
|
|
148
|
+
)
|
|
149
|
+
elif sys.platform == 'win32':
|
|
150
|
+
self.paths['userPrefsDir'] = join(
|
|
151
|
+
os.environ['APPDATA'], 'psychopy3'
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
self.paths['userPrefsDir'] = join(
|
|
155
|
+
os.environ['HOME'], '.psychopy3'
|
|
156
|
+
)
|
|
157
|
+
# get system-appropriate spec file
|
|
142
158
|
if sys.platform == 'win32':
|
|
143
159
|
self.paths['prefsSpecFile'] = join(prefSpecDir, 'Windows.spec')
|
|
144
|
-
self.paths['userPrefsDir'] = join(os.environ['APPDATA'],
|
|
145
|
-
'psychopy3')
|
|
146
160
|
else:
|
|
147
|
-
self.paths['prefsSpecFile'] = join(
|
|
148
|
-
|
|
149
|
-
self.paths['userPrefsDir'] = join(os.environ['HOME'],
|
|
150
|
-
'.psychopy3')
|
|
151
|
-
|
|
161
|
+
self.paths['prefsSpecFile'] = join(
|
|
162
|
+
prefSpecDir, platform.system() + '.spec')
|
|
152
163
|
# directory for files created by the app at runtime needed for operation
|
|
153
164
|
self.paths['userCacheDir'] = join(self.paths['userPrefsDir'], 'cache')
|
|
154
165
|
|
|
@@ -175,7 +186,10 @@ class Preferences:
|
|
|
175
186
|
except OSError as err:
|
|
176
187
|
if err.errno != errno.EEXIST:
|
|
177
188
|
raise
|
|
178
|
-
|
|
189
|
+
# make sure there's a device manager config file
|
|
190
|
+
deviceCfgFile = self.paths['deviceCfgFile'] = Path(self.paths['userPrefsDir']) / "devices.json"
|
|
191
|
+
if not deviceCfgFile.is_file():
|
|
192
|
+
deviceCfgFile.write_text("{}", encoding="utf-8")
|
|
179
193
|
# site-packages root directory for user-installed packages
|
|
180
194
|
userPkgRoot = Path(self.paths['packages'])
|
|
181
195
|
|
|
@@ -268,9 +282,10 @@ class Preferences:
|
|
|
268
282
|
Path(self.paths['themes']) / file.name
|
|
269
283
|
)
|
|
270
284
|
|
|
271
|
-
def loadAll(self):
|
|
285
|
+
def loadAll(self, userDir=None):
|
|
272
286
|
"""Load the user prefs and the application data
|
|
273
287
|
"""
|
|
288
|
+
self.getPaths(userDir=userDir)
|
|
274
289
|
self._validator = validate.Validator()
|
|
275
290
|
|
|
276
291
|
# note: self.paths['userPrefsDir'] gets set in loadSitePrefs()
|
|
@@ -340,6 +355,15 @@ class Preferences:
|
|
|
340
355
|
cfg.write()
|
|
341
356
|
|
|
342
357
|
return cfg
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def devices(self):
|
|
361
|
+
if not hasattr(self, "_devices"):
|
|
362
|
+
self._devices = devices.DeviceConfig(
|
|
363
|
+
self.paths['deviceCfgFile']
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
return self._devices
|
|
343
367
|
|
|
344
368
|
def saveUserPrefs(self):
|
|
345
369
|
"""Validate and save the various setting to the appropriate files
|