psychopy 2024.1.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/CHANGELOG.txt +3571 -0
- psychopy/LICENSE.txt +11 -0
- psychopy/LICENSES.txt +55 -0
- psychopy/__init__.py +74 -0
- psychopy/alerts/__init__.py +65 -0
- psychopy/alerts/_alerts.py +184 -0
- psychopy/alerts/_errorHandler.py +51 -0
- psychopy/alerts/alertsCatalogue/1000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/1100.yaml +0 -0
- psychopy/alerts/alertsCatalogue/1500.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2100.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2110.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2115.yaml +21 -0
- psychopy/alerts/alertsCatalogue/2120.yaml +22 -0
- psychopy/alerts/alertsCatalogue/2150.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2155.yaml +21 -0
- psychopy/alerts/alertsCatalogue/2300.yaml +0 -0
- psychopy/alerts/alertsCatalogue/2500.yaml +0 -0
- psychopy/alerts/alertsCatalogue/3000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/3100.yaml +0 -0
- psychopy/alerts/alertsCatalogue/3110.yaml +25 -0
- psychopy/alerts/alertsCatalogue/3115.yaml +24 -0
- psychopy/alerts/alertsCatalogue/3200.yaml +0 -0
- psychopy/alerts/alertsCatalogue/3400.yaml +0 -0
- psychopy/alerts/alertsCatalogue/4000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/4051.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4052.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4100.yaml +0 -0
- psychopy/alerts/alertsCatalogue/4105.yaml +18 -0
- psychopy/alerts/alertsCatalogue/4115.yaml +21 -0
- psychopy/alerts/alertsCatalogue/4120.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4125.yaml +23 -0
- psychopy/alerts/alertsCatalogue/4200.yaml +0 -0
- psychopy/alerts/alertsCatalogue/4205.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4210.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4305.yaml +21 -0
- psychopy/alerts/alertsCatalogue/4310.yaml +21 -0
- psychopy/alerts/alertsCatalogue/4315.yaml +21 -0
- psychopy/alerts/alertsCatalogue/4320.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4325.yaml +23 -0
- psychopy/alerts/alertsCatalogue/4330.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4335.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4340.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4405.yaml +24 -0
- psychopy/alerts/alertsCatalogue/4505.yaml +22 -0
- psychopy/alerts/alertsCatalogue/4510.yaml +25 -0
- psychopy/alerts/alertsCatalogue/4520.yaml +26 -0
- psychopy/alerts/alertsCatalogue/4530.yaml +23 -0
- psychopy/alerts/alertsCatalogue/4540.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4545.yaml +22 -0
- psychopy/alerts/alertsCatalogue/4550.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4605.yaml +22 -0
- psychopy/alerts/alertsCatalogue/4610.yaml +22 -0
- psychopy/alerts/alertsCatalogue/4615.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4705.yaml +19 -0
- psychopy/alerts/alertsCatalogue/4710.yaml +19 -0
- psychopy/alerts/alertsCatalogue/5000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/5055.yaml +25 -0
- psychopy/alerts/alertsCatalogue/6000.yaml +0 -0
- psychopy/alerts/alertsCatalogue/6105.yaml +18 -0
- psychopy/alerts/alertsCatalogue/7105.yaml +19 -0
- psychopy/alerts/alertsCatalogue/8105.yaml +21 -0
- psychopy/alerts/alertsCatalogue/8110.yaml +21 -0
- psychopy/alerts/alertsCatalogue/9998.yaml +19 -0
- psychopy/alerts/alertsCatalogue/9999.yaml +19 -0
- psychopy/alerts/alertsCatalogue/alertCategories.yaml +109 -0
- psychopy/alerts/alertsCatalogue/alertTemplate.yaml +19 -0
- psychopy/alerts/alertsCatalogue/alertmsg.py +144 -0
- psychopy/alerts/alertsCatalogue/generateAlertmsg.py +34 -0
- psychopy/alerts/alerttools.py +338 -0
- psychopy/app/Resources/Psychopy Window Favicon@16w.png +0 -0
- psychopy/app/Resources/Psychopy Window Favicon@32w.png +0 -0
- psychopy/app/Resources/README.md +12 -0
- psychopy/app/Resources/USB-C.png +0 -0
- psychopy/app/Resources/USB.png +0 -0
- psychopy/app/Resources/__init__.py +0 -0
- psychopy/app/Resources/builder.ico +0 -0
- psychopy/app/Resources/classic/FlowBottom_CompLeft.png +0 -0
- psychopy/app/Resources/classic/FlowBottom_CompRight.png +0 -0
- psychopy/app/Resources/classic/FlowTop_CompLeft.png +0 -0
- psychopy/app/Resources/classic/FlowTop_CompRight.png +0 -0
- psychopy/app/Resources/classic/README.txt +9 -0
- psychopy/app/Resources/classic/__init__.py +0 -0
- psychopy/app/Resources/classic/_layouts.ai +1138 -1
- psychopy/app/Resources/classic/add.png +0 -0
- psychopy/app/Resources/classic/add@2x.png +0 -0
- psychopy/app/Resources/classic/addExp32.png +0 -0
- psychopy/app/Resources/classic/alerts.png +0 -0
- psychopy/app/Resources/classic/beta.png +0 -0
- psychopy/app/Resources/classic/browser.png +0 -0
- psychopy/app/Resources/classic/browser@2x.png +0 -0
- psychopy/app/Resources/classic/bug16.png +0 -0
- psychopy/app/Resources/classic/circle_mask.png +0 -0
- psychopy/app/Resources/classic/circle_mask@2x.png +0 -0
- psychopy/app/Resources/classic/clear.png +0 -0
- psychopy/app/Resources/classic/clear@2x.png +0 -0
- psychopy/app/Resources/classic/coderclass16.png +0 -0
- psychopy/app/Resources/classic/coderfunc16.png +0 -0
- psychopy/app/Resources/classic/coderimport16.png +0 -0
- psychopy/app/Resources/classic/coderjs16.png +0 -0
- psychopy/app/Resources/classic/coderpython16.png +0 -0
- psychopy/app/Resources/classic/codervar16.png +0 -0
- psychopy/app/Resources/classic/cogwindow32.png +0 -0
- psychopy/app/Resources/classic/color32.png +0 -0
- psychopy/app/Resources/classic/compile32.png +0 -0
- psychopy/app/Resources/classic/compile_js.png +0 -0
- psychopy/app/Resources/classic/compile_js@2x.png +0 -0
- psychopy/app/Resources/classic/compile_py.png +0 -0
- psychopy/app/Resources/classic/compile_py@2x.png +0 -0
- psychopy/app/Resources/classic/copy16.png +0 -0
- psychopy/app/Resources/classic/currentFile.png +0 -0
- psychopy/app/Resources/classic/currentFile@2x.png +0 -0
- psychopy/app/Resources/classic/delete16.png +0 -0
- psychopy/app/Resources/classic/desktop.png +0 -0
- psychopy/app/Resources/classic/desktop@2x.png +0 -0
- psychopy/app/Resources/classic/dirup16.png +0 -0
- psychopy/app/Resources/classic/docclose16.png +0 -0
- psychopy/app/Resources/classic/download.png +0 -0
- psychopy/app/Resources/classic/edit.png +0 -0
- psychopy/app/Resources/classic/edit@2x.png +0 -0
- psychopy/app/Resources/classic/editbtn.png +0 -0
- psychopy/app/Resources/classic/editbtn16.png +0 -0
- psychopy/app/Resources/classic/editbtn16@2x.png +0 -0
- psychopy/app/Resources/classic/editbtn@2x.png +0 -0
- psychopy/app/Resources/classic/email.png +0 -0
- psychopy/app/Resources/classic/email@2x.png +0 -0
- psychopy/app/Resources/classic/experiment.png +0 -0
- psychopy/app/Resources/classic/experiment@2x.png +0 -0
- psychopy/app/Resources/classic/expsettings.png +0 -0
- psychopy/app/Resources/classic/expsettings@2x.png +0 -0
- psychopy/app/Resources/classic/file.png +0 -0
- psychopy/app/Resources/classic/file@2x.png +0 -0
- psychopy/app/Resources/classic/filecsv16.png +0 -0
- psychopy/app/Resources/classic/fileimage16.png +0 -0
- psychopy/app/Resources/classic/filenew.png +0 -0
- psychopy/app/Resources/classic/filenew32.png +0 -0
- psychopy/app/Resources/classic/filenew@2x.png +0 -0
- psychopy/app/Resources/classic/fileopen.png +0 -0
- psychopy/app/Resources/classic/fileopen32.png +0 -0
- psychopy/app/Resources/classic/fileopen@2x.png +0 -0
- psychopy/app/Resources/classic/filesave.png +0 -0
- psychopy/app/Resources/classic/filesave32.png +0 -0
- psychopy/app/Resources/classic/filesave@2x.png +0 -0
- psychopy/app/Resources/classic/filesaveas.png +0 -0
- psychopy/app/Resources/classic/filesaveas32.png +0 -0
- psychopy/app/Resources/classic/filesaveas@2x.png +0 -0
- psychopy/app/Resources/classic/fileunknown16.png +0 -0
- psychopy/app/Resources/classic/filter.png +0 -0
- psychopy/app/Resources/classic/filter@2x.png +0 -0
- psychopy/app/Resources/classic/folder-open16.png +0 -0
- psychopy/app/Resources/classic/folder16.png +0 -0
- psychopy/app/Resources/classic/foldernew16.png +0 -0
- psychopy/app/Resources/classic/fork.png +0 -0
- psychopy/app/Resources/classic/fork@2x.png +0 -0
- psychopy/app/Resources/classic/github.png +0 -0
- psychopy/app/Resources/classic/github@2x.png +0 -0
- psychopy/app/Resources/classic/globe.png +0 -0
- psychopy/app/Resources/classic/globe@2x.png +0 -0
- psychopy/app/Resources/classic/globe_bug.png +0 -0
- psychopy/app/Resources/classic/globe_bug@2x.png +0 -0
- psychopy/app/Resources/classic/globe_greensync.png +0 -0
- psychopy/app/Resources/classic/globe_greensync@2x.png +0 -0
- psychopy/app/Resources/classic/globe_info.png +0 -0
- psychopy/app/Resources/classic/globe_info@2x.png +0 -0
- psychopy/app/Resources/classic/globe_magnifier.png +0 -0
- psychopy/app/Resources/classic/globe_magnifier@2x.png +0 -0
- psychopy/app/Resources/classic/globe_run.png +0 -0
- psychopy/app/Resources/classic/globe_run@2x.png +0 -0
- psychopy/app/Resources/classic/globe_user.png +0 -0
- psychopy/app/Resources/classic/globe_user@2x.png +0 -0
- psychopy/app/Resources/classic/goto16.png +0 -0
- psychopy/app/Resources/classic/greendot.png +0 -0
- psychopy/app/Resources/classic/greendot@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe.png +0 -0
- psychopy/app/Resources/classic/greenglobe@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_bug.png +0 -0
- psychopy/app/Resources/classic/greenglobe_bug@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_greensync.png +0 -0
- psychopy/app/Resources/classic/greenglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_info.png +0 -0
- psychopy/app/Resources/classic/greenglobe_info@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_magnifier.png +0 -0
- psychopy/app/Resources/classic/greenglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_run.png +0 -0
- psychopy/app/Resources/classic/greenglobe_run@2x.png +0 -0
- psychopy/app/Resources/classic/greenglobe_user.png +0 -0
- psychopy/app/Resources/classic/greenglobe_user@2x.png +0 -0
- psychopy/app/Resources/classic/greydot.png +0 -0
- psychopy/app/Resources/classic/greydot@2x.png +0 -0
- psychopy/app/Resources/classic/greytick.png +0 -0
- psychopy/app/Resources/classic/greytick@2x.png +0 -0
- psychopy/app/Resources/classic/invalid_img.png +0 -0
- psychopy/app/Resources/classic/jsPilot.png +0 -0
- psychopy/app/Resources/classic/jsPilot@2x.png +0 -0
- psychopy/app/Resources/classic/jsRun.png +0 -0
- psychopy/app/Resources/classic/jsRun@2x.png +0 -0
- psychopy/app/Resources/classic/libroot.png +0 -0
- psychopy/app/Resources/classic/libroot@2x.png +0 -0
- psychopy/app/Resources/classic/monitor16.png +0 -0
- psychopy/app/Resources/classic/monitors.png +0 -0
- psychopy/app/Resources/classic/monitors16.png +0 -0
- psychopy/app/Resources/classic/monitors32.png +0 -0
- psychopy/app/Resources/classic/monitors@2x.png +0 -0
- psychopy/app/Resources/classic/orangedot.png +0 -0
- psychopy/app/Resources/classic/orangedot@2x.png +0 -0
- psychopy/app/Resources/classic/pavlovia.png +0 -0
- psychopy/app/Resources/classic/pavlovia16.png +0 -0
- psychopy/app/Resources/classic/pavlovia16@2x.png +0 -0
- psychopy/app/Resources/classic/pavlovia@2x.png +0 -0
- psychopy/app/Resources/classic/pavsync.png +0 -0
- psychopy/app/Resources/classic/pavsync@2x.png +0 -0
- psychopy/app/Resources/classic/person_off.png +0 -0
- psychopy/app/Resources/classic/person_off@2x.png +0 -0
- psychopy/app/Resources/classic/person_on.png +0 -0
- psychopy/app/Resources/classic/person_on@2x.png +0 -0
- psychopy/app/Resources/classic/plugins32.png +0 -0
- psychopy/app/Resources/classic/plus.png +0 -0
- psychopy/app/Resources/classic/plus@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-app.png +0 -0
- psychopy/app/Resources/classic/preferences-app48.png +0 -0
- psychopy/app/Resources/classic/preferences-app@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-conn.png +0 -0
- psychopy/app/Resources/classic/preferences-conn48.png +0 -0
- psychopy/app/Resources/classic/preferences-conn@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-debug.png +0 -0
- psychopy/app/Resources/classic/preferences-debug@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-general.png +0 -0
- psychopy/app/Resources/classic/preferences-general48.png +0 -0
- psychopy/app/Resources/classic/preferences-general@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-hardware.png +0 -0
- psychopy/app/Resources/classic/preferences-hardware48.png +0 -0
- psychopy/app/Resources/classic/preferences-hardware@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-keyboard.png +0 -0
- psychopy/app/Resources/classic/preferences-keyboard48.png +0 -0
- psychopy/app/Resources/classic/preferences-keyboard@2x.png +0 -0
- psychopy/app/Resources/classic/preferences-pilot.png +0 -0
- psychopy/app/Resources/classic/preferences-pilot@2x.png +0 -0
- psychopy/app/Resources/classic/preferences32.png +0 -0
- psychopy/app/Resources/classic/pyPilot.png +0 -0
- psychopy/app/Resources/classic/pyPilot@2x.png +0 -0
- psychopy/app/Resources/classic/pyRun.png +0 -0
- psychopy/app/Resources/classic/pyRun@2x.png +0 -0
- psychopy/app/Resources/classic/reddot.png +0 -0
- psychopy/app/Resources/classic/reddot@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe.png +0 -0
- psychopy/app/Resources/classic/redglobe@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_bug.png +0 -0
- psychopy/app/Resources/classic/redglobe_bug@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_greensync.png +0 -0
- psychopy/app/Resources/classic/redglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_info.png +0 -0
- psychopy/app/Resources/classic/redglobe_info@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_magnifier.png +0 -0
- psychopy/app/Resources/classic/redglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_run.png +0 -0
- psychopy/app/Resources/classic/redglobe_run@2x.png +0 -0
- psychopy/app/Resources/classic/redglobe_user.png +0 -0
- psychopy/app/Resources/classic/redglobe_user@2x.png +0 -0
- psychopy/app/Resources/classic/redo.png +0 -0
- psychopy/app/Resources/classic/redo32.png +0 -0
- psychopy/app/Resources/classic/redo@2x.png +0 -0
- psychopy/app/Resources/classic/removeExp32.png +0 -0
- psychopy/app/Resources/classic/rename16.png +0 -0
- psychopy/app/Resources/classic/restart.png +0 -0
- psychopy/app/Resources/classic/restart@2x.png +0 -0
- psychopy/app/Resources/classic/run.png +0 -0
- psychopy/app/Resources/classic/run@2x.png +0 -0
- psychopy/app/Resources/classic/runner.png +0 -0
- psychopy/app/Resources/classic/runner@2x.png +0 -0
- psychopy/app/Resources/classic/runnerPilot.png +0 -0
- psychopy/app/Resources/classic/runnerPilot@2x.png +0 -0
- psychopy/app/Resources/classic/savebtn.png +0 -0
- psychopy/app/Resources/classic/savebtn16.png +0 -0
- psychopy/app/Resources/classic/savebtn16@2x.png +0 -0
- psychopy/app/Resources/classic/savebtn@2x.png +0 -0
- psychopy/app/Resources/classic/search.png +0 -0
- psychopy/app/Resources/classic/search@2x.png +0 -0
- psychopy/app/Resources/classic/showBuilder.png +0 -0
- psychopy/app/Resources/classic/showBuilder@2x.png +0 -0
- psychopy/app/Resources/classic/showCoder.png +0 -0
- psychopy/app/Resources/classic/showCoder@2x.png +0 -0
- psychopy/app/Resources/classic/showRunner.png +0 -0
- psychopy/app/Resources/classic/showRunner@2x.png +0 -0
- psychopy/app/Resources/classic/starred.png +0 -0
- psychopy/app/Resources/classic/starred@2x.png +0 -0
- psychopy/app/Resources/classic/start.png +0 -0
- psychopy/app/Resources/classic/start@2x.png +0 -0
- psychopy/app/Resources/classic/stdout.png +0 -0
- psychopy/app/Resources/classic/stop.png +0 -0
- psychopy/app/Resources/classic/stop32.png +0 -0
- psychopy/app/Resources/classic/stop@2x.png +0 -0
- psychopy/app/Resources/classic/switchCtrlBot.png +0 -0
- psychopy/app/Resources/classic/switchCtrlBot@2x.png +0 -0
- psychopy/app/Resources/classic/switchCtrlLeft.png +0 -0
- psychopy/app/Resources/classic/switchCtrlLeft@2x.png +0 -0
- psychopy/app/Resources/classic/switchCtrlRight.png +0 -0
- psychopy/app/Resources/classic/switchCtrlRight@2x.png +0 -0
- psychopy/app/Resources/classic/switchCtrlTop.png +0 -0
- psychopy/app/Resources/classic/switchCtrlTop@2x.png +0 -0
- psychopy/app/Resources/classic/tick.png +0 -0
- psychopy/app/Resources/classic/tick@2x.png +0 -0
- psychopy/app/Resources/classic/undo.png +0 -0
- psychopy/app/Resources/classic/undo32.png +0 -0
- psychopy/app/Resources/classic/undo@2x.png +0 -0
- psychopy/app/Resources/classic/unstarred.png +0 -0
- psychopy/app/Resources/classic/unstarred@2x.png +0 -0
- psychopy/app/Resources/classic/user_none.png +0 -0
- psychopy/app/Resources/classic/view-refresh16.png +0 -0
- psychopy/app/Resources/classic/viewbtn.png +0 -0
- psychopy/app/Resources/classic/viewbtn16.png +0 -0
- psychopy/app/Resources/classic/viewbtn16@2x.png +0 -0
- psychopy/app/Resources/classic/viewbtn@2x.png +0 -0
- psychopy/app/Resources/classic/windows16.png +0 -0
- psychopy/app/Resources/classic/windows16@2x.png +0 -0
- psychopy/app/Resources/click.png +0 -0
- psychopy/app/Resources/coder.ico +0 -0
- psychopy/app/Resources/creditCard.png +0 -0
- psychopy/app/Resources/dark/FlowBottom_CompLeft.png +0 -0
- psychopy/app/Resources/dark/FlowBottom_CompRight.png +0 -0
- psychopy/app/Resources/dark/FlowTop_CompLeft.png +0 -0
- psychopy/app/Resources/dark/FlowTop_CompRight.png +0 -0
- psychopy/app/Resources/dark/README.txt +3 -0
- psychopy/app/Resources/dark/__init__.py +0 -0
- psychopy/app/Resources/dark/add.png +0 -0
- psychopy/app/Resources/dark/add@2x.png +0 -0
- psychopy/app/Resources/dark/addExp32.png +0 -0
- psychopy/app/Resources/dark/addExp32@2x.png +0 -0
- psychopy/app/Resources/dark/alerts.png +0 -0
- psychopy/app/Resources/dark/alerts@2x.png +0 -0
- psychopy/app/Resources/dark/beta.png +0 -0
- psychopy/app/Resources/dark/beta@2x.png +0 -0
- psychopy/app/Resources/dark/browser.png +0 -0
- psychopy/app/Resources/dark/browser@2x.png +0 -0
- psychopy/app/Resources/dark/bug16.png +0 -0
- psychopy/app/Resources/dark/bug16@2x.png +0 -0
- psychopy/app/Resources/dark/circle_mask.png +0 -0
- psychopy/app/Resources/dark/circle_mask@2x.png +0 -0
- psychopy/app/Resources/dark/clear.png +0 -0
- psychopy/app/Resources/dark/clear@2x.png +0 -0
- psychopy/app/Resources/dark/coderclass16.png +0 -0
- psychopy/app/Resources/dark/coderclass16@2x.png +0 -0
- psychopy/app/Resources/dark/coderfunc16.png +0 -0
- psychopy/app/Resources/dark/coderfunc16@2x.png +0 -0
- psychopy/app/Resources/dark/coderimport16.png +0 -0
- psychopy/app/Resources/dark/coderimport16@2x.png +0 -0
- psychopy/app/Resources/dark/coderjs.png +0 -0
- psychopy/app/Resources/dark/coderjs@2x.png +0 -0
- psychopy/app/Resources/dark/coderpython.png +0 -0
- psychopy/app/Resources/dark/coderpython@2x.png +0 -0
- psychopy/app/Resources/dark/codervar16.png +0 -0
- psychopy/app/Resources/dark/codervar16@2x.png +0 -0
- psychopy/app/Resources/dark/cogwindow32.png +0 -0
- psychopy/app/Resources/dark/cogwindow32@2x.png +0 -0
- psychopy/app/Resources/dark/color32.png +0 -0
- psychopy/app/Resources/dark/color32@2x.png +0 -0
- psychopy/app/Resources/dark/compile_js.png +0 -0
- psychopy/app/Resources/dark/compile_js@2x.png +0 -0
- psychopy/app/Resources/dark/compile_py.png +0 -0
- psychopy/app/Resources/dark/compile_py@2x.png +0 -0
- psychopy/app/Resources/dark/copy16.png +0 -0
- psychopy/app/Resources/dark/copy16@2x.png +0 -0
- psychopy/app/Resources/dark/currentFile16.png +0 -0
- psychopy/app/Resources/dark/currentFile16@2x.png +0 -0
- psychopy/app/Resources/dark/delete16.png +0 -0
- psychopy/app/Resources/dark/delete16@2x.png +0 -0
- psychopy/app/Resources/dark/desktop.png +0 -0
- psychopy/app/Resources/dark/desktop@2x.png +0 -0
- psychopy/app/Resources/dark/dirup16.png +0 -0
- psychopy/app/Resources/dark/dirup16@2x.png +0 -0
- psychopy/app/Resources/dark/docclose16.png +0 -0
- psychopy/app/Resources/dark/docclose16@2x.png +0 -0
- psychopy/app/Resources/dark/download.png +0 -0
- psychopy/app/Resources/dark/download@2x.png +0 -0
- psychopy/app/Resources/dark/edit.png +0 -0
- psychopy/app/Resources/dark/edit@2x.png +0 -0
- psychopy/app/Resources/dark/editbtn16.png +0 -0
- psychopy/app/Resources/dark/editbtn16@2x.png +0 -0
- psychopy/app/Resources/dark/email.png +0 -0
- psychopy/app/Resources/dark/email@2x.png +0 -0
- psychopy/app/Resources/dark/experiment.png +0 -0
- psychopy/app/Resources/dark/experiment@2x.png +0 -0
- psychopy/app/Resources/dark/expsettings.png +0 -0
- psychopy/app/Resources/dark/expsettings@2x.png +0 -0
- psychopy/app/Resources/dark/file.png +0 -0
- psychopy/app/Resources/dark/file@16w.png +0 -0
- psychopy/app/Resources/dark/file@16w@2x.png +0 -0
- psychopy/app/Resources/dark/file@2x.png +0 -0
- psychopy/app/Resources/dark/filecsv16.png +0 -0
- psychopy/app/Resources/dark/filecsv16@2x.png +0 -0
- psychopy/app/Resources/dark/fileimage16.png +0 -0
- psychopy/app/Resources/dark/fileimage16@2x.png +0 -0
- psychopy/app/Resources/dark/filenew.png +0 -0
- psychopy/app/Resources/dark/filenew32.png +0 -0
- psychopy/app/Resources/dark/filenew32@2x.png +0 -0
- psychopy/app/Resources/dark/filenew@2x.png +0 -0
- psychopy/app/Resources/dark/fileopen.png +0 -0
- psychopy/app/Resources/dark/fileopen32.png +0 -0
- psychopy/app/Resources/dark/fileopen32@2x.png +0 -0
- psychopy/app/Resources/dark/fileopen@2x.png +0 -0
- psychopy/app/Resources/dark/filesave.png +0 -0
- psychopy/app/Resources/dark/filesave32.png +0 -0
- psychopy/app/Resources/dark/filesave32@2x.png +0 -0
- psychopy/app/Resources/dark/filesave@2x.png +0 -0
- psychopy/app/Resources/dark/filesaveas.png +0 -0
- psychopy/app/Resources/dark/filesaveas32.png +0 -0
- psychopy/app/Resources/dark/filesaveas32@2x.png +0 -0
- psychopy/app/Resources/dark/filesaveas@2x.png +0 -0
- psychopy/app/Resources/dark/fileunknown16.png +0 -0
- psychopy/app/Resources/dark/fileunknown16@2x.png +0 -0
- psychopy/app/Resources/dark/filter.png +0 -0
- psychopy/app/Resources/dark/filter@2x.png +0 -0
- psychopy/app/Resources/dark/folder-open16.png +0 -0
- psychopy/app/Resources/dark/folder-open16@2x.png +0 -0
- psychopy/app/Resources/dark/folder16.png +0 -0
- psychopy/app/Resources/dark/folder16@2x.png +0 -0
- psychopy/app/Resources/dark/foldernew16.png +0 -0
- psychopy/app/Resources/dark/foldernew16@2x.png +0 -0
- psychopy/app/Resources/dark/fork.png +0 -0
- psychopy/app/Resources/dark/fork@2x.png +0 -0
- psychopy/app/Resources/dark/github.png +0 -0
- psychopy/app/Resources/dark/github@2x.png +0 -0
- psychopy/app/Resources/dark/globe.png +0 -0
- psychopy/app/Resources/dark/globe@2x.png +0 -0
- psychopy/app/Resources/dark/globe_bug.png +0 -0
- psychopy/app/Resources/dark/globe_bug@2x.png +0 -0
- psychopy/app/Resources/dark/globe_greensync.png +0 -0
- psychopy/app/Resources/dark/globe_greensync@2x.png +0 -0
- psychopy/app/Resources/dark/globe_info.png +0 -0
- psychopy/app/Resources/dark/globe_info@2x.png +0 -0
- psychopy/app/Resources/dark/globe_magnifier.png +0 -0
- psychopy/app/Resources/dark/globe_magnifier@2x.png +0 -0
- psychopy/app/Resources/dark/globe_run.png +0 -0
- psychopy/app/Resources/dark/globe_run@2x.png +0 -0
- psychopy/app/Resources/dark/globe_user.png +0 -0
- psychopy/app/Resources/dark/globe_user@2x.png +0 -0
- psychopy/app/Resources/dark/goto.png +0 -0
- psychopy/app/Resources/dark/goto@2x.png +0 -0
- psychopy/app/Resources/dark/greendot.png +0 -0
- psychopy/app/Resources/dark/greendot@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe.png +0 -0
- psychopy/app/Resources/dark/greenglobe@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_bug.png +0 -0
- psychopy/app/Resources/dark/greenglobe_bug@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_greensync.png +0 -0
- psychopy/app/Resources/dark/greenglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_info.png +0 -0
- psychopy/app/Resources/dark/greenglobe_info@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_magnifier.png +0 -0
- psychopy/app/Resources/dark/greenglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_run.png +0 -0
- psychopy/app/Resources/dark/greenglobe_run@2x.png +0 -0
- psychopy/app/Resources/dark/greenglobe_user.png +0 -0
- psychopy/app/Resources/dark/greenglobe_user@2x.png +0 -0
- psychopy/app/Resources/dark/greydot.png +0 -0
- psychopy/app/Resources/dark/greydot@2x.png +0 -0
- psychopy/app/Resources/dark/greytick.png +0 -0
- psychopy/app/Resources/dark/greytick@2x.png +0 -0
- psychopy/app/Resources/dark/invalid_img.png +0 -0
- psychopy/app/Resources/dark/jsPilot.png +0 -0
- psychopy/app/Resources/dark/jsPilot@2x.png +0 -0
- psychopy/app/Resources/dark/jsRun.png +0 -0
- psychopy/app/Resources/dark/jsRun@2x.png +0 -0
- psychopy/app/Resources/dark/libroot16.png +0 -0
- psychopy/app/Resources/dark/libroot16@2x.png +0 -0
- psychopy/app/Resources/dark/monitor16.png +0 -0
- psychopy/app/Resources/dark/monitor16@2x.png +0 -0
- psychopy/app/Resources/dark/monitors.png +0 -0
- psychopy/app/Resources/dark/monitors32.png +0 -0
- psychopy/app/Resources/dark/monitors32@2x.png +0 -0
- psychopy/app/Resources/dark/monitors@2x.png +0 -0
- psychopy/app/Resources/dark/orangedot.png +0 -0
- psychopy/app/Resources/dark/orangedot@2x.png +0 -0
- psychopy/app/Resources/dark/pavlovia.png +0 -0
- psychopy/app/Resources/dark/pavlovia16.png +0 -0
- psychopy/app/Resources/dark/pavlovia16@2x.png +0 -0
- psychopy/app/Resources/dark/pavlovia@2x.png +0 -0
- psychopy/app/Resources/dark/pavsync.png +0 -0
- psychopy/app/Resources/dark/pavsync@2x.png +0 -0
- psychopy/app/Resources/dark/person_off.png +0 -0
- psychopy/app/Resources/dark/person_off@2x.png +0 -0
- psychopy/app/Resources/dark/person_on.png +0 -0
- psychopy/app/Resources/dark/person_on@2x.png +0 -0
- psychopy/app/Resources/dark/plugins32.png +0 -0
- psychopy/app/Resources/dark/plugins32@2x.png +0 -0
- psychopy/app/Resources/dark/plus.png +0 -0
- psychopy/app/Resources/dark/plus@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-app.png +0 -0
- psychopy/app/Resources/dark/preferences-app48.png +0 -0
- psychopy/app/Resources/dark/preferences-app48@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-app@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-conn.png +0 -0
- psychopy/app/Resources/dark/preferences-conn48.png +0 -0
- psychopy/app/Resources/dark/preferences-conn48@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-conn@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-debug.png +0 -0
- psychopy/app/Resources/dark/preferences-debug@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-general.png +0 -0
- psychopy/app/Resources/dark/preferences-general48.png +0 -0
- psychopy/app/Resources/dark/preferences-general48@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-general@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-hardware.png +0 -0
- psychopy/app/Resources/dark/preferences-hardware48.png +0 -0
- psychopy/app/Resources/dark/preferences-hardware48@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-hardware@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-keyboard.png +0 -0
- psychopy/app/Resources/dark/preferences-keyboard48.png +0 -0
- psychopy/app/Resources/dark/preferences-keyboard48@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-keyboard@2x.png +0 -0
- psychopy/app/Resources/dark/preferences-pilot.png +0 -0
- psychopy/app/Resources/dark/preferences-pilot@2x.png +0 -0
- psychopy/app/Resources/dark/preferences32.png +0 -0
- psychopy/app/Resources/dark/preferences32@2x.png +0 -0
- psychopy/app/Resources/dark/pyPilot.png +0 -0
- psychopy/app/Resources/dark/pyPilot@2x.png +0 -0
- psychopy/app/Resources/dark/pyRun.png +0 -0
- psychopy/app/Resources/dark/pyRun@2x.png +0 -0
- psychopy/app/Resources/dark/reddot.png +0 -0
- psychopy/app/Resources/dark/reddot@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe.png +0 -0
- psychopy/app/Resources/dark/redglobe@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_bug.png +0 -0
- psychopy/app/Resources/dark/redglobe_bug@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_greensync.png +0 -0
- psychopy/app/Resources/dark/redglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_info.png +0 -0
- psychopy/app/Resources/dark/redglobe_info@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_magnifier.png +0 -0
- psychopy/app/Resources/dark/redglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_run.png +0 -0
- psychopy/app/Resources/dark/redglobe_run@2x.png +0 -0
- psychopy/app/Resources/dark/redglobe_user.png +0 -0
- psychopy/app/Resources/dark/redglobe_user@2x.png +0 -0
- psychopy/app/Resources/dark/redo.png +0 -0
- psychopy/app/Resources/dark/redo32.png +0 -0
- psychopy/app/Resources/dark/redo32@2x.png +0 -0
- psychopy/app/Resources/dark/redo@2x.png +0 -0
- psychopy/app/Resources/dark/removeExp32.png +0 -0
- psychopy/app/Resources/dark/removeExp32@2x.png +0 -0
- psychopy/app/Resources/dark/rename16.png +0 -0
- psychopy/app/Resources/dark/rename16@2x.png +0 -0
- psychopy/app/Resources/dark/restart.png +0 -0
- psychopy/app/Resources/dark/restart@2x.png +0 -0
- psychopy/app/Resources/dark/runner.png +0 -0
- psychopy/app/Resources/dark/runner@2x.png +0 -0
- psychopy/app/Resources/dark/runnerPilot.png +0 -0
- psychopy/app/Resources/dark/runnerPilot@2x.png +0 -0
- psychopy/app/Resources/dark/savebtn16.png +0 -0
- psychopy/app/Resources/dark/savebtn16@2x.png +0 -0
- psychopy/app/Resources/dark/search.png +0 -0
- psychopy/app/Resources/dark/search@2x.png +0 -0
- psychopy/app/Resources/dark/showBuilder.png +0 -0
- psychopy/app/Resources/dark/showBuilder@2x.png +0 -0
- psychopy/app/Resources/dark/showCoder.png +0 -0
- psychopy/app/Resources/dark/showCoder@2x.png +0 -0
- psychopy/app/Resources/dark/showRunner.png +0 -0
- psychopy/app/Resources/dark/showRunner@2x.png +0 -0
- psychopy/app/Resources/dark/starred.png +0 -0
- psychopy/app/Resources/dark/starred@2x.png +0 -0
- psychopy/app/Resources/dark/start.png +0 -0
- psychopy/app/Resources/dark/start@2x.png +0 -0
- psychopy/app/Resources/dark/stdout.png +0 -0
- psychopy/app/Resources/dark/stdout@2x.png +0 -0
- psychopy/app/Resources/dark/stop.png +0 -0
- psychopy/app/Resources/dark/stop32.png +0 -0
- psychopy/app/Resources/dark/stop32@2x.png +0 -0
- psychopy/app/Resources/dark/stop@2x.png +0 -0
- psychopy/app/Resources/dark/switchCtrlBot.png +0 -0
- psychopy/app/Resources/dark/switchCtrlBot@2x.png +0 -0
- psychopy/app/Resources/dark/switchCtrlLeft.png +0 -0
- psychopy/app/Resources/dark/switchCtrlLeft@2x.png +0 -0
- psychopy/app/Resources/dark/switchCtrlRight.png +0 -0
- psychopy/app/Resources/dark/switchCtrlRight@2x.png +0 -0
- psychopy/app/Resources/dark/switchCtrlTop.png +0 -0
- psychopy/app/Resources/dark/switchCtrlTop@2x.png +0 -0
- psychopy/app/Resources/dark/tick.png +0 -0
- psychopy/app/Resources/dark/tick@2x.png +0 -0
- psychopy/app/Resources/dark/undo.png +0 -0
- psychopy/app/Resources/dark/undo32.png +0 -0
- psychopy/app/Resources/dark/undo32@2x.png +0 -0
- psychopy/app/Resources/dark/undo@2x.png +0 -0
- psychopy/app/Resources/dark/unstarred.png +0 -0
- psychopy/app/Resources/dark/unstarred@2x.png +0 -0
- psychopy/app/Resources/dark/user_none.png +0 -0
- psychopy/app/Resources/dark/view-refresh16.png +0 -0
- psychopy/app/Resources/dark/view-refresh16@2x.png +0 -0
- psychopy/app/Resources/dark/viewbtn16.png +0 -0
- psychopy/app/Resources/dark/viewbtn16@2x.png +0 -0
- psychopy/app/Resources/dark/windows16.png +0 -0
- psychopy/app/Resources/dark/windows16@2x.png +0 -0
- psychopy/app/Resources/default.mp3 +0 -0
- psychopy/app/Resources/default.mp4 +0 -0
- psychopy/app/Resources/default.png +0 -0
- psychopy/app/Resources/fonts/Arvo-Bold.ttf +0 -0
- psychopy/app/Resources/fonts/Arvo-BoldItalic.ttf +0 -0
- psychopy/app/Resources/fonts/Arvo-Italic.ttf +0 -0
- psychopy/app/Resources/fonts/Arvo-Regular.ttf +0 -0
- psychopy/app/Resources/fonts/DejaVuSerif.ttf +0 -0
- psychopy/app/Resources/fonts/IndieFlower-Regular.ttf +0 -0
- psychopy/app/Resources/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- psychopy/app/Resources/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-Bold.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-BoldItalic.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-ExtraBold.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-ExtraBoldItalic.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-Italic.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-Light.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-LightItalic.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-Regular.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-SemiBold.ttf +0 -0
- psychopy/app/Resources/fonts/OpenSans-SemiBoldItalic.ttf +0 -0
- psychopy/app/Resources/instruct1.png +0 -0
- psychopy/app/Resources/instruct2.png +0 -0
- psychopy/app/Resources/light/FlowBottom_CompLeft.png +0 -0
- psychopy/app/Resources/light/FlowBottom_CompRight.png +0 -0
- psychopy/app/Resources/light/FlowTop_CompLeft.png +0 -0
- psychopy/app/Resources/light/FlowTop_CompRight.png +0 -0
- psychopy/app/Resources/light/README.txt +3 -0
- psychopy/app/Resources/light/__init__.py +0 -0
- psychopy/app/Resources/light/add.png +0 -0
- psychopy/app/Resources/light/add@2x.png +0 -0
- psychopy/app/Resources/light/addExp32.png +0 -0
- psychopy/app/Resources/light/addExp32@2x.png +0 -0
- psychopy/app/Resources/light/alerts.png +0 -0
- psychopy/app/Resources/light/alerts@2x.png +0 -0
- psychopy/app/Resources/light/beta.png +0 -0
- psychopy/app/Resources/light/beta@2x.png +0 -0
- psychopy/app/Resources/light/browser.png +0 -0
- psychopy/app/Resources/light/browser@2x.png +0 -0
- psychopy/app/Resources/light/bug16.png +0 -0
- psychopy/app/Resources/light/bug16@2x.png +0 -0
- psychopy/app/Resources/light/circle_mask.png +0 -0
- psychopy/app/Resources/light/circle_mask@2x.png +0 -0
- psychopy/app/Resources/light/clear.png +0 -0
- psychopy/app/Resources/light/clear@2x.png +0 -0
- psychopy/app/Resources/light/coderclass16.png +0 -0
- psychopy/app/Resources/light/coderclass16@2x.png +0 -0
- psychopy/app/Resources/light/coderfunc16.png +0 -0
- psychopy/app/Resources/light/coderfunc16@2x.png +0 -0
- psychopy/app/Resources/light/coderimport16.png +0 -0
- psychopy/app/Resources/light/coderimport16@2x.png +0 -0
- psychopy/app/Resources/light/coderjs.png +0 -0
- psychopy/app/Resources/light/coderjs@2x.png +0 -0
- psychopy/app/Resources/light/coderpython.png +0 -0
- psychopy/app/Resources/light/coderpython@2x.png +0 -0
- psychopy/app/Resources/light/codervar16.png +0 -0
- psychopy/app/Resources/light/codervar16@2x.png +0 -0
- psychopy/app/Resources/light/cogwindow32.png +0 -0
- psychopy/app/Resources/light/cogwindow32@2x.png +0 -0
- psychopy/app/Resources/light/color16.png +0 -0
- psychopy/app/Resources/light/color16@2x.png +0 -0
- psychopy/app/Resources/light/color32.png +0 -0
- psychopy/app/Resources/light/color32@2x.png +0 -0
- psychopy/app/Resources/light/compile_js.png +0 -0
- psychopy/app/Resources/light/compile_js@2x.png +0 -0
- psychopy/app/Resources/light/compile_py.png +0 -0
- psychopy/app/Resources/light/compile_py@2x.png +0 -0
- psychopy/app/Resources/light/copy16.png +0 -0
- psychopy/app/Resources/light/copy16@2x.png +0 -0
- psychopy/app/Resources/light/currentFile16.png +0 -0
- psychopy/app/Resources/light/currentFile16@2x.png +0 -0
- psychopy/app/Resources/light/delete16.png +0 -0
- psychopy/app/Resources/light/delete16@2x.png +0 -0
- psychopy/app/Resources/light/delete8.png +0 -0
- psychopy/app/Resources/light/desktop.png +0 -0
- psychopy/app/Resources/light/desktop@2x.png +0 -0
- psychopy/app/Resources/light/dirup16.png +0 -0
- psychopy/app/Resources/light/dirup16@2x.png +0 -0
- psychopy/app/Resources/light/docclose16.png +0 -0
- psychopy/app/Resources/light/docclose16@2x.png +0 -0
- psychopy/app/Resources/light/download.png +0 -0
- psychopy/app/Resources/light/download@2x.png +0 -0
- psychopy/app/Resources/light/edit.png +0 -0
- psychopy/app/Resources/light/edit@2x.png +0 -0
- psychopy/app/Resources/light/editbtn16.png +0 -0
- psychopy/app/Resources/light/editbtn16@2x.png +0 -0
- psychopy/app/Resources/light/email.png +0 -0
- psychopy/app/Resources/light/email@2x.png +0 -0
- psychopy/app/Resources/light/experiment.png +0 -0
- psychopy/app/Resources/light/experiment@2x.png +0 -0
- psychopy/app/Resources/light/expsettings.png +0 -0
- psychopy/app/Resources/light/expsettings@2x.png +0 -0
- psychopy/app/Resources/light/file.png +0 -0
- psychopy/app/Resources/light/file@2x.png +0 -0
- psychopy/app/Resources/light/filecsv16.png +0 -0
- psychopy/app/Resources/light/filecsv16@2x.png +0 -0
- psychopy/app/Resources/light/fileimage16.png +0 -0
- psychopy/app/Resources/light/fileimage16@2x.png +0 -0
- psychopy/app/Resources/light/filenew.png +0 -0
- psychopy/app/Resources/light/filenew32.png +0 -0
- psychopy/app/Resources/light/filenew32@2x.png +0 -0
- psychopy/app/Resources/light/filenew@2x.png +0 -0
- psychopy/app/Resources/light/fileopen.png +0 -0
- psychopy/app/Resources/light/fileopen32.png +0 -0
- psychopy/app/Resources/light/fileopen32@2x.png +0 -0
- psychopy/app/Resources/light/fileopen@2x.png +0 -0
- psychopy/app/Resources/light/filesave.png +0 -0
- psychopy/app/Resources/light/filesave32.png +0 -0
- psychopy/app/Resources/light/filesave32@2x.png +0 -0
- psychopy/app/Resources/light/filesave@2x.png +0 -0
- psychopy/app/Resources/light/filesaveas.png +0 -0
- psychopy/app/Resources/light/filesaveas32.png +0 -0
- psychopy/app/Resources/light/filesaveas32@2x.png +0 -0
- psychopy/app/Resources/light/filesaveas@2x.png +0 -0
- psychopy/app/Resources/light/fileunknown16.png +0 -0
- psychopy/app/Resources/light/fileunknown16@2x.png +0 -0
- psychopy/app/Resources/light/filter.png +0 -0
- psychopy/app/Resources/light/filter@2x.png +0 -0
- psychopy/app/Resources/light/folder-open16.png +0 -0
- psychopy/app/Resources/light/folder-open16@2x.png +0 -0
- psychopy/app/Resources/light/folder16.png +0 -0
- psychopy/app/Resources/light/folder16@2x.png +0 -0
- psychopy/app/Resources/light/foldernew16.png +0 -0
- psychopy/app/Resources/light/foldernew16@2x.png +0 -0
- psychopy/app/Resources/light/fork.png +0 -0
- psychopy/app/Resources/light/fork@2x.png +0 -0
- psychopy/app/Resources/light/github.png +0 -0
- psychopy/app/Resources/light/github@2x.png +0 -0
- psychopy/app/Resources/light/globe.png +0 -0
- psychopy/app/Resources/light/globe@2x.png +0 -0
- psychopy/app/Resources/light/globe_bug.png +0 -0
- psychopy/app/Resources/light/globe_bug@2x.png +0 -0
- psychopy/app/Resources/light/globe_greensync.png +0 -0
- psychopy/app/Resources/light/globe_greensync@2x.png +0 -0
- psychopy/app/Resources/light/globe_info.png +0 -0
- psychopy/app/Resources/light/globe_info@2x.png +0 -0
- psychopy/app/Resources/light/globe_magnifier.png +0 -0
- psychopy/app/Resources/light/globe_magnifier@2x.png +0 -0
- psychopy/app/Resources/light/globe_run.png +0 -0
- psychopy/app/Resources/light/globe_run@2x.png +0 -0
- psychopy/app/Resources/light/globe_user.png +0 -0
- psychopy/app/Resources/light/globe_user@2x.png +0 -0
- psychopy/app/Resources/light/goto16.png +0 -0
- psychopy/app/Resources/light/goto16@2x.png +0 -0
- psychopy/app/Resources/light/greendot.png +0 -0
- psychopy/app/Resources/light/greendot@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe.png +0 -0
- psychopy/app/Resources/light/greenglobe@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_bug.png +0 -0
- psychopy/app/Resources/light/greenglobe_bug@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_greensync.png +0 -0
- psychopy/app/Resources/light/greenglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_info.png +0 -0
- psychopy/app/Resources/light/greenglobe_info@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_magnifier.png +0 -0
- psychopy/app/Resources/light/greenglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_run.png +0 -0
- psychopy/app/Resources/light/greenglobe_run@2x.png +0 -0
- psychopy/app/Resources/light/greenglobe_user.png +0 -0
- psychopy/app/Resources/light/greenglobe_user@2x.png +0 -0
- psychopy/app/Resources/light/greydot.png +0 -0
- psychopy/app/Resources/light/greydot@2x.png +0 -0
- psychopy/app/Resources/light/greytick.png +0 -0
- psychopy/app/Resources/light/greytick@2x.png +0 -0
- psychopy/app/Resources/light/invalid_img.png +0 -0
- psychopy/app/Resources/light/jsPilot.png +0 -0
- psychopy/app/Resources/light/jsPilot@2x.png +0 -0
- psychopy/app/Resources/light/jsRun.png +0 -0
- psychopy/app/Resources/light/jsRun@2x.png +0 -0
- psychopy/app/Resources/light/libroot16.png +0 -0
- psychopy/app/Resources/light/libroot16@2x.png +0 -0
- psychopy/app/Resources/light/monitor16.png +0 -0
- psychopy/app/Resources/light/monitor16@2x.png +0 -0
- psychopy/app/Resources/light/monitors.png +0 -0
- psychopy/app/Resources/light/monitors32.png +0 -0
- psychopy/app/Resources/light/monitors32@2x.png +0 -0
- psychopy/app/Resources/light/monitors@2x.png +0 -0
- psychopy/app/Resources/light/orangedot.png +0 -0
- psychopy/app/Resources/light/orangedot@2x.png +0 -0
- psychopy/app/Resources/light/pavlovia.png +0 -0
- psychopy/app/Resources/light/pavlovia16.png +0 -0
- psychopy/app/Resources/light/pavlovia16@2x.png +0 -0
- psychopy/app/Resources/light/pavlovia@2x.png +0 -0
- psychopy/app/Resources/light/pavsync.png +0 -0
- psychopy/app/Resources/light/pavsync@2x.png +0 -0
- psychopy/app/Resources/light/person_off.png +0 -0
- psychopy/app/Resources/light/person_off@2x.png +0 -0
- psychopy/app/Resources/light/person_on.png +0 -0
- psychopy/app/Resources/light/person_on@2x.png +0 -0
- psychopy/app/Resources/light/plugins32.png +0 -0
- psychopy/app/Resources/light/plugins32@2x.png +0 -0
- psychopy/app/Resources/light/plus.png +0 -0
- psychopy/app/Resources/light/plus@2x.png +0 -0
- psychopy/app/Resources/light/preferences-app.png +0 -0
- psychopy/app/Resources/light/preferences-app48.png +0 -0
- psychopy/app/Resources/light/preferences-app48@2x.png +0 -0
- psychopy/app/Resources/light/preferences-app@2x.png +0 -0
- psychopy/app/Resources/light/preferences-conn.png +0 -0
- psychopy/app/Resources/light/preferences-conn48.png +0 -0
- psychopy/app/Resources/light/preferences-conn48@2x.png +0 -0
- psychopy/app/Resources/light/preferences-conn@2x.png +0 -0
- psychopy/app/Resources/light/preferences-general.png +0 -0
- psychopy/app/Resources/light/preferences-general48.png +0 -0
- psychopy/app/Resources/light/preferences-general48@2x.png +0 -0
- psychopy/app/Resources/light/preferences-general@2x.png +0 -0
- psychopy/app/Resources/light/preferences-hardware.png +0 -0
- psychopy/app/Resources/light/preferences-hardware48.png +0 -0
- psychopy/app/Resources/light/preferences-hardware48@2x.png +0 -0
- psychopy/app/Resources/light/preferences-hardware@2x.png +0 -0
- psychopy/app/Resources/light/preferences-keyboard.png +0 -0
- psychopy/app/Resources/light/preferences-keyboard48.png +0 -0
- psychopy/app/Resources/light/preferences-keyboard48@2x.png +0 -0
- psychopy/app/Resources/light/preferences-keyboard@2x.png +0 -0
- psychopy/app/Resources/light/preferences-pilot.png +0 -0
- psychopy/app/Resources/light/preferences-pilot@2x.png +0 -0
- psychopy/app/Resources/light/preferences32.png +0 -0
- psychopy/app/Resources/light/preferences32@2x.png +0 -0
- psychopy/app/Resources/light/pyPilot.png +0 -0
- psychopy/app/Resources/light/pyPilot@2x.png +0 -0
- psychopy/app/Resources/light/pyRun.png +0 -0
- psychopy/app/Resources/light/pyRun@2x.png +0 -0
- psychopy/app/Resources/light/reddot.png +0 -0
- psychopy/app/Resources/light/reddot@2x.png +0 -0
- psychopy/app/Resources/light/redglobe.png +0 -0
- psychopy/app/Resources/light/redglobe@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_bug.png +0 -0
- psychopy/app/Resources/light/redglobe_bug@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_greensync.png +0 -0
- psychopy/app/Resources/light/redglobe_greensync@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_info.png +0 -0
- psychopy/app/Resources/light/redglobe_info@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_magnifier.png +0 -0
- psychopy/app/Resources/light/redglobe_magnifier@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_run.png +0 -0
- psychopy/app/Resources/light/redglobe_run@2x.png +0 -0
- psychopy/app/Resources/light/redglobe_user.png +0 -0
- psychopy/app/Resources/light/redglobe_user@2x.png +0 -0
- psychopy/app/Resources/light/redo.png +0 -0
- psychopy/app/Resources/light/redo32.png +0 -0
- psychopy/app/Resources/light/redo32@2x.png +0 -0
- psychopy/app/Resources/light/redo@2x.png +0 -0
- psychopy/app/Resources/light/removeExp32.png +0 -0
- psychopy/app/Resources/light/removeExp32@2x.png +0 -0
- psychopy/app/Resources/light/rename16.png +0 -0
- psychopy/app/Resources/light/rename16@2x.png +0 -0
- psychopy/app/Resources/light/restart.png +0 -0
- psychopy/app/Resources/light/restart@2x.png +0 -0
- psychopy/app/Resources/light/runner.png +0 -0
- psychopy/app/Resources/light/runner@2x.png +0 -0
- psychopy/app/Resources/light/runnerPilot.png +0 -0
- psychopy/app/Resources/light/runnerPilot@2x.png +0 -0
- psychopy/app/Resources/light/savebtn16.png +0 -0
- psychopy/app/Resources/light/savebtn16@2x.png +0 -0
- psychopy/app/Resources/light/search.png +0 -0
- psychopy/app/Resources/light/search@2x.png +0 -0
- psychopy/app/Resources/light/showBuilder.png +0 -0
- psychopy/app/Resources/light/showBuilder@2x.png +0 -0
- psychopy/app/Resources/light/showCoder.png +0 -0
- psychopy/app/Resources/light/showCoder@2x.png +0 -0
- psychopy/app/Resources/light/showRunner.png +0 -0
- psychopy/app/Resources/light/showRunner@2x.png +0 -0
- psychopy/app/Resources/light/starred.png +0 -0
- psychopy/app/Resources/light/starred@2x.png +0 -0
- psychopy/app/Resources/light/start.png +0 -0
- psychopy/app/Resources/light/start@2x.png +0 -0
- psychopy/app/Resources/light/stdout.png +0 -0
- psychopy/app/Resources/light/stdout@2x.png +0 -0
- psychopy/app/Resources/light/stop.png +0 -0
- psychopy/app/Resources/light/stop32.png +0 -0
- psychopy/app/Resources/light/stop32@2x.png +0 -0
- psychopy/app/Resources/light/stop@2x.png +0 -0
- psychopy/app/Resources/light/switchCtrlBot.png +0 -0
- psychopy/app/Resources/light/switchCtrlBot@2x.png +0 -0
- psychopy/app/Resources/light/switchCtrlLeft.png +0 -0
- psychopy/app/Resources/light/switchCtrlLeft@2x.png +0 -0
- psychopy/app/Resources/light/switchCtrlRight.png +0 -0
- psychopy/app/Resources/light/switchCtrlRight@2x.png +0 -0
- psychopy/app/Resources/light/switchCtrlTop.png +0 -0
- psychopy/app/Resources/light/switchCtrlTop@2x.png +0 -0
- psychopy/app/Resources/light/tick.png +0 -0
- psychopy/app/Resources/light/tick@2x.png +0 -0
- psychopy/app/Resources/light/undo.png +0 -0
- psychopy/app/Resources/light/undo32.png +0 -0
- psychopy/app/Resources/light/undo32@2x.png +0 -0
- psychopy/app/Resources/light/undo@2x.png +0 -0
- psychopy/app/Resources/light/unstarred.png +0 -0
- psychopy/app/Resources/light/unstarred@2x.png +0 -0
- psychopy/app/Resources/light/user_none.png +0 -0
- psychopy/app/Resources/light/view-refresh16.png +0 -0
- psychopy/app/Resources/light/view-refresh16@2x.png +0 -0
- psychopy/app/Resources/light/viewbtn16.png +0 -0
- psychopy/app/Resources/light/viewbtn16@2x.png +0 -0
- psychopy/app/Resources/light/windows16.png +0 -0
- psychopy/app/Resources/light/windows16@2x.png +0 -0
- psychopy/app/Resources/moveComponentIcons.py +50 -0
- psychopy/app/Resources/next.png +0 -0
- psychopy/app/Resources/psychopy.desktop +13 -0
- psychopy/app/Resources/psychopy.icns +0 -0
- psychopy/app/Resources/psychopy.ico +0 -0
- psychopy/app/Resources/psychopy.png +0 -0
- psychopy/app/Resources/psychopy.xml +9 -0
- psychopy/app/Resources/psychopy@2x.png +0 -0
- psychopy/app/Resources/psychopySplash.png +0 -0
- psychopy/app/Resources/psychopySplash@2x.png +0 -0
- psychopy/app/Resources/routine_templates/Basic.psyexp +345 -0
- psychopy/app/Resources/routine_templates/Misc.psyexp +229 -0
- psychopy/app/Resources/routine_templates/Online.psyexp +202 -0
- psychopy/app/Resources/routine_templates/Trials.psyexp +736 -0
- psychopy/app/Resources/routine_templates/readme.md +14 -0
- psychopy/app/Resources/runner.ico +0 -0
- psychopy/app/Resources/tips.txt +45 -0
- psychopy/app/Resources/tips_ar_001.txt +45 -0
- psychopy/app/Resources/tips_fr_FR.txt +45 -0
- psychopy/app/Resources/tips_ja_JP.txt +42 -0
- psychopy/app/Resources/tips_zh_CN.txt +45 -0
- psychopy/app/Resources/window.ico +0 -0
- psychopy/app/__init__.py +224 -0
- psychopy/app/_psychopyApp.py +1224 -0
- psychopy/app/appData.spec +58 -0
- psychopy/app/builder/__init__.py +5 -0
- psychopy/app/builder/builder.py +4658 -0
- psychopy/app/builder/dialogs/__init__.py +2014 -0
- psychopy/app/builder/dialogs/dlgsCode.py +610 -0
- psychopy/app/builder/dialogs/dlgsConditions.py +670 -0
- psychopy/app/builder/dialogs/findDlg.py +189 -0
- psychopy/app/builder/dialogs/paramCtrls.py +1213 -0
- psychopy/app/builder/localizedStrings.py +225 -0
- psychopy/app/builder/validators.py +581 -0
- psychopy/app/coder/__init__.py +8 -0
- psychopy/app/coder/codeEditorBase.py +460 -0
- psychopy/app/coder/coder.py +3086 -0
- psychopy/app/coder/fileBrowser.py +668 -0
- psychopy/app/coder/folding.py +128 -0
- psychopy/app/coder/psychoParser.py +139 -0
- psychopy/app/coder/repl.py +493 -0
- psychopy/app/coder/sourceTree.py +302 -0
- psychopy/app/colorpicker/__init__.py +594 -0
- psychopy/app/colorpicker/ui.py +336 -0
- psychopy/app/connections/__init__.py +10 -0
- psychopy/app/connections/news.py +104 -0
- psychopy/app/connections/sendusage.py +61 -0
- psychopy/app/connections/updates.py +642 -0
- psychopy/app/console.py +164 -0
- psychopy/app/dialogs.py +632 -0
- psychopy/app/errorDlg.py +238 -0
- psychopy/app/frametracker.py +8 -0
- psychopy/app/idle.py +143 -0
- psychopy/app/jobs.py +648 -0
- psychopy/app/linuxconfig/__init__.py +144 -0
- psychopy/app/linuxconfig/ui.py +88 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +9554 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +9712 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.po +18 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +9100 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +9094 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.po +9100 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +9569 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +5806 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +9577 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +9549 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +9559 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +9560 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +11687 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +8757 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +25 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +24 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +8747 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +9545 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +8268 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +8022 -0
- psychopy/app/pavlovia_ui/__init__.py +22 -0
- psychopy/app/pavlovia_ui/_base.py +237 -0
- psychopy/app/pavlovia_ui/functions.py +176 -0
- psychopy/app/pavlovia_ui/menu.py +140 -0
- psychopy/app/pavlovia_ui/project.py +943 -0
- psychopy/app/pavlovia_ui/search.py +445 -0
- psychopy/app/pavlovia_ui/sync.py +137 -0
- psychopy/app/pavlovia_ui/user.py +264 -0
- psychopy/app/plugin_manager/__init__.py +5 -0
- psychopy/app/plugin_manager/dialog.py +400 -0
- psychopy/app/plugin_manager/output.py +132 -0
- psychopy/app/plugin_manager/packages.py +502 -0
- psychopy/app/plugin_manager/plugins.py +1315 -0
- psychopy/app/plugin_manager/utils.py +115 -0
- psychopy/app/preferencesDlg.py +790 -0
- psychopy/app/psychopyApp.py +103 -0
- psychopy/app/ribbon.py +907 -0
- psychopy/app/runner/__init__.py +1 -0
- psychopy/app/runner/runner.py +1262 -0
- psychopy/app/runner/scriptProcess.py +353 -0
- psychopy/app/stdout/__init__.py +1 -0
- psychopy/app/stdout/stdOutRich.py +380 -0
- psychopy/app/sysInfoDlg.py +242 -0
- psychopy/app/themes/__init__.py +78 -0
- psychopy/app/themes/colors.py +201 -0
- psychopy/app/themes/css/contrast_black.css +112 -0
- psychopy/app/themes/css/contrast_white.css +115 -0
- psychopy/app/themes/css/dark.css +112 -0
- psychopy/app/themes/css/light.css +115 -0
- psychopy/app/themes/fonts.py +629 -0
- psychopy/app/themes/handlers.py +321 -0
- psychopy/app/themes/icons.py +250 -0
- psychopy/app/themes/spec/Classic.json +154 -0
- psychopy/app/themes/spec/ClassicDark.json +154 -0
- psychopy/app/themes/spec/GitHub.json +154 -0
- psychopy/app/themes/spec/HiVisDark.json +153 -0
- psychopy/app/themes/spec/HiVisLight.json +154 -0
- psychopy/app/themes/spec/MinimalDark.json +154 -0
- psychopy/app/themes/spec/MinimalLight.json +153 -0
- psychopy/app/themes/spec/PsychopyDark.json +231 -0
- psychopy/app/themes/spec/PsychopyLight.json +236 -0
- psychopy/app/themes/ui.py +74 -0
- psychopy/app/ui/__init__.py +184 -0
- psychopy/app/urls.py +31 -0
- psychopy/app/utils.py +1680 -0
- psychopy/app/viewer/__init__.py +0 -0
- psychopy/clock.py +612 -0
- psychopy/colors.py +1050 -0
- psychopy/compatibility.py +95 -0
- psychopy/constants.py +87 -0
- psychopy/contrib/__init__.py +0 -0
- psychopy/contrib/configobj/LICENSE +39 -0
- psychopy/contrib/configobj/__init__.py +2453 -0
- psychopy/contrib/configobj/_version.py +2 -0
- psychopy/contrib/configobj/validate.py +1458 -0
- psychopy/contrib/lazy_import.py +402 -0
- psychopy/contrib/mseq.py +273 -0
- psychopy/contrib/mseqSearch.py +183 -0
- psychopy/contrib/psi.py +100 -0
- psychopy/contrib/quest.py +484 -0
- psychopy/contrib/tesselate.py +188 -0
- psychopy/core.py +167 -0
- psychopy/data/__init__.py +44 -0
- psychopy/data/base.py +566 -0
- psychopy/data/counterbalance.py +210 -0
- psychopy/data/experiment.py +708 -0
- psychopy/data/fit.py +248 -0
- psychopy/data/shelf.py +237 -0
- psychopy/data/staircase.py +2247 -0
- psychopy/data/trial.py +1978 -0
- psychopy/data/utils.py +783 -0
- psychopy/demos/__init__.py +0 -0
- psychopy/demos/builder/Design Templates/branchedExperiment/README.md +5 -0
- psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +239 -0
- psychopy/demos/builder/Design Templates/branchedExperiment/trialTypes.xlsx +0 -0
- psychopy/demos/builder/Design Templates/psychophysicsStaircase/README.md +20 -0
- psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +264 -0
- psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/README.md +14 -0
- psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +201 -0
- psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/stairDefinitions.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/README.md +18 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/chooseBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/facesBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/housesBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +131 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face01.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face02.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face03.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house01.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house02.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house03.jpg +0 -0
- psychopy/demos/builder/Experiments/BART/README.md +30 -0
- psychopy/demos/builder/Experiments/BART/assets/background.png +0 -0
- psychopy/demos/builder/Experiments/BART/assets/bang.mp3 +0 -0
- psychopy/demos/builder/Experiments/BART/assets/bang.wav +0 -0
- psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
- psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
- psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
- psychopy/demos/builder/Experiments/BART/bart.psyexp +931 -0
- psychopy/demos/builder/Experiments/BART/spreadsheets/conditions.xlsx +0 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +193 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/README.md +23 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/TIPI.xlsx +0 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/bigFiveItems.xlsx +0 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/demographics.xlsx +0 -0
- psychopy/demos/builder/Experiments/BigFiveInventory/mini_IPIP.xlsx +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/README.md +40 -0
- psychopy/demos/builder/Experiments/dragAndDrop/archived_conditions.xlsx +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +1221 -0
- psychopy/demos/builder/Experiments/dragAndDrop/draw grid stim.py +61 -0
- psychopy/demos/builder/Experiments/dragAndDrop/shapeMaker.psyexp +91 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_1.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_10.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_2.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_3.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_4.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_5.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_6.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_7.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_8.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_9.png +0 -0
- psychopy/demos/builder/Experiments/dragAndDrop/updated_conditions.xlsx +0 -0
- psychopy/demos/builder/Experiments/goNoGo/conditions.xlsx +0 -0
- psychopy/demos/builder/Experiments/goNoGo/gng.psyexp +449 -0
- psychopy/demos/builder/Experiments/goNoGo/go.png +0 -0
- psychopy/demos/builder/Experiments/goNoGo/nogo.png +0 -0
- psychopy/demos/builder/Experiments/goNoGo/readme.md +14 -0
- psychopy/demos/builder/Experiments/mentalRotation/F.png +0 -0
- psychopy/demos/builder/Experiments/mentalRotation/FR.png +0 -0
- psychopy/demos/builder/Experiments/mentalRotation/MentalRot.csv +33 -0
- psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +605 -0
- psychopy/demos/builder/Experiments/mentalRotation/README.md +23 -0
- psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +478 -0
- psychopy/demos/builder/Experiments/navon/README.md +26 -0
- psychopy/demos/builder/Experiments/navon/bigHsmallH.png +0 -0
- psychopy/demos/builder/Experiments/navon/bigHsmallS.png +0 -0
- psychopy/demos/builder/Experiments/navon/bigSsmallH.png +0 -0
- psychopy/demos/builder/Experiments/navon/bigSsmallS.png +0 -0
- psychopy/demos/builder/Experiments/navon/mask.png +0 -0
- psychopy/demos/builder/Experiments/navon/stimuli.pptx +0 -0
- psychopy/demos/builder/Experiments/navon/trialTypes.xlsx +0 -0
- psychopy/demos/builder/Experiments/sternberg/README.md +14 -0
- psychopy/demos/builder/Experiments/sternberg/mainTrials.xlsx +0 -0
- psychopy/demos/builder/Experiments/sternberg/pracTrials.xlsx +0 -0
- psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +611 -0
- psychopy/demos/builder/Experiments/stroop/README.md +15 -0
- psychopy/demos/builder/Experiments/stroop/stroop.psyexp +225 -0
- psychopy/demos/builder/Experiments/stroop/trialTypes.csv +7 -0
- psychopy/demos/builder/Experiments/stroopExtended/README.md +14 -0
- psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +243 -0
- psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +243 -0
- psychopy/demos/builder/Experiments/stroopExtended/trialTypes.xlsx +0 -0
- psychopy/demos/builder/Experiments/stroopExtended/trialTypesReverse.xlsx +0 -0
- psychopy/demos/builder/Experiments/stroopVoice/README.md +27 -0
- psychopy/demos/builder/Experiments/stroopVoice/conditions.xlsx +0 -0
- psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +366 -0
- psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +298 -0
- psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +341 -0
- psychopy/demos/builder/Feature Demos/gratings/readme.md +8 -0
- psychopy/demos/builder/Feature Demos/noise/face.jpg +0 -0
- psychopy/demos/builder/Feature Demos/noise/noise.psyexp +424 -0
- psychopy/demos/builder/Feature Demos/noise/readme.md +3 -0
- psychopy/demos/builder/Feature Demos/panorama/panImg.jpg +0 -0
- psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +142 -0
- psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +423 -0
- psychopy/demos/builder/Feature Demos/progress/readme.md +1 -0
- psychopy/demos/builder/Feature Demos/sliders/README.md +8 -0
- psychopy/demos/builder/Feature Demos/sliders/fruitConditions.xlsx +0 -0
- psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +938 -0
- psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +621 -0
- psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +404 -0
- psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +641 -0
- psychopy/demos/builder/Hardware/EGI_netstation/README.md +17 -0
- psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +254 -0
- psychopy/demos/builder/Hardware/EGI_netstation/trialTypesEEG.csv +7 -0
- psychopy/demos/builder/Hardware/Eyetracking_visual_search/readme.md +19 -0
- psychopy/demos/builder/Hardware/Eyetracking_visual_search/trials_params.xlsx +0 -0
- psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +691 -0
- psychopy/demos/builder/Hardware/camera/camera.psyexp +263 -0
- psychopy/demos/builder/Hardware/camera/readme.md +6 -0
- psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +354 -0
- psychopy/demos/builder/Hardware/eyetracking/readme.md +7 -0
- psychopy/demos/builder/Hardware/eyetracking_custom_cal/Bullseye_grey.mov +0 -0
- psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +349 -0
- psychopy/demos/builder/Hardware/eyetracking_custom_cal/readme.md +7 -0
- psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +202 -0
- psychopy/demos/builder/Hardware/fMRI/readme.md +7 -0
- psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +349 -0
- psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +304 -0
- psychopy/demos/builder/Hardware/microphone/microphone.psyexp +451 -0
- psychopy/demos/builder/Hardware/microphone/phrases.xlsx +0 -0
- psychopy/demos/builder/Hardware/microphone/readme.md +9 -0
- psychopy/demos/builder/Hardware/pump/README.md +19 -0
- psychopy/demos/builder/Hardware/pump/pump.psyexp +382 -0
- psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +288 -0
- psychopy/demos/builder/Helper Tools/achorVSalignment/readme.md +5 -0
- psychopy/demos/builder/Helper Tools/clockFace/README.md +3 -0
- psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +160 -0
- psychopy/demos/builder/Helper Tools/colors/README.md +5 -0
- psychopy/demos/builder/Helper Tools/colors/colors.psyexp +300 -0
- psychopy/demos/builder/Helper Tools/drawPolygon/README.md +5 -0
- psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +591 -0
- psychopy/demos/builder/Helper Tools/keyNameFinder/README.md +14 -0
- psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +169 -0
- psychopy/demos/builder/Helper Tools/spatialUnits/README.md +6 -0
- psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +157 -0
- psychopy/demos/builder/README.txt +43 -0
- psychopy/demos/builder/__init__.py +0 -0
- psychopy/demos/coder/__init__.py +0 -0
- psychopy/demos/coder/basic/hello_world.py +37 -0
- psychopy/demos/coder/csvFromPsydat.py +29 -0
- psychopy/demos/coder/experiment control/JND_staircase_analysis.py +85 -0
- psychopy/demos/coder/experiment control/JND_staircase_exp.py +110 -0
- psychopy/demos/coder/experiment control/TrialHandler.py +64 -0
- psychopy/demos/coder/experiment control/TrialHandler2.py +42 -0
- psychopy/demos/coder/experiment control/__init__.py +0 -0
- psychopy/demos/coder/experiment control/autoDraw_autoLog.py +50 -0
- psychopy/demos/coder/experiment control/experimentHandler.py +57 -0
- psychopy/demos/coder/experiment control/fMRI_launchScan.py +74 -0
- psychopy/demos/coder/experiment control/gammaMotionAnalysis.py +68 -0
- psychopy/demos/coder/experiment control/gammaMotionNull.py +169 -0
- psychopy/demos/coder/experiment control/logFiles.py +55 -0
- psychopy/demos/coder/experiment control/piloting.py +65 -0
- psychopy/demos/coder/experiment control/runtimeInfo.py +79 -0
- psychopy/demos/coder/hardware/CRS_BitsBox.py +75 -0
- psychopy/demos/coder/hardware/CRS_BitsPlusPlus.py +73 -0
- psychopy/demos/coder/hardware/RiftHeadTrackingExample.py +95 -0
- psychopy/demos/coder/hardware/RiftMinimal.py +44 -0
- psychopy/demos/coder/hardware/VSHD_Distortion.py +49 -0
- psychopy/demos/coder/hardware/__init__.py +0 -0
- psychopy/demos/coder/hardware/camera.py +90 -0
- psychopy/demos/coder/hardware/cedrusRB730.py +38 -0
- psychopy/demos/coder/hardware/crsBitsAdvancedDemo.py +780 -0
- psychopy/demos/coder/hardware/egi_netstation.py +45 -0
- psychopy/demos/coder/hardware/ioLab_bbox.py +62 -0
- psychopy/demos/coder/hardware/labjack_u3.py +61 -0
- psychopy/demos/coder/hardware/monitorDemo.py +28 -0
- psychopy/demos/coder/hardware/parallelPortOutput.py +49 -0
- psychopy/demos/coder/hardware/qmixPump.py +79 -0
- psychopy/demos/coder/hardware/testSoundLatency.py +127 -0
- psychopy/demos/coder/input/GUI.py +52 -0
- psychopy/demos/coder/input/__init__.py +0 -0
- psychopy/demos/coder/input/customMouse.py +51 -0
- psychopy/demos/coder/input/joystick_universal.py +84 -0
- psychopy/demos/coder/input/keyNameFinder.py +39 -0
- psychopy/demos/coder/input/mic.png +0 -0
- psychopy/demos/coder/input/mouse.py +66 -0
- psychopy/demos/coder/iohub/delaytest.py +231 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/images/canal.jpg +0 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/images/fall.jpg +0 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/images/lake.jpg +0 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/images/party.jpg +0 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/images/swimming.jpg +0 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByConditionVariables.py +67 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByMessages.py +34 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/run.py +323 -0
- psychopy/demos/coder/iohub/eyetracking/gcCursor/trial_conditions.xlsx +0 -0
- psychopy/demos/coder/iohub/eyetracking/simple.py +147 -0
- psychopy/demos/coder/iohub/eyetracking/validation.py +251 -0
- psychopy/demos/coder/iohub/iodatastore/saveEventReport.py +56 -0
- psychopy/demos/coder/iohub/keyboard.py +189 -0
- psychopy/demos/coder/iohub/keyboardreactiontime.py +88 -0
- psychopy/demos/coder/iohub/launchHub.py +160 -0
- psychopy/demos/coder/iohub/mouse.py +112 -0
- psychopy/demos/coder/iohub/mouse_multi_window.py +124 -0
- psychopy/demos/coder/iohub/serial/_parseserial.py +53 -0
- psychopy/demos/coder/iohub/serial/customparser.py +84 -0
- psychopy/demos/coder/iohub/serial/pstbox.py +166 -0
- psychopy/demos/coder/iohub/wintab/_wintabgraphics.py +281 -0
- psychopy/demos/coder/iohub/wintab/pen_demo.py +260 -0
- psychopy/demos/coder/misc/encrypt_data.py +48 -0
- psychopy/demos/coder/misc/makeMovie.py +28 -0
- psychopy/demos/coder/misc/rigidBodyTransform.py +76 -0
- psychopy/demos/coder/stimuli/Campaign.ttf +0 -0
- psychopy/demos/coder/stimuli/MovieStim.py +65 -0
- psychopy/demos/coder/stimuli/__init__.py +0 -0
- psychopy/demos/coder/stimuli/aperture.py +37 -0
- psychopy/demos/coder/stimuli/beach.jpg +0 -0
- psychopy/demos/coder/stimuli/bufferImageStim.py +78 -0
- psychopy/demos/coder/stimuli/clockface.py +43 -0
- psychopy/demos/coder/stimuli/colorPalette.py +134 -0
- psychopy/demos/coder/stimuli/compare_text_timing.py +193 -0
- psychopy/demos/coder/stimuli/counterphase.py +43 -0
- psychopy/demos/coder/stimuli/customTextures.py +57 -0
- psychopy/demos/coder/stimuli/dot_gabors.py +35 -0
- psychopy/demos/coder/stimuli/dots.py +35 -0
- psychopy/demos/coder/stimuli/elementArrays.py +92 -0
- psychopy/demos/coder/stimuli/embeddedOpenGL.py +35 -0
- psychopy/demos/coder/stimuli/face.jpg +0 -0
- psychopy/demos/coder/stimuli/face_jpg.py +48 -0
- psychopy/demos/coder/stimuli/gabor.py +30 -0
- psychopy/demos/coder/stimuli/imagesAndPatches.py +51 -0
- psychopy/demos/coder/stimuli/jwpIntro.mp4 +0 -0
- psychopy/demos/coder/stimuli/kanizsa.py +41 -0
- psychopy/demos/coder/stimuli/maskReveal.py +63 -0
- psychopy/demos/coder/stimuli/plaid.py +43 -0
- psychopy/demos/coder/stimuli/ratingScale.py +183 -0
- psychopy/demos/coder/stimuli/rotatingFlashingWedge.py +37 -0
- psychopy/demos/coder/stimuli/screensAndWindows.py +58 -0
- psychopy/demos/coder/stimuli/secondOrderGratings.py +66 -0
- psychopy/demos/coder/stimuli/shapeContains.py +50 -0
- psychopy/demos/coder/stimuli/shapes.py +63 -0
- psychopy/demos/coder/stimuli/soundStimuli.py +49 -0
- psychopy/demos/coder/stimuli/starField.py +48 -0
- psychopy/demos/coder/stimuli/stim3d.py +107 -0
- psychopy/demos/coder/stimuli/textBoxStim/textbox_glyph_placement.py +93 -0
- psychopy/demos/coder/stimuli/textBoxStim/textbox_simple.py +71 -0
- psychopy/demos/coder/stimuli/textStimuli.py +106 -0
- psychopy/demos/coder/stimuli/textbox_editable.py +64 -0
- psychopy/demos/coder/stimuli/variousVisualStims.py +47 -0
- psychopy/demos/coder/stimuli/visual_noise.py +31 -0
- psychopy/demos/coder/sysInfo.py +56 -0
- psychopy/demos/coder/timing/__init__.py +0 -0
- psychopy/demos/coder/timing/callOnFlip.py +39 -0
- psychopy/demos/coder/timing/clocksAndTimers.py +46 -0
- psychopy/demos/coder/timing/millikeyKeyboardTimingTest.py +323 -0
- psychopy/demos/coder/timing/timeByFrames.py +74 -0
- psychopy/demos/coder/timing/timeByFramesEx.py +107 -0
- psychopy/demos/coder/understanding psychopy/colors.py +70 -0
- psychopy/demos/coder/understanding psychopy/fontLayout.py +69 -0
- psychopy/demos/demo_migration.py +214 -0
- psychopy/demos/modernizeDemos.py +16 -0
- psychopy/demos/test_demo_migration.py +155 -0
- psychopy/devices/__init__.py +8 -0
- psychopy/event.py +1333 -0
- psychopy/exceptions.py +59 -0
- psychopy/experiment/__init__.py +51 -0
- psychopy/experiment/_experiment.py +1392 -0
- psychopy/experiment/blankTemplate.xltx +0 -0
- psychopy/experiment/components/__init__.py +411 -0
- psychopy/experiment/components/_base.py +1351 -0
- psychopy/experiment/components/aperture/__init__.py +149 -0
- psychopy/experiment/components/aperture/classic/aperture.png +0 -0
- psychopy/experiment/components/aperture/classic/aperture@2x.png +0 -0
- psychopy/experiment/components/aperture/dark/aperture.png +0 -0
- psychopy/experiment/components/aperture/dark/aperture@2x.png +0 -0
- psychopy/experiment/components/aperture/light/aperture.png +0 -0
- psychopy/experiment/components/aperture/light/aperture@2x.png +0 -0
- psychopy/experiment/components/brush/__init__.py +163 -0
- psychopy/experiment/components/brush/classic/brush.png +0 -0
- psychopy/experiment/components/brush/classic/brush@2x.png +0 -0
- psychopy/experiment/components/brush/dark/brush.png +0 -0
- psychopy/experiment/components/brush/dark/brush@2x.png +0 -0
- psychopy/experiment/components/brush/light/brush.png +0 -0
- psychopy/experiment/components/brush/light/brush@2x.png +0 -0
- psychopy/experiment/components/button/__init__.py +489 -0
- psychopy/experiment/components/button/classic/button.png +0 -0
- psychopy/experiment/components/button/classic/button@2x.png +0 -0
- psychopy/experiment/components/button/dark/button.png +0 -0
- psychopy/experiment/components/button/dark/button@2x.png +0 -0
- psychopy/experiment/components/button/light/button.png +0 -0
- psychopy/experiment/components/button/light/button@2x.png +0 -0
- psychopy/experiment/components/buttonBox/__init__.py +316 -0
- psychopy/experiment/components/buttonBox/classic/buttonBox.png +0 -0
- psychopy/experiment/components/buttonBox/classic/buttonBox@2x.png +0 -0
- psychopy/experiment/components/buttonBox/dark/buttonBox.png +0 -0
- psychopy/experiment/components/buttonBox/dark/buttonBox@2x.png +0 -0
- psychopy/experiment/components/buttonBox/light/buttonBox.png +0 -0
- psychopy/experiment/components/buttonBox/light/buttonBox@2x.png +0 -0
- psychopy/experiment/components/camera/__init__.py +591 -0
- psychopy/experiment/components/camera/classic/webcam.png +0 -0
- psychopy/experiment/components/camera/classic/webcam@2x.png +0 -0
- psychopy/experiment/components/camera/dark/webcam.png +0 -0
- psychopy/experiment/components/camera/dark/webcam@2x.png +0 -0
- psychopy/experiment/components/camera/light/webcam.png +0 -0
- psychopy/experiment/components/camera/light/webcam@2x.png +0 -0
- psychopy/experiment/components/code/__init__.py +266 -0
- psychopy/experiment/components/code/classic/code.png +0 -0
- psychopy/experiment/components/code/classic/code@2x.png +0 -0
- psychopy/experiment/components/code/dark/code.png +0 -0
- psychopy/experiment/components/code/dark/code@2x.png +0 -0
- psychopy/experiment/components/code/light/code.png +0 -0
- psychopy/experiment/components/code/light/code@2x.png +0 -0
- psychopy/experiment/components/dots/__init__.py +199 -0
- psychopy/experiment/components/dots/classic/dots.png +0 -0
- psychopy/experiment/components/dots/classic/dots@2x.png +0 -0
- psychopy/experiment/components/dots/dark/dots.png +0 -0
- psychopy/experiment/components/dots/dark/dots@2x.png +0 -0
- psychopy/experiment/components/dots/dots.xcf +0 -0
- psychopy/experiment/components/dots/light/dots.png +0 -0
- psychopy/experiment/components/dots/light/dots@2x.png +0 -0
- psychopy/experiment/components/eyetracker_record/__init__.py +128 -0
- psychopy/experiment/components/eyetracker_record/classic/eyetracker_record.png +0 -0
- psychopy/experiment/components/eyetracker_record/classic/eyetracker_record@2x.png +0 -0
- psychopy/experiment/components/eyetracker_record/dark/eyetracker_record.png +0 -0
- psychopy/experiment/components/eyetracker_record/dark/eyetracker_record@2x.png +0 -0
- psychopy/experiment/components/eyetracker_record/light/eyetracker_record.png +0 -0
- psychopy/experiment/components/eyetracker_record/light/eyetracker_record@2x.png +0 -0
- psychopy/experiment/components/form/__init__.py +223 -0
- psychopy/experiment/components/form/classic/form.png +0 -0
- psychopy/experiment/components/form/classic/form@2x.png +0 -0
- psychopy/experiment/components/form/dark/form.png +0 -0
- psychopy/experiment/components/form/dark/form@2x.png +0 -0
- psychopy/experiment/components/form/formItems.xltx +0 -0
- psychopy/experiment/components/form/light/form.png +0 -0
- psychopy/experiment/components/form/light/form@2x.png +0 -0
- psychopy/experiment/components/grating/__init__.py +192 -0
- psychopy/experiment/components/grating/classic/grating.png +0 -0
- psychopy/experiment/components/grating/classic/grating@2x.png +0 -0
- psychopy/experiment/components/grating/dark/grating.png +0 -0
- psychopy/experiment/components/grating/dark/grating@2x.png +0 -0
- psychopy/experiment/components/grating/light/grating.png +0 -0
- psychopy/experiment/components/grating/light/grating@2x.png +0 -0
- psychopy/experiment/components/image/__init__.py +184 -0
- psychopy/experiment/components/image/classic/image.png +0 -0
- psychopy/experiment/components/image/classic/image@2x.png +0 -0
- psychopy/experiment/components/image/dark/image.png +0 -0
- psychopy/experiment/components/image/dark/image@2x.png +0 -0
- psychopy/experiment/components/image/light/image.png +0 -0
- psychopy/experiment/components/image/light/image@2x.png +0 -0
- psychopy/experiment/components/joyButtons/__init__.py +453 -0
- psychopy/experiment/components/joyButtons/classic/joyButtons.png +0 -0
- psychopy/experiment/components/joyButtons/classic/joybuttons@2x.png +0 -0
- psychopy/experiment/components/joyButtons/dark/joyButtons.png +0 -0
- psychopy/experiment/components/joyButtons/dark/joyButtons@2x.png +0 -0
- psychopy/experiment/components/joyButtons/light/joyButtons.png +0 -0
- psychopy/experiment/components/joyButtons/light/joyButtons@2x.png +0 -0
- psychopy/experiment/components/joyButtons/virtualJoyButtons.py +34 -0
- psychopy/experiment/components/joystick/__init__.py +572 -0
- psychopy/experiment/components/joystick/classic/joystick.png +0 -0
- psychopy/experiment/components/joystick/classic/joystick@2x.png +0 -0
- psychopy/experiment/components/joystick/dark/joystick.png +0 -0
- psychopy/experiment/components/joystick/dark/joystick@2x.png +0 -0
- psychopy/experiment/components/joystick/light/joystick.png +0 -0
- psychopy/experiment/components/joystick/light/joystick@2x.png +0 -0
- psychopy/experiment/components/joystick/virtualJoystick.py +40 -0
- psychopy/experiment/components/keyboard/__init__.py +587 -0
- psychopy/experiment/components/keyboard/classic/keyboard.png +0 -0
- psychopy/experiment/components/keyboard/classic/keyboard@2x.png +0 -0
- psychopy/experiment/components/keyboard/dark/keyboard.png +0 -0
- psychopy/experiment/components/keyboard/dark/keyboard@2x.png +0 -0
- psychopy/experiment/components/keyboard/light/keyboard.png +0 -0
- psychopy/experiment/components/keyboard/light/keyboard@2x.png +0 -0
- psychopy/experiment/components/keyboard.xcf +0 -0
- psychopy/experiment/components/microphone/__init__.py +664 -0
- psychopy/experiment/components/microphone/classic/microphone.png +0 -0
- psychopy/experiment/components/microphone/classic/microphone@2x.png +0 -0
- psychopy/experiment/components/microphone/dark/microphone.png +0 -0
- psychopy/experiment/components/microphone/dark/microphone@2x.png +0 -0
- psychopy/experiment/components/microphone/light/microphone.png +0 -0
- psychopy/experiment/components/microphone/light/microphone@2x.png +0 -0
- psychopy/experiment/components/mouse/__init__.py +747 -0
- psychopy/experiment/components/mouse/classic/mouse.png +0 -0
- psychopy/experiment/components/mouse/classic/mouse@2x.png +0 -0
- psychopy/experiment/components/mouse/dark/mouse.png +0 -0
- psychopy/experiment/components/mouse/dark/mouse@2x.png +0 -0
- psychopy/experiment/components/mouse/light/mouse.png +0 -0
- psychopy/experiment/components/mouse/light/mouse@2x.png +0 -0
- psychopy/experiment/components/movie/__init__.py +367 -0
- psychopy/experiment/components/movie/classic/movie.png +0 -0
- psychopy/experiment/components/movie/classic/movie@2x.png +0 -0
- psychopy/experiment/components/movie/dark/movie.png +0 -0
- psychopy/experiment/components/movie/dark/movie@2x.png +0 -0
- psychopy/experiment/components/movie/light/movie.png +0 -0
- psychopy/experiment/components/movie/light/movie@2x.png +0 -0
- psychopy/experiment/components/panorama/__init__.py +456 -0
- psychopy/experiment/components/panorama/classic/panorama.png +0 -0
- psychopy/experiment/components/panorama/classic/panorama@2x.png +0 -0
- psychopy/experiment/components/panorama/dark/panorama.png +0 -0
- psychopy/experiment/components/panorama/dark/panorama@2x.png +0 -0
- psychopy/experiment/components/panorama/light/panorama.png +0 -0
- psychopy/experiment/components/panorama/light/panorama@2x.png +0 -0
- psychopy/experiment/components/parallelOut/__init__.py +178 -0
- psychopy/experiment/components/parallelOut/classic/parallel.png +0 -0
- psychopy/experiment/components/parallelOut/classic/parallel@2x.png +0 -0
- psychopy/experiment/components/parallelOut/dark/parallel.png +0 -0
- psychopy/experiment/components/parallelOut/dark/parallel@2x.png +0 -0
- psychopy/experiment/components/parallelOut/light/parallel.png +0 -0
- psychopy/experiment/components/parallelOut/light/parallel@2x.png +0 -0
- psychopy/experiment/components/patch/__init__.py +121 -0
- 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/polygon/__init__.py +303 -0
- psychopy/experiment/components/polygon/classic/polygon.png +0 -0
- psychopy/experiment/components/polygon/classic/polygon@2x.png +0 -0
- psychopy/experiment/components/polygon/dark/polygon.png +0 -0
- psychopy/experiment/components/polygon/dark/polygon@2x.png +0 -0
- psychopy/experiment/components/polygon/light/polygon.png +0 -0
- psychopy/experiment/components/polygon/light/polygon@2x.png +0 -0
- psychopy/experiment/components/progress/__init__.py +130 -0
- psychopy/experiment/components/progress/classic/progress.png +0 -0
- psychopy/experiment/components/progress/classic/progress@2x.png +0 -0
- psychopy/experiment/components/progress/dark/progress.png +0 -0
- psychopy/experiment/components/progress/dark/progress@2x.png +0 -0
- psychopy/experiment/components/progress/light/progress.png +0 -0
- psychopy/experiment/components/progress/light/progress@2x.png +0 -0
- psychopy/experiment/components/ratingScale/__init__.py +337 -0
- 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/experiment/components/resourceManager/__init__.py +176 -0
- psychopy/experiment/components/resourceManager/classic/resource_manager.png +0 -0
- psychopy/experiment/components/resourceManager/classic/resource_manager@2x.png +0 -0
- psychopy/experiment/components/resourceManager/dark/resource_manager.png +0 -0
- psychopy/experiment/components/resourceManager/dark/resource_manager@2x.png +0 -0
- psychopy/experiment/components/resourceManager/light/resource_manager.png +0 -0
- psychopy/experiment/components/resourceManager/light/resource_manager@2x.png +0 -0
- psychopy/experiment/components/roi/__init__.py +311 -0
- psychopy/experiment/components/roi/classic/eyetracker_roi.png +0 -0
- psychopy/experiment/components/roi/classic/eyetracker_roi@2x.png +0 -0
- psychopy/experiment/components/roi/dark/eyetracker_roi.png +0 -0
- psychopy/experiment/components/roi/dark/eyetracker_roi@2X.png +0 -0
- psychopy/experiment/components/roi/light/eyetracker_roi.png +0 -0
- psychopy/experiment/components/roi/light/eyetracker_roi@2X.png +0 -0
- psychopy/experiment/components/routineSettings/__init__.py +338 -0
- psychopy/experiment/components/routineSettings/classic/routineSettings.png +0 -0
- psychopy/experiment/components/routineSettings/classic/routineSettings@2x.png +0 -0
- psychopy/experiment/components/routineSettings/dark/routineSettings.png +0 -0
- psychopy/experiment/components/routineSettings/dark/routineSettings@2x.png +0 -0
- psychopy/experiment/components/routineSettings/light/routineSettings.png +0 -0
- psychopy/experiment/components/routineSettings/light/routineSettings@2x.png +0 -0
- psychopy/experiment/components/serialOut/__init__.py +205 -0
- psychopy/experiment/components/serialOut/classic/serial.png +0 -0
- psychopy/experiment/components/serialOut/classic/serial@2x.png +0 -0
- psychopy/experiment/components/serialOut/dark/serial.png +0 -0
- psychopy/experiment/components/serialOut/dark/serial@2x.png +0 -0
- psychopy/experiment/components/serialOut/light/serial.png +0 -0
- psychopy/experiment/components/serialOut/light/serial@2x.png +0 -0
- psychopy/experiment/components/settings/JS_htmlHeader.tmpl +23 -0
- psychopy/experiment/components/settings/JS_setupExp.tmpl +28 -0
- psychopy/experiment/components/settings/__init__.py +2155 -0
- psychopy/experiment/components/settings/classic/settings.png +0 -0
- psychopy/experiment/components/settings/classic/settings@2x.png +0 -0
- psychopy/experiment/components/settings/dark/settings.png +0 -0
- psychopy/experiment/components/settings/dark/settings@2x.png +0 -0
- psychopy/experiment/components/settings/light/settings.png +0 -0
- psychopy/experiment/components/settings/light/settings@2x.png +0 -0
- psychopy/experiment/components/slider/__init__.py +410 -0
- psychopy/experiment/components/slider/classic/slider.png +0 -0
- psychopy/experiment/components/slider/classic/slider@2x.png +0 -0
- psychopy/experiment/components/slider/dark/slider.png +0 -0
- psychopy/experiment/components/slider/dark/slider@2x.png +0 -0
- psychopy/experiment/components/slider/light/slider.png +0 -0
- psychopy/experiment/components/slider/light/slider@2x.png +0 -0
- psychopy/experiment/components/sound/__init__.py +307 -0
- psychopy/experiment/components/sound/classic/sound.png +0 -0
- psychopy/experiment/components/sound/classic/sound@2x.png +0 -0
- psychopy/experiment/components/sound/dark/sound.png +0 -0
- psychopy/experiment/components/sound/dark/sound@2x.png +0 -0
- psychopy/experiment/components/sound/light/sound.png +0 -0
- psychopy/experiment/components/sound/light/sound@2x.png +0 -0
- psychopy/experiment/components/static/__init__.py +267 -0
- psychopy/experiment/components/static/classic/static.png +0 -0
- psychopy/experiment/components/static/classic/static@2x.png +0 -0
- psychopy/experiment/components/static/dark/static.png +0 -0
- psychopy/experiment/components/static/dark/static@2x.png +0 -0
- psychopy/experiment/components/static/light/static.png +0 -0
- psychopy/experiment/components/static/light/static@2x.png +0 -0
- psychopy/experiment/components/text/__init__.py +169 -0
- psychopy/experiment/components/text/classic/text.png +0 -0
- psychopy/experiment/components/text/classic/text@2x.png +0 -0
- psychopy/experiment/components/text/dark/text.png +0 -0
- psychopy/experiment/components/text/dark/text@2x.png +0 -0
- psychopy/experiment/components/text/light/text.png +0 -0
- psychopy/experiment/components/text/light/text@2x.png +0 -0
- psychopy/experiment/components/textbox/__init__.py +330 -0
- psychopy/experiment/components/textbox/classic/textbox.png +0 -0
- psychopy/experiment/components/textbox/classic/textbox@2x.png +0 -0
- psychopy/experiment/components/textbox/dark/textbox.png +0 -0
- psychopy/experiment/components/textbox/dark/textbox@2x.png +0 -0
- psychopy/experiment/components/textbox/light/textbox.png +0 -0
- psychopy/experiment/components/textbox/light/textbox@2x.png +0 -0
- psychopy/experiment/components/unknown/__init__.py +86 -0
- psychopy/experiment/components/unknown/classic/unknown.png +0 -0
- psychopy/experiment/components/unknown/classic/unknown@2x.png +0 -0
- psychopy/experiment/components/unknown/dark/unknown.png +0 -0
- psychopy/experiment/components/unknown/dark/unknown@2x.png +0 -0
- psychopy/experiment/components/unknown/light/unknown.png +0 -0
- psychopy/experiment/components/unknown/light/unknown@2x.png +0 -0
- psychopy/experiment/components/unknownPlugin/__init__.py +84 -0
- psychopy/experiment/components/unknownPlugin/classic/unknownPlugin.png +0 -0
- psychopy/experiment/components/unknownPlugin/classic/unknownPlugin@2x.png +0 -0
- psychopy/experiment/components/unknownPlugin/dark/unknownPlugin.png +0 -0
- psychopy/experiment/components/unknownPlugin/dark/unknownPlugin@2x.png +0 -0
- psychopy/experiment/components/unknownPlugin/light/unknownPlugin.png +0 -0
- psychopy/experiment/components/unknownPlugin/light/unknownPlugin@2x.png +0 -0
- psychopy/experiment/components/utils.py +0 -0
- psychopy/experiment/components/variable/__init__.py +184 -0
- psychopy/experiment/components/variable/classic/variable.png +0 -0
- psychopy/experiment/components/variable/classic/variable@2x.png +0 -0
- psychopy/experiment/components/variable/dark/variable.png +0 -0
- psychopy/experiment/components/variable/dark/variable@2x.png +0 -0
- psychopy/experiment/components/variable/light/variable.png +0 -0
- psychopy/experiment/components/variable/light/variable@2x.png +0 -0
- psychopy/experiment/experiment.xsd +223 -0
- psychopy/experiment/exports.py +388 -0
- psychopy/experiment/flow.py +531 -0
- psychopy/experiment/localization.py +11 -0
- psychopy/experiment/loopTemplate.xltx +0 -0
- psychopy/experiment/loops.py +952 -0
- psychopy/experiment/params.py +437 -0
- psychopy/experiment/plugins.py +115 -0
- psychopy/experiment/py2js.py +183 -0
- psychopy/experiment/py2js_transpiler.py +606 -0
- psychopy/experiment/questPlusTemplate.xltx +0 -0
- psychopy/experiment/questTemplate.xltx +0 -0
- psychopy/experiment/routines/__init__.py +108 -0
- psychopy/experiment/routines/_base.py +907 -0
- psychopy/experiment/routines/counterbalance/__init__.py +315 -0
- psychopy/experiment/routines/counterbalance/classic/counterbalance.png +0 -0
- psychopy/experiment/routines/counterbalance/classic/counterbalance@2x.png +0 -0
- psychopy/experiment/routines/counterbalance/counterbalanceItems.xltx +0 -0
- psychopy/experiment/routines/counterbalance/dark/counterbalance.png +0 -0
- psychopy/experiment/routines/counterbalance/dark/counterbalance@2x.png +0 -0
- psychopy/experiment/routines/counterbalance/light/counterbalance.png +0 -0
- psychopy/experiment/routines/counterbalance/light/counterbalance@2x.png +0 -0
- psychopy/experiment/routines/eyetracker_calibrate/__init__.py +275 -0
- psychopy/experiment/routines/eyetracker_calibrate/classic/eyetracker_calib.png +0 -0
- psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib.png +0 -0
- psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib@2x.png +0 -0
- psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib.png +0 -0
- psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib@2x.png +0 -0
- psychopy/experiment/routines/eyetracker_validate/__init__.py +354 -0
- psychopy/experiment/routines/eyetracker_validate/classic/eyetracker_valid.png +0 -0
- psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid.png +0 -0
- psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid@2x.png +0 -0
- psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid.png +0 -0
- psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid@2x.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/__init__.py +236 -0
- psychopy/experiment/routines/pavlovia_survey/classic/survey.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/classic/survey@2x.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/dark/survey.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/dark/survey@2x.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/light/survey.png +0 -0
- psychopy/experiment/routines/pavlovia_survey/light/survey@2x.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/__init__.py +429 -0
- psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator@2x.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator@2x.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator.png +0 -0
- psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator@2x.png +0 -0
- psychopy/experiment/routines/unknown/__init__.py +28 -0
- psychopy/experiment/routines/unknown/classic/eyetracker_record.png +0 -0
- psychopy/experiment/routines/unknown/classic/unknown.png +0 -0
- psychopy/experiment/routines/unknown/dark/eyetracker_record.png +0 -0
- psychopy/experiment/routines/unknown/dark/eyetracker_record@2x.png +0 -0
- psychopy/experiment/routines/unknown/dark/unknown.png +0 -0
- psychopy/experiment/routines/unknown/dark/unknown@2x.png +0 -0
- psychopy/experiment/routines/unknown/light/eyetracker_record.png +0 -0
- psychopy/experiment/routines/unknown/light/eyetracker_record@2x.png +0 -0
- psychopy/experiment/routines/unknown/light/unknown.png +0 -0
- psychopy/experiment/routines/unknown/light/unknown@2x.png +0 -0
- psychopy/experiment/routines/utils.py +0 -0
- psychopy/experiment/staircaseTemplate.xltx +0 -0
- psychopy/experiment/utils.py +44 -0
- psychopy/filters.py +11 -0
- psychopy/gamma.py +9 -0
- psychopy/gui/__init__.py +47 -0
- psychopy/gui/qtgui.py +866 -0
- psychopy/gui/util.py +78 -0
- psychopy/gui/wxgui.py +414 -0
- psychopy/hardware/__init__.py +216 -0
- psychopy/hardware/base.py +323 -0
- psychopy/hardware/bbtk/__init__.py +30 -0
- psychopy/hardware/brainproducts.py +31 -0
- psychopy/hardware/button.py +205 -0
- psychopy/hardware/buttonbox/__init__.py +122 -0
- psychopy/hardware/camera/__init__.py +2809 -0
- psychopy/hardware/cedrus.py +36 -0
- psychopy/hardware/crs/__init__.py +46 -0
- psychopy/hardware/crs/bits.py +34 -0
- psychopy/hardware/crs/colorcal.py +30 -0
- psychopy/hardware/crs/optical.py +46 -0
- psychopy/hardware/crs/shaders.py +21 -0
- psychopy/hardware/emotiv.py +33 -0
- psychopy/hardware/emulator.py +37 -0
- psychopy/hardware/eyetracker.py +208 -0
- psychopy/hardware/forp.py +42 -0
- psychopy/hardware/gammasci.py +32 -0
- psychopy/hardware/iolab.py +35 -0
- psychopy/hardware/joystick/__init__.py +597 -0
- psychopy/hardware/keyboard.py +880 -0
- psychopy/hardware/knownDevices.json +52 -0
- psychopy/hardware/labhackers.py +27 -0
- psychopy/hardware/labjacks.py +30 -0
- psychopy/hardware/listener.py +323 -0
- psychopy/hardware/manager.py +963 -0
- psychopy/hardware/microphone.py +1074 -0
- psychopy/hardware/minolta.py +30 -0
- psychopy/hardware/mouse/__init__.py +885 -0
- psychopy/hardware/photodiode.py +639 -0
- psychopy/hardware/photometer/__init__.py +176 -0
- psychopy/hardware/pr.py +30 -0
- psychopy/hardware/qmix.py +41 -0
- psychopy/hardware/serialdevice.py +342 -0
- psychopy/hardware/speaker.py +54 -0
- psychopy/hardware/triggerbox/__init__.py +56 -0
- psychopy/hardware/triggerbox/base.py +170 -0
- psychopy/hardware/triggerbox/parallel.py +362 -0
- psychopy/info.py +790 -0
- psychopy/iohub/__init__.py +45 -0
- psychopy/iohub/client/__init__.py +1454 -0
- psychopy/iohub/client/connect.py +260 -0
- psychopy/iohub/client/eyetracker/__init__.py +4 -0
- psychopy/iohub/client/eyetracker/validation/__init__.py +8 -0
- psychopy/iohub/client/eyetracker/validation/posgrid.py +274 -0
- psychopy/iohub/client/eyetracker/validation/procedure.py +1292 -0
- psychopy/iohub/client/eyetracker/validation/trigger.py +240 -0
- psychopy/iohub/client/keyboard.py +508 -0
- psychopy/iohub/client/wintab.py +378 -0
- psychopy/iohub/constants.py +1176 -0
- psychopy/iohub/datastore/__init__.py +439 -0
- psychopy/iohub/datastore/default_datastore.yaml +8 -0
- psychopy/iohub/datastore/util.py +868 -0
- psychopy/iohub/default_config.yaml +17 -0
- psychopy/iohub/devices/__init__.py +960 -0
- psychopy/iohub/devices/computer.py +607 -0
- psychopy/iohub/devices/default_device.yaml +15 -0
- psychopy/iohub/devices/deviceConfigValidation.py +517 -0
- psychopy/iohub/devices/display/__init__.py +755 -0
- psychopy/iohub/devices/display/default_display.yaml +118 -0
- psychopy/iohub/devices/display/supported_config_settings.yaml +69 -0
- psychopy/iohub/devices/eventfilters.py +3572 -0
- psychopy/iohub/devices/experiment/__init__.py +316 -0
- psychopy/iohub/devices/experiment/default_experiment.yaml +63 -0
- psychopy/iohub/devices/experiment/supported_config_settings.yaml +22 -0
- psychopy/iohub/devices/eyetracker/__init__.py +436 -0
- psychopy/iohub/devices/eyetracker/calibration/__init__.py +1 -0
- psychopy/iohub/devices/eyetracker/calibration/procedure.py +409 -0
- psychopy/iohub/devices/eyetracker/default_eyetracker.yaml +18 -0
- psychopy/iohub/devices/eyetracker/eye_events.py +1751 -0
- psychopy/iohub/devices/eyetracker/filters/__init__.py +4 -0
- psychopy/iohub/devices/eyetracker/filters/parser.py +913 -0
- psychopy/iohub/devices/eyetracker/hw/__init__.py +4 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +17 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +28 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/calibration.py +18 -0
- psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +21 -0
- psychopy/iohub/devices/eyetracker/hw/mouse/__init__.py +8 -0
- psychopy/iohub/devices/eyetracker/hw/mouse/calibration.py +10 -0
- psychopy/iohub/devices/eyetracker/hw/mouse/default_eyetracker.yaml +102 -0
- psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +444 -0
- psychopy/iohub/devices/eyetracker/hw/mouse/supported_config_settings.yaml +110 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/__init__.py +4 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +26 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/eyetracker.py +18 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/__init__.py +29 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/bisector.py +20 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/constants.py +19 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/data_parse.py +22 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/eyetracker.py +18 -0
- psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/pupil_remote.py +18 -0
- psychopy/iohub/devices/eyetracker/hw/sr_research/__init__.py +4 -0
- psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/__init__.py +28 -0
- psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/calibration.py +22 -0
- psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/eyetracker.py +38 -0
- psychopy/iohub/devices/eyetracker/hw/tobii/__init__.py +29 -0
- psychopy/iohub/devices/eyetracker/hw/tobii/calibration.py +19 -0
- psychopy/iohub/devices/eyetracker/hw/tobii/eyetracker.py +17 -0
- psychopy/iohub/devices/eyetracker/hw/tobii/tobiiwrapper.py +19 -0
- psychopy/iohub/devices/eyetracker/supported_config_settings.yaml +44 -0
- psychopy/iohub/devices/keyboard/__init__.py +309 -0
- psychopy/iohub/devices/keyboard/darwin.py +428 -0
- psychopy/iohub/devices/keyboard/darwinkey.py +94 -0
- psychopy/iohub/devices/keyboard/default_keyboard.yaml +94 -0
- psychopy/iohub/devices/keyboard/linux2.py +102 -0
- psychopy/iohub/devices/keyboard/supported_config_settings.yaml +39 -0
- psychopy/iohub/devices/keyboard/win32.py +356 -0
- psychopy/iohub/devices/mouse/__init__.py +709 -0
- psychopy/iohub/devices/mouse/darwin.py +414 -0
- psychopy/iohub/devices/mouse/default_mouse.yaml +92 -0
- psychopy/iohub/devices/mouse/linux2.py +167 -0
- psychopy/iohub/devices/mouse/supported_config_settings.yaml +39 -0
- psychopy/iohub/devices/mouse/win32.py +279 -0
- psychopy/iohub/devices/pyXHook.py +619 -0
- psychopy/iohub/devices/serial/__init__.py +779 -0
- psychopy/iohub/devices/serial/default_pstbox.yaml +108 -0
- psychopy/iohub/devices/serial/default_serial.yaml +108 -0
- psychopy/iohub/devices/serial/supported_config_settings.yaml +85 -0
- psychopy/iohub/devices/serial/supported_config_settings_pstbox.yaml +85 -0
- psychopy/iohub/devices/supported_config_settings.yaml +36 -0
- psychopy/iohub/devices/wintab/__init__.py +580 -0
- psychopy/iohub/devices/wintab/default_wintab.yaml +89 -0
- psychopy/iohub/devices/wintab/supported_config_settings.yaml +42 -0
- psychopy/iohub/devices/wintab/win32.py +722 -0
- psychopy/iohub/devices/xlib.py +6795 -0
- psychopy/iohub/errors.py +61 -0
- psychopy/iohub/lazy_import.py +582 -0
- psychopy/iohub/net.py +349 -0
- psychopy/iohub/server.py +1075 -0
- psychopy/iohub/start_iohub_process.py +118 -0
- psychopy/iohub/util/__init__.py +841 -0
- psychopy/iohub/util/visualangle.py +126 -0
- psychopy/layout.py +916 -0
- psychopy/liaison.py +419 -0
- psychopy/locale_setup.py +31 -0
- psychopy/localization/__init__.py +22 -0
- psychopy/localization/_localization.py +155 -0
- psychopy/localization/generateTranslationTemplate.py +139 -0
- psychopy/localization/mappings.txt +170 -0
- psychopy/localization/messages.pot +7516 -0
- psychopy/localization/readme.txt +68 -0
- psychopy/logging.py +406 -0
- psychopy/microphone.py +54 -0
- psychopy/misc.py +40 -0
- psychopy/monitors/MonitorCenter.py +1254 -0
- psychopy/monitors/__init__.py +30 -0
- psychopy/monitors/calibData.py +78 -0
- psychopy/monitors/calibTools.py +1300 -0
- psychopy/monitors/getLumSeries.py +50 -0
- psychopy/monitors/psychopy-icon.svg +395 -0
- psychopy/monitors/psychopy.ico +0 -0
- psychopy/parallel/__init__.py +206 -0
- psychopy/parallel/_dlportio.py +150 -0
- psychopy/parallel/_inpout.py +120 -0
- psychopy/parallel/_linux.py +105 -0
- psychopy/piloting.py +61 -0
- psychopy/platform_specific/__init__.py +48 -0
- psychopy/platform_specific/darwin.py +328 -0
- psychopy/platform_specific/linux.py +79 -0
- psychopy/platform_specific/posix.py +16 -0
- psychopy/platform_specific/win32.py +99 -0
- psychopy/plugins/__init__.py +1354 -0
- psychopy/preferences/Darwin.spec +282 -0
- psychopy/preferences/FreeBSD.spec +282 -0
- psychopy/preferences/Linux.spec +282 -0
- psychopy/preferences/Windows.spec +282 -0
- psychopy/preferences/__init__.py +20 -0
- psychopy/preferences/baseNoArch.spec +278 -0
- psychopy/preferences/generateHints.py +91 -0
- psychopy/preferences/generateSpec.py +80 -0
- psychopy/preferences/hints.py +329 -0
- psychopy/preferences/preferences.py +332 -0
- psychopy/projects/__init__.py +11 -0
- psychopy/projects/gitignore.py +45 -0
- psychopy/projects/pavlovia.py +1566 -0
- psychopy/projects/sshkeys.py +107 -0
- psychopy/psychojs.zip +0 -0
- psychopy/pylintrc +69 -0
- psychopy/scripts/__init__.py +0 -0
- psychopy/scripts/psyexpCompile.py +233 -0
- psychopy/session.py +1450 -0
- psychopy/sound/__init__.py +279 -0
- psychopy/sound/_base.py +282 -0
- psychopy/sound/audioclip.py +851 -0
- psychopy/sound/audiodevice.py +857 -0
- psychopy/sound/backend_ptb.py +660 -0
- psychopy/sound/backend_pygame.py +309 -0
- psychopy/sound/backend_pyo.py +33 -0
- psychopy/sound/backend_pysound.py +341 -0
- psychopy/sound/backend_sounddevice.py +32 -0
- psychopy/sound/exceptions.py +115 -0
- psychopy/sound/microphone.py +274 -0
- psychopy/sound/transcribe.py +1248 -0
- psychopy/tests/.DS_Store +0 -0
- psychopy/tests/README.md +344 -0
- psychopy/tests/__init__.py +47 -0
- psychopy/tests/data/Electronic_Chime-KevanGC-495939803.wav +0 -0
- psychopy/tests/data/TestCircle_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestCircle_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestCircle_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestCircle_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestCircle_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestCircle_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestCircle_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestCircle_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestCircle_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestCircle_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestCircle_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestCircle_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestCircle_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestCircle_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestCircle_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestCircle_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestCircle_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestCircle_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestCircle_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestCircle_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq10_s0.5.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq10_s0.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq10_s1.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq1_s0.5.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq1_s0.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq1_s1.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq3_s0.5.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq3_s0.png +0 -0
- psychopy/tests/data/TestForm_scrolling_nq3_s1.png +0 -0
- psychopy/tests/data/TestImage_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestImage_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestImage_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestImage_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestImage_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestImage_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestImage_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestImage_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestImage_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestImage_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestImage_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestImage_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestImage_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestImage_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestImage_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestImage_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestImage_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestImage_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestImage_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestImage_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_testValue_minmax_0.png +0 -0
- psychopy/tests/data/TestProgress_testValue_minmax_1.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_center center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_center center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_top center_0.3.png +0 -0
- psychopy/tests/data/TestProgress_testValue_vertical_top center_0.6.png +0 -0
- psychopy/tests/data/TestProgress_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestProgress_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestProgress_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestProgress_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestProgress_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestProgress_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestProgress_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestProgress_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestProgress_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestProgress_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestProgress_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestProgress_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestProgress_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestProgress_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestProgress_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestProgress_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestProgress_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestProgress_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestProgress_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestProgress_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestROI_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestROI_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestROI_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestROI_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestROI_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestROI_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestROI_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestROI_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestROI_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestROI_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestROI_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestROI_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestROI_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestROI_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestROI_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestROI_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestROI_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestROI_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestROI_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestROI_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestSession_exp1.png +0 -0
- psychopy/tests/data/TestSession_exp2.png +0 -0
- psychopy/tests/data/TestShape_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestShape_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestShape_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestShape_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestShape_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestShape_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestShape_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestShape_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestShape_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestShape_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestShape_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestShape_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestShape_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestShape_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestShape_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestShape_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestShape_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestShape_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestShape_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestShape_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestTarget_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/TestTarget_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/TestTarget_w128h128_center_center.png +0 -0
- psychopy/tests/data/TestTarget_w128h128_top_left.png +0 -0
- psychopy/tests/data/TestTarget_w128h128_top_right.png +0 -0
- psychopy/tests/data/TestTarget_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/TestTarget_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/TestTarget_w128h64_center_center.png +0 -0
- psychopy/tests/data/TestTarget_w128h64_top_left.png +0 -0
- psychopy/tests/data/TestTarget_w128h64_top_right.png +0 -0
- psychopy/tests/data/TestTarget_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/TestTarget_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/TestTarget_w64h128_center_center.png +0 -0
- psychopy/tests/data/TestTarget_w64h128_top_left.png +0 -0
- psychopy/tests/data/TestTarget_w64h128_top_right.png +0 -0
- psychopy/tests/data/TestTarget_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/TestTarget_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/TestTarget_w64h64_center_center.png +0 -0
- psychopy/tests/data/TestTarget_w64h64_top_left.png +0 -0
- psychopy/tests/data/TestTarget_w64h64_top_right.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_0p6.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_0p8.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_1.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_1p2.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_1p4.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_1p6.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_1p8.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_2p0.png +0 -0
- psychopy/tests/data/TestTextbox_testLetterSpacing_None.png +0 -0
- psychopy/tests/data/Test_textbox_testSpeechpoint_-3ovr8_0ovr8.png +0 -0
- psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_-3ovr8.png +0 -0
- psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_3ovr8.png +0 -0
- psychopy/tests/data/Test_textbox_testSpeechpoint_3ovr8_0ovr8.png +0 -0
- psychopy/tests/data/Test_textbox_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/Test_textbox_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/Test_textbox_w128h128_center_center.png +0 -0
- psychopy/tests/data/Test_textbox_w128h128_top_left.png +0 -0
- psychopy/tests/data/Test_textbox_w128h128_top_right.png +0 -0
- psychopy/tests/data/Test_textbox_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/Test_textbox_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/Test_textbox_w128h64_center_center.png +0 -0
- psychopy/tests/data/Test_textbox_w128h64_top_left.png +0 -0
- psychopy/tests/data/Test_textbox_w128h64_top_right.png +0 -0
- psychopy/tests/data/Test_textbox_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/Test_textbox_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/Test_textbox_w64h128_center_center.png +0 -0
- psychopy/tests/data/Test_textbox_w64h128_top_left.png +0 -0
- psychopy/tests/data/Test_textbox_w64h128_top_right.png +0 -0
- psychopy/tests/data/Test_textbox_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/Test_textbox_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/Test_textbox_w64h64_center_center.png +0 -0
- psychopy/tests/data/Test_textbox_w64h64_top_left.png +0 -0
- psychopy/tests/data/Test_textbox_w64h64_top_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h128_center_center.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h128_top_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h128_top_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h64_center_center.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h64_top_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w128h64_top_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h128_center_center.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h128_top_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h128_top_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_right.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h64_center_center.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h64_top_left.png +0 -0
- psychopy/tests/data/Test_uax14_textbox_w64h64_top_right.png +0 -0
- psychopy/tests/data/TextComponent_disabled.psyexp +63 -0
- psychopy/tests/data/TextComponent_not_disabled.psyexp +63 -0
- psychopy/tests/data/aperture1_deg.png +0 -0
- psychopy/tests/data/aperture1_degFlat.png +0 -0
- psychopy/tests/data/aperture1_degFlatPos.png +0 -0
- psychopy/tests/data/aperture1_norm.png +0 -0
- psychopy/tests/data/aperture1_normHexbackground.png +0 -0
- psychopy/tests/data/aperture1_normNoShade.png +0 -0
- psychopy/tests/data/aperture1_pix.png +0 -0
- psychopy/tests/data/aperture1_stencil.png +0 -0
- psychopy/tests/data/aperture2_deg.png +0 -0
- psychopy/tests/data/aperture2_degFlat.png +0 -0
- psychopy/tests/data/aperture2_degFlatPos.png +0 -0
- psychopy/tests/data/aperture2_norm.png +0 -0
- psychopy/tests/data/aperture2_normHexbackground.png +0 -0
- psychopy/tests/data/aperture2_normNoShade.png +0 -0
- psychopy/tests/data/aperture2_pix.png +0 -0
- psychopy/tests/data/aperture2_stencil.png +0 -0
- psychopy/tests/data/beatandrcos_cm.png +0 -0
- psychopy/tests/data/beatandrcos_deg.png +0 -0
- psychopy/tests/data/beatandrcos_degFlat.png +0 -0
- psychopy/tests/data/beatandrcos_degFlatPos.png +0 -0
- psychopy/tests/data/beatandrcos_height.png +0 -0
- psychopy/tests/data/beatandrcos_norm.png +0 -0
- psychopy/tests/data/beatandrcos_normAddBlend.png +0 -0
- psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/beatandrcos_normHexbackground.png +0 -0
- psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/beatandrcos_norm_local.png +0 -0
- psychopy/tests/data/beatandrcos_pix.png +0 -0
- psychopy/tests/data/beatandrcos_stencil.png +0 -0
- psychopy/tests/data/blend_add_cm.png +0 -0
- psychopy/tests/data/blend_add_deg.png +0 -0
- psychopy/tests/data/blend_add_degFlat.png +0 -0
- psychopy/tests/data/blend_add_degFlatPos.png +0 -0
- psychopy/tests/data/blend_add_height.png +0 -0
- psychopy/tests/data/blend_add_norm.png +0 -0
- psychopy/tests/data/blend_add_normAddBlend.png +0 -0
- psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
- psychopy/tests/data/blend_add_normHexbackground.png +0 -0
- psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
- psychopy/tests/data/blend_add_normNoShade.png +0 -0
- psychopy/tests/data/blend_add_norm_local.png +0 -0
- psychopy/tests/data/blend_add_pix.png +0 -0
- psychopy/tests/data/blend_add_stencil.png +0 -0
- psychopy/tests/data/broken2020_2_5_resources/broken_resources.psyexp +84 -0
- psychopy/tests/data/broken2020_2_5_resources/psychopy72.png +0 -0
- psychopy/tests/data/bufferimg_gabor_cm.png +0 -0
- psychopy/tests/data/bufferimg_gabor_deg.png +0 -0
- psychopy/tests/data/bufferimg_gabor_degFlat.png +0 -0
- psychopy/tests/data/bufferimg_gabor_degFlatPos.png +0 -0
- psychopy/tests/data/bufferimg_gabor_height.png +0 -0
- psychopy/tests/data/bufferimg_gabor_norm.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normAddBlend.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normHexbackground.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normNoShade.png +0 -0
- psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_pix.png +0 -0
- psychopy/tests/data/bufferimg_gabor_stencil.png +0 -0
- psychopy/tests/data/circleHex_cm.png +0 -0
- psychopy/tests/data/circleHex_deg.png +0 -0
- psychopy/tests/data/circleHex_degFlat.png +0 -0
- psychopy/tests/data/circleHex_degFlatPos.png +0 -0
- psychopy/tests/data/circleHex_height.png +0 -0
- psychopy/tests/data/circleHex_norm.png +0 -0
- psychopy/tests/data/circleHex_normAddBlend.png +0 -0
- psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
- psychopy/tests/data/circleHex_normHexbackground.png +0 -0
- psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
- psychopy/tests/data/circleHex_normNoShade.png +0 -0
- psychopy/tests/data/circleHex_norm_local.png +0 -0
- psychopy/tests/data/circleHex_pix.png +0 -0
- psychopy/tests/data/circleHex_stencil.png +0 -0
- psychopy/tests/data/corrFullRandom.csv +16 -0
- psychopy/tests/data/corrFullRandom.tsv +6 -0
- psychopy/tests/data/corrFullRandomTH2.csv +16 -0
- psychopy/tests/data/corrMultiKeyExpWide.csv +3 -0
- psychopy/tests/data/corrMultiKeyTrials.csv +8 -0
- psychopy/tests/data/corrMultiKeyTrials.xlsx +0 -0
- psychopy/tests/data/corrRandom.csv +16 -0
- psychopy/tests/data/corrRandom.tsv +6 -0
- psychopy/tests/data/corrRandomTH2.csv +16 -0
- psychopy/tests/data/corrXlsx.xlsx +0 -0
- psychopy/tests/data/correctScript/js/correctCodeComponent.js +181 -0
- psychopy/tests/data/correctScript/js/correctImageComponent.js +204 -0
- psychopy/tests/data/correctScript/js/correctKeyboardComponent.js +225 -0
- psychopy/tests/data/correctScript/js/correctMouseComponent.js +221 -0
- psychopy/tests/data/correctScript/js/correctMovieComponent.js +210 -0
- psychopy/tests/data/correctScript/js/correctPolygonComponent.js +205 -0
- psychopy/tests/data/correctScript/js/correctSliderComponent.js +209 -0
- psychopy/tests/data/correctScript/js/correctSoundComponent.js +205 -0
- psychopy/tests/data/correctScript/js/correctTextComponent.js +207 -0
- psychopy/tests/data/correctScript/python/correctApertureComponent.py +157 -0
- psychopy/tests/data/correctScript/python/correctCodeComponent.py +135 -0
- psychopy/tests/data/correctScript/python/correctDotsComponent.py +161 -0
- psychopy/tests/data/correctScript/python/correctEnvGratingComponent.py +167 -0
- psychopy/tests/data/correctScript/python/correctFormComponent.py +150 -0
- psychopy/tests/data/correctScript/python/correctGratingComponent.py +158 -0
- psychopy/tests/data/correctScript/python/correctImageComponent.py +160 -0
- psychopy/tests/data/correctScript/python/correctJoyButtonsComponent.py +204 -0
- psychopy/tests/data/correctScript/python/correctJoystickComponent.py +214 -0
- psychopy/tests/data/correctScript/python/correctKeyboardComponent.py +169 -0
- psychopy/tests/data/correctScript/python/correctMicrophoneComponent.py +163 -0
- psychopy/tests/data/correctScript/python/correctMouseComponent.py +174 -0
- psychopy/tests/data/correctScript/python/correctMovieComponent.py +160 -0
- psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +169 -0
- psychopy/tests/data/correctScript/python/correctParallelOutComponent.py +156 -0
- psychopy/tests/data/correctScript/python/correctPatchComponent.py +159 -0
- psychopy/tests/data/correctScript/python/correctPolygonComponent.py +159 -0
- psychopy/tests/data/correctScript/python/correctQmixPumpComponent.py +174 -0
- psychopy/tests/data/correctScript/python/correctRatingScaleComponent.py +152 -0
- psychopy/tests/data/correctScript/python/correctSliderComponent.py +159 -0
- psychopy/tests/data/correctScript/python/correctSoundComponent.py +157 -0
- psychopy/tests/data/correctScript/python/correctStaticComponent.py +176 -0
- psychopy/tests/data/correctScript/python/correctTextComponent.py +159 -0
- psychopy/tests/data/correctScript/python/correctVariableComponent.py +139 -0
- psychopy/tests/data/correctScript/python/correctcedrusButtonBoxComponent.py +199 -0
- psychopy/tests/data/correctScript/python/correctioLabsButtonBoxComponent.py +187 -0
- psychopy/tests/data/dataTest.xlsx +0 -0
- psychopy/tests/data/dots_cm.png +0 -0
- psychopy/tests/data/dots_deg.png +0 -0
- psychopy/tests/data/dots_degFlat.png +0 -0
- psychopy/tests/data/dots_degFlatPos.png +0 -0
- psychopy/tests/data/dots_height.png +0 -0
- psychopy/tests/data/dots_norm.png +0 -0
- psychopy/tests/data/dots_normAddBlend.png +0 -0
- psychopy/tests/data/dots_normAddBlend_local.png +0 -0
- psychopy/tests/data/dots_normHexbackground.png +0 -0
- psychopy/tests/data/dots_normHexbackground_local.png +0 -0
- psychopy/tests/data/dots_normNoShade.png +0 -0
- psychopy/tests/data/dots_norm_local.png +0 -0
- psychopy/tests/data/dots_pix.png +0 -0
- psychopy/tests/data/dots_stencil.png +0 -0
- psychopy/tests/data/elarray1_cm.png +0 -0
- psychopy/tests/data/elarray1_deg.png +0 -0
- psychopy/tests/data/elarray1_degFlat.png +0 -0
- psychopy/tests/data/elarray1_degFlatPos.png +0 -0
- psychopy/tests/data/elarray1_height.png +0 -0
- psychopy/tests/data/elarray1_norm.png +0 -0
- psychopy/tests/data/elarray1_normAddBlend.png +0 -0
- psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
- psychopy/tests/data/elarray1_normHexbackground.png +0 -0
- psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
- psychopy/tests/data/elarray1_norm_local.png +0 -0
- psychopy/tests/data/elarray1_pix.png +0 -0
- psychopy/tests/data/elarray1_stencil.png +0 -0
- psychopy/tests/data/envelopeandrcos_cm.png +0 -0
- psychopy/tests/data/envelopeandrcos_deg.png +0 -0
- psychopy/tests/data/envelopeandrcos_degFlat.png +0 -0
- psychopy/tests/data/envelopeandrcos_degFlatPos.png +0 -0
- psychopy/tests/data/envelopeandrcos_height.png +0 -0
- psychopy/tests/data/envelopeandrcos_norm.png +0 -0
- psychopy/tests/data/envelopeandrcos_normAddBlend.png +0 -0
- psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_normHexbackground.png +0 -0
- psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_pix.png +0 -0
- psychopy/tests/data/envelopeandrcos_stencil.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_cm.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_deg.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_degFlat.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_degFlatPos.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_height.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_norm.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normAddBlend.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normHexbackground.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_pix.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_stencil.png +0 -0
- psychopy/tests/data/filltext.png +0 -0
- psychopy/tests/data/form_font_demographics.png +0 -0
- psychopy/tests/data/form_font_demographics.xlsx +0 -0
- psychopy/tests/data/form_font_languages.png +0 -0
- psychopy/tests/data/form_font_languages.xlsx +0 -0
- psychopy/tests/data/form_font_nonstandard.png +0 -0
- psychopy/tests/data/futureParams.psyexp +150 -0
- psychopy/tests/data/gabor1_cm.png +0 -0
- psychopy/tests/data/gabor1_deg.png +0 -0
- psychopy/tests/data/gabor1_degFlat.png +0 -0
- psychopy/tests/data/gabor1_degFlatPos.png +0 -0
- psychopy/tests/data/gabor1_height.png +0 -0
- psychopy/tests/data/gabor1_norm.png +0 -0
- psychopy/tests/data/gabor1_normAddBlend.png +0 -0
- psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
- psychopy/tests/data/gabor1_normHexbackground.png +0 -0
- psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
- psychopy/tests/data/gabor1_normNoShade.png +0 -0
- psychopy/tests/data/gabor1_norm_local.png +0 -0
- psychopy/tests/data/gabor1_pix.png +0 -0
- psychopy/tests/data/gabor1_stencil.png +0 -0
- psychopy/tests/data/gabor2_cm.png +0 -0
- psychopy/tests/data/gabor2_deg.png +0 -0
- psychopy/tests/data/gabor2_degFlat.png +0 -0
- psychopy/tests/data/gabor2_degFlatPos.png +0 -0
- psychopy/tests/data/gabor2_height.png +0 -0
- psychopy/tests/data/gabor2_norm.png +0 -0
- psychopy/tests/data/gabor2_normAddBlend.png +0 -0
- psychopy/tests/data/gabor2_normHexbackground.png +0 -0
- psychopy/tests/data/gabor2_normNoShade.png +0 -0
- psychopy/tests/data/gabor2_pix.png +0 -0
- psychopy/tests/data/gabor2_stencil.png +0 -0
- psychopy/tests/data/ghost_stroop.psyexp +131 -0
- psychopy/tests/data/ghost_trialTypes.xlsx +0 -0
- psychopy/tests/data/green_48000.flac.dist +0 -0
- psychopy/tests/data/greyscale.jpg +0 -0
- psychopy/tests/data/greyscale2.png +0 -0
- psychopy/tests/data/greyscale2_cm.png +0 -0
- psychopy/tests/data/greyscale2_deg.png +0 -0
- psychopy/tests/data/greyscale2_degFlat.png +0 -0
- psychopy/tests/data/greyscale2_degFlatPos.png +0 -0
- psychopy/tests/data/greyscale2_height.png +0 -0
- psychopy/tests/data/greyscale2_norm.png +0 -0
- psychopy/tests/data/greyscale2_normAddBlend.png +0 -0
- psychopy/tests/data/greyscale2_normHexbackground.png +0 -0
- psychopy/tests/data/greyscale2_normNoShade.png +0 -0
- psychopy/tests/data/greyscale2_pix.png +0 -0
- psychopy/tests/data/greyscale2_stencil.png +0 -0
- psychopy/tests/data/greyscaleLowContr_cm.png +0 -0
- psychopy/tests/data/greyscaleLowContr_deg.png +0 -0
- psychopy/tests/data/greyscaleLowContr_degFlat.png +0 -0
- psychopy/tests/data/greyscaleLowContr_degFlatPos.png +0 -0
- psychopy/tests/data/greyscaleLowContr_height.png +0 -0
- psychopy/tests/data/greyscaleLowContr_norm.png +0 -0
- psychopy/tests/data/greyscaleLowContr_normAddBlend.png +0 -0
- psychopy/tests/data/greyscaleLowContr_normHexbackground.png +0 -0
- psychopy/tests/data/greyscaleLowContr_normNoShade.png +0 -0
- psychopy/tests/data/greyscaleLowContr_pix.png +0 -0
- psychopy/tests/data/greyscaleLowContr_stencil.png +0 -0
- psychopy/tests/data/greyscale_cm.png +0 -0
- psychopy/tests/data/greyscale_deg.png +0 -0
- psychopy/tests/data/greyscale_degFlat.png +0 -0
- psychopy/tests/data/greyscale_degFlatPos.png +0 -0
- psychopy/tests/data/greyscale_height.png +0 -0
- psychopy/tests/data/greyscale_norm.png +0 -0
- psychopy/tests/data/greyscale_normAddBlend.png +0 -0
- psychopy/tests/data/greyscale_normHexbackground.png +0 -0
- psychopy/tests/data/greyscale_normNoShade.png +0 -0
- psychopy/tests/data/greyscale_pix.png +0 -0
- psychopy/tests/data/greyscale_stencil.png +0 -0
- psychopy/tests/data/imageAndGauss_cm.png +0 -0
- psychopy/tests/data/imageAndGauss_deg.png +0 -0
- psychopy/tests/data/imageAndGauss_degFlat.png +0 -0
- psychopy/tests/data/imageAndGauss_degFlatPos.png +0 -0
- psychopy/tests/data/imageAndGauss_height.png +0 -0
- psychopy/tests/data/imageAndGauss_norm.png +0 -0
- psychopy/tests/data/imageAndGauss_normAddBlend.png +0 -0
- psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normHexbackground.png +0 -0
- psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normNoShade.png +0 -0
- psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
- psychopy/tests/data/imageAndGauss_pix.png +0 -0
- psychopy/tests/data/imageAndGauss_stencil.png +0 -0
- psychopy/tests/data/movFrame1_cm.png +0 -0
- psychopy/tests/data/movFrame1_deg.png +0 -0
- psychopy/tests/data/movFrame1_degFlat.png +0 -0
- psychopy/tests/data/movFrame1_degFlatPos.png +0 -0
- psychopy/tests/data/movFrame1_height.png +0 -0
- psychopy/tests/data/movFrame1_norm.png +0 -0
- psychopy/tests/data/movFrame1_normAddBlend.png +0 -0
- psychopy/tests/data/movFrame1_normHexbackground.png +0 -0
- psychopy/tests/data/movFrame1_normNoShade.png +0 -0
- psychopy/tests/data/movFrame1_pix.png +0 -0
- psychopy/tests/data/movFrame1_stencil.png +0 -0
- psychopy/tests/data/multiKeypressExperiment.psydat +357 -0
- psychopy/tests/data/multiKeypressTrialhandler.psydat +252 -0
- psychopy/tests/data/multiStairConds.xlsx +0 -0
- psychopy/tests/data/multiStairQuestPlus.xlsx +0 -0
- psychopy/tests/data/newstyle.psydat +6155 -0
- psychopy/tests/data/noiseAndRcos_cm.png +0 -0
- psychopy/tests/data/noiseAndRcos_deg.png +0 -0
- psychopy/tests/data/noiseAndRcos_degFlat.png +0 -0
- psychopy/tests/data/noiseAndRcos_degFlatPos.png +0 -0
- psychopy/tests/data/noiseAndRcos_height.png +0 -0
- psychopy/tests/data/noiseAndRcos_norm.png +0 -0
- psychopy/tests/data/noiseAndRcos_normAddBlend.png +0 -0
- psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normHexbackground.png +0 -0
- psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normNoShade.png +0 -0
- psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_pix.png +0 -0
- psychopy/tests/data/noiseAndRcos_stencil.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_cm.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_deg.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_degFlat.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_degFlatPos.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_height.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_norm.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normAddBlend.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normHexbackground.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normNoShade.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_pix.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_stencil.png +0 -0
- psychopy/tests/data/numpyImage_cm.png +0 -0
- psychopy/tests/data/numpyImage_deg.png +0 -0
- psychopy/tests/data/numpyImage_degFlat.png +0 -0
- psychopy/tests/data/numpyImage_degFlatPos.png +0 -0
- psychopy/tests/data/numpyImage_height.png +0 -0
- psychopy/tests/data/numpyImage_norm.png +0 -0
- psychopy/tests/data/numpyImage_normAddBlend.png +0 -0
- psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
- psychopy/tests/data/numpyImage_normHexbackground.png +0 -0
- psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
- psychopy/tests/data/numpyImage_normNoShade.png +0 -0
- psychopy/tests/data/numpyImage_norm_local.png +0 -0
- psychopy/tests/data/numpyImage_pix.png +0 -0
- psychopy/tests/data/numpyImage_stencil.png +0 -0
- psychopy/tests/data/numpyLowContr_cm.png +0 -0
- psychopy/tests/data/numpyLowContr_deg.png +0 -0
- psychopy/tests/data/numpyLowContr_degFlat.png +0 -0
- psychopy/tests/data/numpyLowContr_degFlatPos.png +0 -0
- psychopy/tests/data/numpyLowContr_height.png +0 -0
- psychopy/tests/data/numpyLowContr_norm.png +0 -0
- psychopy/tests/data/numpyLowContr_normAddBlend.png +0 -0
- psychopy/tests/data/numpyLowContr_normHexbackground.png +0 -0
- psychopy/tests/data/numpyLowContr_normNoShade.png +0 -0
- psychopy/tests/data/numpyLowContr_pix.png +0 -0
- psychopy/tests/data/numpyLowContr_stencil.png +0 -0
- psychopy/tests/data/oldstyle.psydat +1534 -0
- psychopy/tests/data/oldstyle_stair.psydat +263 -0
- psychopy/tests/data/ratingscale1_cm.png +0 -0
- psychopy/tests/data/ratingscale1_deg.png +0 -0
- psychopy/tests/data/ratingscale1_degFlat.png +0 -0
- psychopy/tests/data/ratingscale1_degFlatPos.png +0 -0
- psychopy/tests/data/ratingscale1_height.png +0 -0
- psychopy/tests/data/ratingscale1_norm.png +0 -0
- psychopy/tests/data/ratingscale1_normAddBlend.png +0 -0
- psychopy/tests/data/ratingscale1_normHexbackground.png +0 -0
- psychopy/tests/data/ratingscale1_normNoShade.png +0 -0
- psychopy/tests/data/ratingscale1_pix.png +0 -0
- psychopy/tests/data/ratingscale1_stencil.png +0 -0
- psychopy/tests/data/red_16000.flac.dist +0 -0
- psychopy/tests/data/retroListParam.psyexp +82 -0
- psychopy/tests/data/right_to_left_unidcode.xlsx +0 -0
- psychopy/tests/data/sample.meshwarp.data +11522 -0
- psychopy/tests/data/shape2_1_cm.png +0 -0
- psychopy/tests/data/shape2_1_deg.png +0 -0
- psychopy/tests/data/shape2_1_degFlat.png +0 -0
- psychopy/tests/data/shape2_1_degFlatPos.png +0 -0
- psychopy/tests/data/shape2_1_height.png +0 -0
- psychopy/tests/data/shape2_1_norm.png +0 -0
- psychopy/tests/data/shape2_1_normAddBlend.png +0 -0
- psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
- psychopy/tests/data/shape2_1_normHexbackground.png +0 -0
- psychopy/tests/data/shape2_1_normNoShade.png +0 -0
- psychopy/tests/data/shape2_1_norm_local.png +0 -0
- psychopy/tests/data/shape2_1_pix.png +0 -0
- psychopy/tests/data/shape2_1_stencil.png +0 -0
- psychopy/tests/data/shape2_2_cm.png +0 -0
- psychopy/tests/data/shape2_2_deg.png +0 -0
- psychopy/tests/data/shape2_2_degFlat.png +0 -0
- psychopy/tests/data/shape2_2_degFlatPos.png +0 -0
- psychopy/tests/data/shape2_2_height.png +0 -0
- psychopy/tests/data/shape2_2_norm.png +0 -0
- psychopy/tests/data/shape2_2_normAddBlend.png +0 -0
- psychopy/tests/data/shape2_2_normHexbackground.png +0 -0
- psychopy/tests/data/shape2_2_normNoShade.png +0 -0
- psychopy/tests/data/shape2_2_pix.png +0 -0
- psychopy/tests/data/shape2_2_stencil.png +0 -0
- psychopy/tests/data/simpleimage1_cm.png +0 -0
- psychopy/tests/data/simpleimage1_deg.png +0 -0
- psychopy/tests/data/simpleimage1_degFlat.png +0 -0
- psychopy/tests/data/simpleimage1_degFlatPos.png +0 -0
- psychopy/tests/data/simpleimage1_height.png +0 -0
- psychopy/tests/data/simpleimage1_norm.png +0 -0
- psychopy/tests/data/simpleimage1_normAddBlend.png +0 -0
- psychopy/tests/data/simpleimage1_normHexbackground.png +0 -0
- psychopy/tests/data/simpleimage1_normNoShade.png +0 -0
- psychopy/tests/data/simpleimage1_pix.png +0 -0
- psychopy/tests/data/simpleimage1_stencil.png +0 -0
- psychopy/tests/data/test001EntryImporting.psyexp +66 -0
- psychopy/tests/data/testMovie.mp4 +0 -0
- psychopy/tests/data/test_circle_radius_.1height.png +0 -0
- psychopy/tests/data/test_circle_radius_.2height.png +0 -0
- psychopy/tests/data/test_circle_radius_10pix.png +0 -0
- psychopy/tests/data/test_circle_radius_20pix.png +0 -0
- psychopy/tests/data/test_form_combinations_choice_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_choice_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_choice_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_choice_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_description_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_description_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_description_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_description_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_free text_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_free text_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_free text_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_free text_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_heading_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_heading_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_heading_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_heading_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_radio_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_radio_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_radio_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_radio_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_rating_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_rating_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_rating_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_rating_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_slider_bigItem.png +0 -0
- psychopy/tests/data/test_form_combinations_slider_bigItemOverflow.png +0 -0
- psychopy/tests/data/test_form_combinations_slider_bigResp.png +0 -0
- psychopy/tests/data/test_form_combinations_slider_bigRespOverflow.png +0 -0
- psychopy/tests/data/test_get_resources/blue.png +0 -0
- psychopy/tests/data/test_get_resources/groupA.csv +3 -0
- psychopy/tests/data/test_get_resources/groupB.csv +3 -0
- psychopy/tests/data/test_get_resources/groups.csv +3 -0
- psychopy/tests/data/test_get_resources/handledbyrm_constrloop.psyexp +143 -0
- psychopy/tests/data/test_get_resources/handledbyrm_noloop.psyexp +114 -0
- psychopy/tests/data/test_get_resources/handledbyrm_recurloop.psyexp +138 -0
- psychopy/tests/data/test_get_resources/handledbyrm_strloop.psyexp +126 -0
- psychopy/tests/data/test_get_resources/handledbystatic_constrloop.psyexp +141 -0
- psychopy/tests/data/test_get_resources/handledbystatic_noloop.psyexp +112 -0
- psychopy/tests/data/test_get_resources/handledbystatic_recurloop.psyexp +136 -0
- psychopy/tests/data/test_get_resources/handledbystatic_strloop.psyexp +124 -0
- psychopy/tests/data/test_get_resources/unhandled_constrloop.psyexp +128 -0
- psychopy/tests/data/test_get_resources/unhandled_noloop.psyexp +99 -0
- psychopy/tests/data/test_get_resources/unhandled_recurloop.psyexp +123 -0
- psychopy/tests/data/test_get_resources/unhandled_strloop.psyexp +111 -0
- psychopy/tests/data/test_get_resources/white.png +0 -0
- psychopy/tests/data/test_get_resources/yellow.png +0 -0
- psychopy/tests/data/test_image_aspect_default_None.png +0 -0
- psychopy/tests/data/test_image_aspect_default_xFull_yNone.png +0 -0
- psychopy/tests/data/test_image_aspect_default_xNone_yFull.png +0 -0
- psychopy/tests/data/test_image_aspect_default_xNone_yNone.png +0 -0
- psychopy/tests/data/test_image_flip_anchor_horiz.png +0 -0
- psychopy/tests/data/test_image_flip_anchor_vert.png +0 -0
- psychopy/tests/data/test_loops/testLoopsBlocks.psyexp +161 -0
- psychopy/tests/data/test_loops/testStaircase.psyexp +105 -0
- psychopy/tests/data/test_loops/test_current_loop_attr.psyexp +185 -0
- psychopy/tests/data/test_panorama/panoramaTestImage.png +0 -0
- psychopy/tests/data/test_panorama/panoramaTestImage.svg +1 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_-0.3.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_0.3.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_-0.3.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_0.3.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_-1.0.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_1.0.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_-0.3.png +0 -0
- psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_0.3.png +0 -0
- psychopy/tests/data/test_resources.psyexp +491 -0
- psychopy/tests/data/test_session/outside_root/externalExp.psyexp +116 -0
- psychopy/tests/data/test_session/root/annotation/annotation.psyexp +161 -0
- psychopy/tests/data/test_session/root/error/error.psyexp +93 -0
- psychopy/tests/data/test_session/root/exp1/exp1.psyexp +133 -0
- psychopy/tests/data/test_session/root/exp2/exp2.psyexp +133 -0
- psychopy/tests/data/test_session/root/frameRate/frameRate.psyexp +114 -0
- psychopy/tests/data/test_session/root/invUseVersion/invUseVersion.psyexp +133 -0
- psychopy/tests/data/test_session/root/testClockFormat/testClockFormat.psyexp +122 -0
- psychopy/tests/data/test_session/root/testCtrls/testCtrls.psyexp +115 -0
- psychopy/tests/data/test_session/root/testEditExpInfo/testEditExpInfo.psyexp +135 -0
- psychopy/tests/data/test_session/root/testNamedButtonBox/testNamedButtonBox.psyexp +145 -0
- psychopy/tests/data/test_slider_horiz_accute_horiz.png +0 -0
- psychopy/tests/data/test_slider_horiz_accute_vert.png +0 -0
- psychopy/tests/data/test_slider_horiz_horiz.png +0 -0
- psychopy/tests/data/test_slider_horiz_obtuse_horiz.png +0 -0
- psychopy/tests/data/test_slider_horiz_obtuse_vert.png +0 -0
- psychopy/tests/data/test_slider_horiz_vert.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_blanks.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_clustered.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_morelabels.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_morelabelsclustered.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_moreticks.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_moreticksclustered.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_nolabels.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_noticks.png +0 -0
- psychopy/tests/data/test_slider_ticklabelloc_simple.png +0 -0
- psychopy/tests/data/test_slider_triangle_horiz_False_flip_False.png +0 -0
- psychopy/tests/data/test_slider_triangle_horiz_False_flip_True.png +0 -0
- psychopy/tests/data/test_slider_triangle_horiz_True_flip_False.png +0 -0
- psychopy/tests/data/test_slider_triangle_horiz_True_flip_True.png +0 -0
- psychopy/tests/data/test_static_component_script.psyexp +76 -0
- psychopy/tests/data/test_win_bg_large_256_default.png +0 -0
- psychopy/tests/data/test_win_bg_large_500_default.png +0 -0
- psychopy/tests/data/test_win_bg_small_200_default.png +0 -0
- psychopy/tests/data/test_win_bg_small_256_default.png +0 -0
- psychopy/tests/data/test_win_bg_tall_200_default.png +0 -0
- psychopy/tests/data/test_win_bg_tall_256_default.png +0 -0
- psychopy/tests/data/test_win_bg_tall_500_default.png +0 -0
- psychopy/tests/data/test_win_bg_tall_fill_default.png +0 -0
- psychopy/tests/data/test_win_bg_wide_200_default.png +0 -0
- psychopy/tests/data/test_win_bg_wide_256_default.png +0 -0
- psychopy/tests/data/test_win_bg_wide_500_default.png +0 -0
- psychopy/tests/data/test_win_bg_wide_fill_default.png +0 -0
- psychopy/tests/data/test_win_bgcolor_blue.png +0 -0
- psychopy/tests/data/test_win_bgcolor_green.png +0 -0
- psychopy/tests/data/test_win_bgcolor_red.png +0 -0
- psychopy/tests/data/testimage.jpg +0 -0
- psychopy/tests/data/testimagegray.jpg +0 -0
- psychopy/tests/data/testpixels.png +0 -0
- psychopy/tests/data/testwedges.png +0 -0
- psychopy/tests/data/text1_cm.png +0 -0
- psychopy/tests/data/text1_deg.png +0 -0
- psychopy/tests/data/text1_degFlat.png +0 -0
- psychopy/tests/data/text1_degFlatPos.png +0 -0
- psychopy/tests/data/text1_height.png +0 -0
- psychopy/tests/data/text1_norm.png +0 -0
- psychopy/tests/data/text1_normAddBlend.png +0 -0
- psychopy/tests/data/text1_normAddBlend_local.png +0 -0
- psychopy/tests/data/text1_normHexbackground.png +0 -0
- psychopy/tests/data/text1_normHexbackground_local.png +0 -0
- psychopy/tests/data/text1_normNoShade.png +0 -0
- psychopy/tests/data/text1_norm_local.png +0 -0
- psychopy/tests/data/text1_pix.png +0 -0
- psychopy/tests/data/text1_stencil.png +0 -0
- psychopy/tests/data/text2_cm.png +0 -0
- psychopy/tests/data/text2_deg.png +0 -0
- psychopy/tests/data/text2_degFlat.png +0 -0
- psychopy/tests/data/text2_degFlatPos.png +0 -0
- psychopy/tests/data/text2_height.png +0 -0
- psychopy/tests/data/text2_norm.png +0 -0
- psychopy/tests/data/text2_normNoShade.png +0 -0
- psychopy/tests/data/text2_pix.png +0 -0
- psychopy/tests/data/text2_stencil.png +0 -0
- psychopy/tests/data/textbox_charcolors_default_roygbiv.png +0 -0
- psychopy/tests/data/textbox_charcolors_default_white_hello_black_there.png +0 -0
- psychopy/tests/data/textbox_default_align_bottom_center.png +0 -0
- psychopy/tests/data/textbox_default_align_bottom_left.png +0 -0
- psychopy/tests/data/textbox_default_align_bottom_right.png +0 -0
- psychopy/tests/data/textbox_default_align_center.png +0 -0
- psychopy/tests/data/textbox_default_align_center_center.png +0 -0
- psychopy/tests/data/textbox_default_align_center_left.png +0 -0
- psychopy/tests/data/textbox_default_align_center_right.png +0 -0
- psychopy/tests/data/textbox_default_align_centre.png +0 -0
- psychopy/tests/data/textbox_default_align_centre_centre.png +0 -0
- psychopy/tests/data/textbox_default_align_more_than_two_words.png +0 -0
- psychopy/tests/data/textbox_default_align_someword.png +0 -0
- psychopy/tests/data/textbox_default_align_top_center.png +0 -0
- psychopy/tests/data/textbox_default_align_top_left.png +0 -0
- psychopy/tests/data/textbox_default_align_top_right.png +0 -0
- psychopy/tests/data/textbox_default_colors_WOB.png +0 -0
- psychopy/tests/data/textbox_default_colors_exemplar1.png +0 -0
- psychopy/tests/data/textbox_default_colors_exemplar2.png +0 -0
- psychopy/tests/data/textbox_default_colors_exemplar3.png +0 -0
- psychopy/tests/data/textbox_default_colors_tyke1.png +0 -0
- psychopy/tests/data/textbox_default_colors_tyke2.png +0 -0
- psychopy/tests/data/textbox_default_colors_tyke3.png +0 -0
- psychopy/tests/data/textbox_default_cutoff_top.png +0 -0
- psychopy/tests/data/textbox_default_exemplar_1.png +0 -0
- psychopy/tests/data/textbox_default_exemplar_2.png +0 -0
- psychopy/tests/data/textbox_default_exemplar_3.png +0 -0
- psychopy/tests/data/textbox_default_exemplar_4.png +0 -0
- psychopy/tests/data/textbox_default_tyke_1.png +0 -0
- psychopy/tests/data/textbox_default_tyke_2.png +0 -0
- psychopy/tests/data/textbox_typing_blank.png +0 -0
- psychopy/tests/data/textbox_typing_longKoeran.png +0 -0
- psychopy/tests/data/textbox_typing_longWord.png +0 -0
- psychopy/tests/data/textbox_typing_navDel.png +0 -0
- psychopy/tests/data/textbox_typing_navLR.png +0 -0
- psychopy/tests/data/textbox_typing_newline.png +0 -0
- psychopy/tests/data/textbox_typing_pangram.png +0 -0
- psychopy/tests/data/textbox_uax14_align_bottom_center.png +0 -0
- psychopy/tests/data/textbox_uax14_align_bottom_left.png +0 -0
- psychopy/tests/data/textbox_uax14_align_bottom_right.png +0 -0
- psychopy/tests/data/textbox_uax14_align_center.png +0 -0
- psychopy/tests/data/textbox_uax14_align_center_center.png +0 -0
- psychopy/tests/data/textbox_uax14_align_center_left.png +0 -0
- psychopy/tests/data/textbox_uax14_align_center_right.png +0 -0
- psychopy/tests/data/textbox_uax14_align_centre.png +0 -0
- psychopy/tests/data/textbox_uax14_align_centre_centre.png +0 -0
- psychopy/tests/data/textbox_uax14_align_more_than_two_words.png +0 -0
- psychopy/tests/data/textbox_uax14_align_someword.png +0 -0
- psychopy/tests/data/textbox_uax14_align_top_center.png +0 -0
- psychopy/tests/data/textbox_uax14_align_top_left.png +0 -0
- psychopy/tests/data/textbox_uax14_align_top_right.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_WOB.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_exemplar1.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_exemplar2.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_exemplar3.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_tyke1.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_tyke2.png +0 -0
- psychopy/tests/data/textbox_uax14_colors_tyke3.png +0 -0
- psychopy/tests/data/textbox_uax14_cutoff_top.png +0 -0
- psychopy/tests/data/textbox_uax14_exemplar_1.png +0 -0
- psychopy/tests/data/textbox_uax14_exemplar_2.png +0 -0
- psychopy/tests/data/textbox_uax14_exemplar_3.png +0 -0
- psychopy/tests/data/textbox_uax14_exemplar_4.png +0 -0
- psychopy/tests/data/textbox_uax14_tyke_1.png +0 -0
- psychopy/tests/data/textbox_uax14_tyke_2.png +0 -0
- psychopy/tests/data/trialTypes.csv +1 -0
- psychopy/tests/data/trialTypes.docx +0 -0
- psychopy/tests/data/trialTypes.pkl +0 -0
- psychopy/tests/data/trialTypes.tsv +7 -0
- psychopy/tests/data/trialTypes.xls +0 -0
- psychopy/tests/data/trialTypes.xlsx +0 -0
- psychopy/tests/data/trialTypes_eu.csv +7 -0
- psychopy/tests/data/trialsBlankCols.xlsx +0 -0
- psychopy/tests/data/wedge1_cm.png +0 -0
- psychopy/tests/data/wedge1_deg.png +0 -0
- psychopy/tests/data/wedge1_degFlat.png +0 -0
- psychopy/tests/data/wedge1_degFlatPos.png +0 -0
- psychopy/tests/data/wedge1_height.png +0 -0
- psychopy/tests/data/wedge1_norm.png +0 -0
- psychopy/tests/data/wedge1_normAddBlend.png +0 -0
- psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
- psychopy/tests/data/wedge1_normHexbackground.png +0 -0
- psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
- psychopy/tests/data/wedge1_normNoShade.png +0 -0
- psychopy/tests/data/wedge1_norm_local.png +0 -0
- psychopy/tests/data/wedge1_pix.png +0 -0
- psychopy/tests/data/wedge1_stencil.png +0 -0
- psychopy/tests/data/wedge2_cm.png +0 -0
- psychopy/tests/data/wedge2_deg.png +0 -0
- psychopy/tests/data/wedge2_degFlat.png +0 -0
- psychopy/tests/data/wedge2_degFlatPos.png +0 -0
- psychopy/tests/data/wedge2_height.png +0 -0
- psychopy/tests/data/wedge2_norm.png +0 -0
- psychopy/tests/data/wedge2_normAddBlend.png +0 -0
- psychopy/tests/data/wedge2_normHexbackground.png +0 -0
- psychopy/tests/data/wedge2_normNoShade.png +0 -0
- psychopy/tests/data/wedge2_pix.png +0 -0
- psychopy/tests/data/wedge2_stencil.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-1_1_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-1_1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-2_2_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale-2_2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale1_-1_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale1_-1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale1_1_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale1_1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale2_-2_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale2_-2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale2_2_pos-0.4_0.png +0 -0
- psychopy/tests/data/winScalePos_ori0_scale2_2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale-1_-1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale-1_1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale-2_-2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale-2_2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale1_-1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale1_1_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale2_-2_pos0_0.png +0 -0
- psychopy/tests/data/winScalePos_ori45_scale2_2_pos0_0.png +0 -0
- psychopy/tests/doc/run_btn.png +0 -0
- psychopy/tests/doc/test-suite-bugs.jpg +0 -0
- psychopy/tests/doc/user-bugs.jpg +0 -0
- psychopy/tests/dummy_xorg.conf +21 -0
- psychopy/tests/run.py +50 -0
- psychopy/tests/test_Installation.py +38 -0
- psychopy/tests/test_alerts/test_alerts.py +37 -0
- psychopy/tests/test_alerts/test_alerttools.py +131 -0
- psychopy/tests/test_app/__init__.py +0 -0
- psychopy/tests/test_app/conftest.py +41 -0
- psychopy/tests/test_app/test_builder/__init__.py +0 -0
- psychopy/tests/test_app/test_builder/test_BuilderFrame.py +151 -0
- psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +135 -0
- psychopy/tests/test_app/test_builder/test_ComponentDialogs.py +98 -0
- psychopy/tests/test_app/test_runner/__init__.py +0 -0
- psychopy/tests/test_app/test_runner/test_RunnerFrame.py +166 -0
- psychopy/tests/test_app/test_speed.py +144 -0
- psychopy/tests/test_app/test_themes/test_icons.py +79 -0
- psychopy/tests/test_codecov.py +29 -0
- psychopy/tests/test_colors/test_Color.py +41 -0
- psychopy/tests/test_data/dataPrev.xlsx +0 -0
- psychopy/tests/test_data/test_ExperimentHandler.py +171 -0
- psychopy/tests/test_data/test_MultiStairHandler.py +174 -0
- psychopy/tests/test_data/test_StairHandlers.py +1241 -0
- psychopy/tests/test_data/test_TrialHandler.py +335 -0
- psychopy/tests/test_data/test_TrialHandler2.py +408 -0
- psychopy/tests/test_data/test_TrialHandlerExt.py +359 -0
- psychopy/tests/test_data/test_fitFunctions.py +101 -0
- psychopy/tests/test_data/test_utils.py +169 -0
- psychopy/tests/test_data/test_xlsx.py +92 -0
- psychopy/tests/test_demos/test_builder_demos.py +52 -0
- psychopy/tests/test_experiment/__init__.py +0 -0
- psychopy/tests/test_experiment/known_py_diffs.txt +47 -0
- psychopy/tests/test_experiment/needs_wx/__init__.py +0 -0
- psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +13730 -0
- psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +130 -0
- psychopy/tests/test_experiment/needs_wx/test_Experiment.py +406 -0
- psychopy/tests/test_experiment/needs_wx/test_components.py +170 -0
- psychopy/tests/test_experiment/test_component_compile_js.py +59 -0
- psychopy/tests/test_experiment/test_component_compile_python.py +165 -0
- psychopy/tests/test_experiment/test_components/__init__.py +1 -0
- psychopy/tests/test_experiment/test_components/test_ButtonBox.py +216 -0
- psychopy/tests/test_experiment/test_components/test_Code.py +82 -0
- psychopy/tests/test_experiment/test_components/test_Image.py +24 -0
- psychopy/tests/test_experiment/test_components/test_Mouse.py +149 -0
- psychopy/tests/test_experiment/test_components/test_Polygon.py +81 -0
- psychopy/tests/test_experiment/test_components/test_ResourceManager.py +60 -0
- psychopy/tests/test_experiment/test_components/test_Settings.py +82 -0
- psychopy/tests/test_experiment/test_components/test_Static.py +60 -0
- psychopy/tests/test_experiment/test_components/test_all_components.py +162 -0
- psychopy/tests/test_experiment/test_components/test_base_components.py +323 -0
- psychopy/tests/test_experiment/test_experiment.py +103 -0
- psychopy/tests/test_experiment/test_loops.py +95 -0
- psychopy/tests/test_experiment/test_params.py +350 -0
- psychopy/tests/test_experiment/test_py2js.py +230 -0
- psychopy/tests/test_experiment/test_routines/__init__.py +1 -0
- psychopy/tests/test_experiment/test_routines/test_EyetrackerCalibrationRoutine.py +9 -0
- psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +9 -0
- psychopy/tests/test_experiment/test_routines/test_all_routines.py +23 -0
- psychopy/tests/test_experiment/test_routines/test_base_routine.py +154 -0
- psychopy/tests/test_experiment/test_routines/test_standalone_routines.py +36 -0
- psychopy/tests/test_gui/test_DlgFromDictQt.py +82 -0
- psychopy/tests/test_gui/test_DlgFromDictWx.py +82 -0
- psychopy/tests/test_hardware/__init__.py +0 -0
- psychopy/tests/test_hardware/test_CRS_BitsSharp.py +67 -0
- psychopy/tests/test_hardware/test_CRS_bitsShaders.py +110 -0
- psychopy/tests/test_hardware/test_device_manager.py +36 -0
- psychopy/tests/test_hardware/test_emulator.py +106 -0
- psychopy/tests/test_hardware/test_gammasci.py +35 -0
- psychopy/tests/test_hardware/test_keyboard.py +73 -0
- psychopy/tests/test_hardware/test_keyboard_events.py +362 -0
- psychopy/tests/test_hardware/test_ports.py +151 -0
- psychopy/tests/test_iohub/__init__.py +1 -0
- psychopy/tests/test_iohub/test_computer.py +124 -0
- psychopy/tests/test_iohub/test_event_get_and_clear.py +94 -0
- psychopy/tests/test_iohub/test_keyboard.py +91 -0
- psychopy/tests/test_iohub/test_launch.py +19 -0
- psychopy/tests/test_iohub/testutil.py +54 -0
- psychopy/tests/test_liaison/test_Liaison.py +228 -0
- psychopy/tests/test_misc/__init__.py +0 -0
- psychopy/tests/test_misc/memory_usage.py +164 -0
- psychopy/tests/test_misc/test_GammaFun.py +15 -0
- psychopy/tests/test_misc/test_clock.py +61 -0
- psychopy/tests/test_misc/test_color.py +203 -0
- psychopy/tests/test_misc/test_core.py +446 -0
- psychopy/tests/test_misc/test_event.py +307 -0
- psychopy/tests/test_misc/test_info.py +18 -0
- psychopy/tests/test_misc/test_layout.py +49 -0
- psychopy/tests/test_misc/test_locale.py +38 -0
- psychopy/tests/test_misc/test_web.py +22 -0
- psychopy/tests/test_monitors/test_monitors.py +91 -0
- psychopy/tests/test_preferences/__init__.py +0 -0
- psychopy/tests/test_preferences/test_prefs.py +28 -0
- psychopy/tests/test_session/test_Session.py +253 -0
- psychopy/tests/test_sound/__init__.py +0 -0
- psychopy/tests/test_sound/test_audioclip.py +331 -0
- psychopy/tests/test_sound/test_hamming.py +45 -0
- psychopy/tests/test_sound/test_microphone.py +217 -0
- psychopy/tests/test_sound/test_sound.py +67 -0
- psychopy/tests/test_sound/test_sound_pygame.py +67 -0
- psychopy/tests/test_tools/test_animationtools.py +51 -0
- psychopy/tests/test_tools/test_arraytools.py +130 -0
- psychopy/tests/test_tools/test_attributetools.py +257 -0
- psychopy/tests/test_tools/test_colorspacetools.py +149 -0
- psychopy/tests/test_tools/test_environmenttools.py +52 -0
- psychopy/tests/test_tools/test_fileerrortools.py +97 -0
- psychopy/tests/test_tools/test_filetools.py +104 -0
- psychopy/tests/test_tools/test_imagetools.py +57 -0
- psychopy/tests/test_tools/test_mathtools.py +688 -0
- psychopy/tests/test_tools/test_stringtools.py +143 -0
- psychopy/tests/test_tools/test_versionchooser.py +178 -0
- psychopy/tests/test_tools/test_viewtools.py +67 -0
- psychopy/tests/test_visual/__init__.py +0 -0
- psychopy/tests/test_visual/measure_parity.py +243 -0
- psychopy/tests/test_visual/test_all_stimuli.py +752 -0
- psychopy/tests/test_visual/test_basevisual.py +534 -0
- psychopy/tests/test_visual/test_brush.py +68 -0
- psychopy/tests/test_visual/test_button.py +15 -0
- psychopy/tests/test_visual/test_circle.py +66 -0
- psychopy/tests/test_visual/test_contains_overlaps.py +242 -0
- psychopy/tests/test_visual/test_custommouse.py +47 -0
- psychopy/tests/test_visual/test_dots.py +155 -0
- psychopy/tests/test_visual/test_form.py +414 -0
- psychopy/tests/test_visual/test_framepacking.py +41 -0
- psychopy/tests/test_visual/test_gamma.py +152 -0
- psychopy/tests/test_visual/test_glfw_backend.py +14 -0
- psychopy/tests/test_visual/test_image.py +218 -0
- psychopy/tests/test_visual/test_panorama.py +41 -0
- psychopy/tests/test_visual/test_progress.py +96 -0
- psychopy/tests/test_visual/test_projections.py +215 -0
- psychopy/tests/test_visual/test_projections_interactive.py +163 -0
- psychopy/tests/test_visual/test_ratingScale.py +299 -0
- psychopy/tests/test_visual/test_roi.py +99 -0
- psychopy/tests/test_visual/test_shape.py +23 -0
- psychopy/tests/test_visual/test_slider.py +328 -0
- psychopy/tests/test_visual/test_target.py +35 -0
- psychopy/tests/test_visual/test_textbox.py +474 -0
- psychopy/tests/test_visual/test_winFlipTiming.py +95 -0
- psychopy/tests/test_visual/test_winScalePos.py +77 -0
- psychopy/tests/test_visual/test_window.py +107 -0
- psychopy/tests/utils.py +318 -0
- psychopy/tools/LineBreak.txt +3597 -0
- psychopy/tools/__init__.py +9 -0
- psychopy/tools/animationtools.py +49 -0
- psychopy/tools/apptools.py +32 -0
- psychopy/tools/arraytools.py +514 -0
- psychopy/tools/attributetools.py +205 -0
- psychopy/tools/audiotools.py +358 -0
- psychopy/tools/colorspacetools.py +715 -0
- psychopy/tools/coordinatetools.py +104 -0
- psychopy/tools/environmenttools.py +62 -0
- psychopy/tools/fileerrortools.py +54 -0
- psychopy/tools/filetools.py +406 -0
- psychopy/tools/fontmanager.py +1041 -0
- psychopy/tools/gltools.py +4959 -0
- psychopy/tools/imagetools.py +65 -0
- psychopy/tools/linebreak.py +322 -0
- psychopy/tools/mathtools.py +4040 -0
- psychopy/tools/monitorunittools.py +270 -0
- psychopy/tools/movietools.py +1071 -0
- psychopy/tools/pkgtools.py +601 -0
- psychopy/tools/plottools.py +25 -0
- psychopy/tools/rifttools.py +76 -0
- psychopy/tools/stereotools.py +9 -0
- psychopy/tools/stimulustools.py +28 -0
- psychopy/tools/stringtools.py +421 -0
- psychopy/tools/systemtools.py +1331 -0
- psychopy/tools/typetools.py +52 -0
- psychopy/tools/unittools.py +16 -0
- psychopy/tools/versionchooser.py +602 -0
- psychopy/tools/viewtools.py +1084 -0
- psychopy/tools/wizard.py +804 -0
- psychopy/visual/__init__.py +124 -0
- psychopy/visual/aperture.py +330 -0
- psychopy/visual/backends/__init__.py +77 -0
- psychopy/visual/backends/_base.py +481 -0
- psychopy/visual/backends/gamma.py +349 -0
- psychopy/visual/backends/glfwbackend.py +28 -0
- psychopy/visual/backends/pygamebackend.py +343 -0
- psychopy/visual/backends/pygletbackend.py +920 -0
- psychopy/visual/basevisual.py +1952 -0
- psychopy/visual/brush.py +204 -0
- psychopy/visual/bufferimage.py +231 -0
- psychopy/visual/button.py +206 -0
- psychopy/visual/circle.py +173 -0
- psychopy/visual/custommouse.py +263 -0
- psychopy/visual/dot.py +668 -0
- psychopy/visual/dropdown.py +165 -0
- psychopy/visual/elementarray.py +728 -0
- psychopy/visual/filters.py +419 -0
- psychopy/visual/form.py +1180 -0
- psychopy/visual/globalVars.py +24 -0
- psychopy/visual/grating.py +501 -0
- psychopy/visual/helpers.py +334 -0
- psychopy/visual/image.py +440 -0
- psychopy/visual/line.py +238 -0
- psychopy/visual/movie.py +12 -0
- psychopy/visual/movie2.py +829 -0
- psychopy/visual/movie3.py +599 -0
- psychopy/visual/movies/__init__.py +850 -0
- psychopy/visual/movies/frame.py +255 -0
- psychopy/visual/movies/metadata.py +242 -0
- psychopy/visual/movies/players/__init__.py +62 -0
- psychopy/visual/movies/players/_base.py +364 -0
- psychopy/visual/movies/players/ffpyplayer_player.py +1399 -0
- psychopy/visual/nnlvs.py +827 -0
- psychopy/visual/noise.py +34 -0
- psychopy/visual/panorama.py +313 -0
- psychopy/visual/patch.py +29 -0
- psychopy/visual/pie.py +248 -0
- psychopy/visual/polygon.py +229 -0
- psychopy/visual/progress.py +313 -0
- psychopy/visual/radial.py +33 -0
- psychopy/visual/ratingscale.py +1424 -0
- psychopy/visual/rect.py +204 -0
- psychopy/visual/rift.py +2654 -0
- psychopy/visual/roi.py +141 -0
- psychopy/visual/secondorder.py +29 -0
- psychopy/visual/shaders.py +453 -0
- psychopy/visual/shape.py +701 -0
- psychopy/visual/simpleimage.py +303 -0
- psychopy/visual/slider.py +1169 -0
- psychopy/visual/stim3d.py +2712 -0
- psychopy/visual/target.py +278 -0
- psychopy/visual/text.py +759 -0
- psychopy/visual/textbox/__init__.py +1280 -0
- psychopy/visual/textbox/fontmanager.py +574 -0
- psychopy/visual/textbox/parsedtext.py +317 -0
- psychopy/visual/textbox/textgrid.py +278 -0
- psychopy/visual/textbox/textureatlas.py +248 -0
- psychopy/visual/textbox2/__init__.py +4 -0
- psychopy/visual/textbox2/textbox2.py +1817 -0
- psychopy/visual/vlcmoviestim.py +1294 -0
- psychopy/visual/window.py +3706 -0
- psychopy/visual/windowframepack.py +86 -0
- psychopy/visual/windowwarp.py +457 -0
- psychopy/voicekey/__init__.py +684 -0
- psychopy/voicekey/demo_vks.py +12 -0
- psychopy/voicekey/labjack_vks.py +50 -0
- psychopy/voicekey/parallel_vks.py +44 -0
- psychopy/voicekey/signal.py +42 -0
- psychopy/voicekey/vk_tools.py +280 -0
- psychopy/web.py +286 -0
- psychopy-2024.1.0.dist-info/METADATA +154 -0
- psychopy-2024.1.0.dist-info/RECORD +2950 -0
- psychopy-2024.1.0.dist-info/WHEEL +4 -0
- psychopy-2024.1.0.dist-info/entry_points.txt +3 -0
- psychopy-2024.1.0.dist-info/licenses/AUTHORS.md +138 -0
- psychopy-2024.1.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,2809 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Classes and functions for reading and writing camera streams.
|
|
4
|
+
|
|
5
|
+
A camera may be used to document participant responses on video or used by the
|
|
6
|
+
experimenter to create movie stimuli or instructions.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Part of the PsychoPy library
|
|
11
|
+
# Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2024 Open Science Tools Ltd.
|
|
12
|
+
# Distributed under the terms of the GNU General Public License (GPL).
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'VIDEO_DEVICE_ROOT_LINUX',
|
|
16
|
+
'CAMERA_UNKNOWN_VALUE',
|
|
17
|
+
'CAMERA_NULL_VALUE',
|
|
18
|
+
# 'CAMERA_MODE_VIDEO',
|
|
19
|
+
# 'CAMERA_MODE_CV',
|
|
20
|
+
# 'CAMERA_MODE_PHOTO',
|
|
21
|
+
'CAMERA_TEMP_FILE_VIDEO',
|
|
22
|
+
'CAMERA_TEMP_FILE_AUDIO',
|
|
23
|
+
'CAMERA_API_AVFOUNDATION',
|
|
24
|
+
'CAMERA_API_DIRECTSHOW',
|
|
25
|
+
'CAMERA_API_VIDEO4LINUX2',
|
|
26
|
+
'CAMERA_API_ANY',
|
|
27
|
+
'CAMERA_API_UNKNOWN',
|
|
28
|
+
'CAMERA_API_NULL',
|
|
29
|
+
'CAMERA_LIB_FFPYPLAYER',
|
|
30
|
+
'CAMERA_LIB_OPENCV',
|
|
31
|
+
'CAMERA_LIB_UNKNOWN',
|
|
32
|
+
'CAMERA_LIB_NULL',
|
|
33
|
+
'CameraError',
|
|
34
|
+
'CameraNotReadyError',
|
|
35
|
+
'CameraNotFoundError',
|
|
36
|
+
'CameraFormatNotSupportedError',
|
|
37
|
+
'CameraFrameRateNotSupportedError',
|
|
38
|
+
'CameraFrameSizeNotSupportedError',
|
|
39
|
+
'FormatNotFoundError',
|
|
40
|
+
'PlayerNotAvailableError',
|
|
41
|
+
'CameraInterfaceFFmpeg',
|
|
42
|
+
'CameraInterfaceOpenCV',
|
|
43
|
+
'Camera',
|
|
44
|
+
'CameraInfo',
|
|
45
|
+
'getCameras',
|
|
46
|
+
'getCameraDescriptions',
|
|
47
|
+
'getOpenCameras',
|
|
48
|
+
'closeAllOpenCameras',
|
|
49
|
+
'renderVideo'
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
import platform
|
|
53
|
+
import inspect
|
|
54
|
+
import os
|
|
55
|
+
import os.path
|
|
56
|
+
import sys
|
|
57
|
+
import math
|
|
58
|
+
import uuid
|
|
59
|
+
import threading
|
|
60
|
+
import queue
|
|
61
|
+
import time
|
|
62
|
+
import numpy as np
|
|
63
|
+
|
|
64
|
+
from psychopy.constants import NOT_STARTED
|
|
65
|
+
from psychopy.hardware import DeviceManager
|
|
66
|
+
from psychopy.visual.movies.frame import MovieFrame, NULL_MOVIE_FRAME_INFO
|
|
67
|
+
from psychopy.sound.microphone import Microphone
|
|
68
|
+
from psychopy.hardware.microphone import MicrophoneDevice
|
|
69
|
+
from psychopy.tools import systemtools as st
|
|
70
|
+
import psychopy.tools.movietools as movietools
|
|
71
|
+
import psychopy.logging as logging
|
|
72
|
+
from psychopy.localization import _translate
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ------------------------------------------------------------------------------
|
|
76
|
+
# Constants
|
|
77
|
+
#
|
|
78
|
+
|
|
79
|
+
VIDEO_DEVICE_ROOT_LINUX = '/dev'
|
|
80
|
+
CAMERA_UNKNOWN_VALUE = u'Unknown' # fields where we couldn't get a value
|
|
81
|
+
CAMERA_NULL_VALUE = u'Null' # fields where we couldn't get a value
|
|
82
|
+
# camera operating modes
|
|
83
|
+
# CAMERA_MODE_VIDEO = u'video'
|
|
84
|
+
# CAMERA_MODE_CV = u'cv'
|
|
85
|
+
# CAMERA_MODE_PHOTO = u'photo'
|
|
86
|
+
|
|
87
|
+
# camera status
|
|
88
|
+
CAMERA_STATUS_OK = 'ok'
|
|
89
|
+
CAMERA_STATUS_PAUSED = 'paused'
|
|
90
|
+
CAMERA_STATUS_EOF = 'eof'
|
|
91
|
+
|
|
92
|
+
# camera API flags, these specify which API camera settings were queried with
|
|
93
|
+
CAMERA_API_AVFOUNDATION = u'AVFoundation' # mac
|
|
94
|
+
CAMERA_API_DIRECTSHOW = u'DirectShow' # windows
|
|
95
|
+
CAMERA_API_VIDEO4LINUX2 = u'Video4Linux2' # linux
|
|
96
|
+
CAMERA_API_ANY = u'Any' # any API (OpenCV only)
|
|
97
|
+
CAMERA_API_UNKNOWN = u'Unknown' # unknown API
|
|
98
|
+
CAMERA_API_NULL = u'Null' # empty field
|
|
99
|
+
|
|
100
|
+
# camera libraries for playback nad recording
|
|
101
|
+
CAMERA_LIB_FFPYPLAYER = u'FFPyPlayer'
|
|
102
|
+
CAMERA_LIB_OPENCV = u'OpenCV'
|
|
103
|
+
CAMERA_LIB_UNKNOWN = u'Unknown'
|
|
104
|
+
CAMERA_LIB_NULL = u'Null'
|
|
105
|
+
|
|
106
|
+
# special values
|
|
107
|
+
CAMERA_FRAMERATE_NOMINAL_NTSC = '30.000030'
|
|
108
|
+
CAMERA_FRAMERATE_NTSC = 30.000030
|
|
109
|
+
|
|
110
|
+
# FourCC and pixel format mappings, mostly used with AVFoundation to determine
|
|
111
|
+
# the FFMPEG decoder which is most suitable for it. Please expand this if you
|
|
112
|
+
# know any more!
|
|
113
|
+
pixelFormatTbl = {
|
|
114
|
+
'yuvs': 'yuyv422', # 4:2:2
|
|
115
|
+
'420v': 'nv12', # 4:2:0
|
|
116
|
+
'2vuy': 'uyvy422' # QuickTime 4:2:2
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Camera standards to help with selection. Some standalone cameras sometimes
|
|
120
|
+
# support an insane number of formats, this will help narrow them down.
|
|
121
|
+
standardResolutions = {
|
|
122
|
+
'vga': (640, 480),
|
|
123
|
+
'svga': (800, 600),
|
|
124
|
+
'xga': (1024, 768),
|
|
125
|
+
'wxga': (1280, 768),
|
|
126
|
+
'wxga+': (1440, 900),
|
|
127
|
+
'sxga': (1280, 1024),
|
|
128
|
+
'wsxga+': (1680, 1050),
|
|
129
|
+
'uxga': (1600, 1200),
|
|
130
|
+
'wuxga': (1920, 1200),
|
|
131
|
+
'wqxga': (2560, 1600),
|
|
132
|
+
'wquxga': (3840, 2400),
|
|
133
|
+
'720p': (1280, 720), # also known as HD
|
|
134
|
+
'1080p': (1920, 1080),
|
|
135
|
+
'2160p': (3840, 2160),
|
|
136
|
+
'uhd': (3840, 2160),
|
|
137
|
+
'dci': (4096, 2160)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ------------------------------------------------------------------------------
|
|
142
|
+
# Exceptions
|
|
143
|
+
#
|
|
144
|
+
|
|
145
|
+
class CameraError(Exception):
|
|
146
|
+
"""Base class for errors around the camera."""
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class CameraNotReadyError(CameraError):
|
|
150
|
+
"""Camera is not ready."""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CameraNotFoundError(CameraError):
|
|
154
|
+
"""Raised when a camera cannot be found on the system."""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class CameraFormatNotSupportedError(CameraError):
|
|
158
|
+
"""Raised when a camera cannot use the settings requested by the user."""
|
|
159
|
+
|
|
160
|
+
class CameraFrameRateNotSupportedError(CameraFormatNotSupportedError):
|
|
161
|
+
"""Raised when a camera cannot use the frame rate settings requested by the
|
|
162
|
+
user."""
|
|
163
|
+
|
|
164
|
+
class CameraFrameSizeNotSupportedError(CameraFormatNotSupportedError):
|
|
165
|
+
"""Raised when a camera cannot use the frame size settings requested by the
|
|
166
|
+
user."""
|
|
167
|
+
|
|
168
|
+
class FormatNotFoundError(CameraError):
|
|
169
|
+
"""Cannot find a suitable pixel format for the camera."""
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class PlayerNotAvailableError(Exception):
|
|
173
|
+
"""Raised when a player object is not available but is required."""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ------------------------------------------------------------------------------
|
|
177
|
+
# Classes
|
|
178
|
+
#
|
|
179
|
+
|
|
180
|
+
class CameraInfo:
|
|
181
|
+
"""Information about a specific operating mode for a camera attached to the
|
|
182
|
+
system.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
index : int
|
|
187
|
+
Index of the camera. This is the enumeration for the camera which is
|
|
188
|
+
used to identify and select it by the `cameraLib`. This value may differ
|
|
189
|
+
between operating systems and the `cameraLib` being used.
|
|
190
|
+
name : str
|
|
191
|
+
Camera name retrieved by the OS. This may be a human-readable name
|
|
192
|
+
(i.e. DirectShow on Windows), an index on MacOS or a path (e.g.,
|
|
193
|
+
`/dev/video0` on Linux). If the `cameraLib` does not support this
|
|
194
|
+
feature, then this value will be generated.
|
|
195
|
+
frameSize : ArrayLike
|
|
196
|
+
Resolution of the frame `(w, h)` in pixels.
|
|
197
|
+
frameRate : ArrayLike
|
|
198
|
+
Allowable framerate for this camera mode.
|
|
199
|
+
pixelFormat : str
|
|
200
|
+
Pixel format for the stream. If `u'Null'`, then `codecFormat` is being
|
|
201
|
+
used to configure the camera.
|
|
202
|
+
codecFormat : str
|
|
203
|
+
Codec format for the stream. If `u'Null'`, then `pixelFormat` is being
|
|
204
|
+
used to configure the camera. Usually this value is used for high-def
|
|
205
|
+
stream formats.
|
|
206
|
+
cameraLib : str
|
|
207
|
+
Library used to access the camera. This can be either, 'ffpyplayer',
|
|
208
|
+
'opencv'.
|
|
209
|
+
cameraAPI : str
|
|
210
|
+
API used to access the camera. This relates to the external interface
|
|
211
|
+
being used by `cameraLib` to access the camera. This value can be:
|
|
212
|
+
'AVFoundation', 'DirectShow' or 'Video4Linux2'.
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
__slots__ = [
|
|
216
|
+
'_index',
|
|
217
|
+
'_name',
|
|
218
|
+
'_frameSize',
|
|
219
|
+
'_frameRate',
|
|
220
|
+
'_pixelFormat',
|
|
221
|
+
'_codecFormat',
|
|
222
|
+
'_cameraLib',
|
|
223
|
+
'_cameraAPI' # API in use, e.g. DirectShow on Windows
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
def __init__(self,
|
|
227
|
+
index=-1,
|
|
228
|
+
name=CAMERA_NULL_VALUE,
|
|
229
|
+
frameSize=(-1, -1),
|
|
230
|
+
frameRate=-1.0,
|
|
231
|
+
pixelFormat=CAMERA_UNKNOWN_VALUE,
|
|
232
|
+
codecFormat=CAMERA_UNKNOWN_VALUE,
|
|
233
|
+
cameraLib=CAMERA_NULL_VALUE,
|
|
234
|
+
cameraAPI=CAMERA_API_NULL):
|
|
235
|
+
|
|
236
|
+
self.index = index
|
|
237
|
+
self.name = name
|
|
238
|
+
self.frameSize = frameSize
|
|
239
|
+
self.frameRate = frameRate
|
|
240
|
+
self.pixelFormat = pixelFormat
|
|
241
|
+
self.codecFormat = codecFormat
|
|
242
|
+
self.cameraLib = cameraLib
|
|
243
|
+
self.cameraAPI = cameraAPI
|
|
244
|
+
|
|
245
|
+
def __repr__(self):
|
|
246
|
+
return (f"CameraInfo(index={repr(self.index)}, "
|
|
247
|
+
f"name={repr(self.name)}, "
|
|
248
|
+
f"frameSize={repr(self.frameSize)}, "
|
|
249
|
+
f"frameRate={self.frameRate}, "
|
|
250
|
+
f"pixelFormat={repr(self.pixelFormat)}, "
|
|
251
|
+
f"codecFormat={repr(self.codecFormat)}, "
|
|
252
|
+
f"cameraLib={repr(self.cameraLib)}, "
|
|
253
|
+
f"cameraAPI={repr(self.cameraAPI)})")
|
|
254
|
+
|
|
255
|
+
def __str__(self):
|
|
256
|
+
return self.description()
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def index(self):
|
|
260
|
+
"""Camera index (`int`). This is the enumerated index of this camera.
|
|
261
|
+
"""
|
|
262
|
+
return self._index
|
|
263
|
+
|
|
264
|
+
@index.setter
|
|
265
|
+
def index(self, value):
|
|
266
|
+
self._index = int(value)
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def name(self):
|
|
270
|
+
"""Camera name (`str`). This is the camera name retrieved by the OS.
|
|
271
|
+
"""
|
|
272
|
+
return self._name
|
|
273
|
+
|
|
274
|
+
@name.setter
|
|
275
|
+
def name(self, value):
|
|
276
|
+
self._name = str(value)
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def frameSize(self):
|
|
280
|
+
"""Resolution (w, h) in pixels (`ArrayLike` or `None`).
|
|
281
|
+
"""
|
|
282
|
+
return self._frameSize
|
|
283
|
+
|
|
284
|
+
@frameSize.setter
|
|
285
|
+
def frameSize(self, value):
|
|
286
|
+
if value is None:
|
|
287
|
+
self._frameSize = None
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
assert len(value) == 2, "Value for `frameSize` must have length 2."
|
|
291
|
+
assert all([isinstance(i, int) for i in value]), (
|
|
292
|
+
"Values for `frameSize` must be integers.")
|
|
293
|
+
|
|
294
|
+
self._frameSize = value
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def frameRate(self):
|
|
298
|
+
"""Frame rate (`float`) or range (`ArrayLike`).
|
|
299
|
+
|
|
300
|
+
Depends on the backend being used. If a range is provided, then the
|
|
301
|
+
first value is the maximum and the second value is the minimum frame
|
|
302
|
+
rate.
|
|
303
|
+
"""
|
|
304
|
+
return self._frameRate
|
|
305
|
+
|
|
306
|
+
@frameRate.setter
|
|
307
|
+
def frameRate(self, value):
|
|
308
|
+
# assert len(value) == 2, "Value for `frameRateRange` must have length 2."
|
|
309
|
+
# assert all([isinstance(i, int) for i in value]), (
|
|
310
|
+
# "Values for `frameRateRange` must be integers.")
|
|
311
|
+
# assert value[0] <= value[1], (
|
|
312
|
+
# "Value for `frameRateRange` must be `min` <= `max`.")
|
|
313
|
+
|
|
314
|
+
self._frameRate = value
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def pixelFormat(self):
|
|
318
|
+
"""Video pixel format (`str`). An empty string indicates this field is
|
|
319
|
+
not initialized.
|
|
320
|
+
"""
|
|
321
|
+
return self._pixelFormat
|
|
322
|
+
|
|
323
|
+
@pixelFormat.setter
|
|
324
|
+
def pixelFormat(self, value):
|
|
325
|
+
self._pixelFormat = str(value)
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def codecFormat(self):
|
|
329
|
+
"""Codec format, may be used instead of `pixelFormat` for some
|
|
330
|
+
configurations. Default is `''`.
|
|
331
|
+
"""
|
|
332
|
+
return self._codecFormat
|
|
333
|
+
|
|
334
|
+
@codecFormat.setter
|
|
335
|
+
def codecFormat(self, value):
|
|
336
|
+
self._codecFormat = str(value)
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def cameraLib(self):
|
|
340
|
+
"""Camera library these settings are targeted towards (`str`).
|
|
341
|
+
"""
|
|
342
|
+
return self._cameraLib
|
|
343
|
+
|
|
344
|
+
@cameraLib.setter
|
|
345
|
+
def cameraLib(self, value):
|
|
346
|
+
self._cameraLib = str(value)
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def cameraAPI(self):
|
|
350
|
+
"""Camera API in use to obtain this information (`str`).
|
|
351
|
+
"""
|
|
352
|
+
return self._cameraAPI
|
|
353
|
+
|
|
354
|
+
@cameraAPI.setter
|
|
355
|
+
def cameraAPI(self, value):
|
|
356
|
+
self._cameraAPI = str(value)
|
|
357
|
+
|
|
358
|
+
def frameSizeAsFormattedString(self):
|
|
359
|
+
"""Get image size as as formatted string.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
str
|
|
364
|
+
Size formatted as `'WxH'` (e.g. `'480x320'`).
|
|
365
|
+
|
|
366
|
+
"""
|
|
367
|
+
return '{width}x{height}'.format(
|
|
368
|
+
width=self.frameSize[0],
|
|
369
|
+
height=self.frameSize[1])
|
|
370
|
+
|
|
371
|
+
def description(self):
|
|
372
|
+
"""Get a description as a string.
|
|
373
|
+
|
|
374
|
+
For all backends, this value is guaranteed to be valid after the camera
|
|
375
|
+
has been opened. Some backends may be able to provide this information
|
|
376
|
+
before the camera is opened.
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
str
|
|
381
|
+
Description of the camera format as a human readable string.
|
|
382
|
+
|
|
383
|
+
"""
|
|
384
|
+
codecFormat = self._codecFormat
|
|
385
|
+
pixelFormat = self._pixelFormat
|
|
386
|
+
codec = codecFormat if not pixelFormat else pixelFormat
|
|
387
|
+
|
|
388
|
+
if self.frameSize is None:
|
|
389
|
+
frameSize = (-1, -1)
|
|
390
|
+
else:
|
|
391
|
+
frameSize = self.frameSize
|
|
392
|
+
|
|
393
|
+
return "[{name}] {width}x{height}@{frameRate}fps, {codec}".format(
|
|
394
|
+
#index=self.index,
|
|
395
|
+
name=self.name,
|
|
396
|
+
width=str(frameSize[0]),
|
|
397
|
+
height=str(frameSize[1]),
|
|
398
|
+
frameRate=str(self.frameRate),
|
|
399
|
+
codec=codec
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class CameraInterface:
|
|
404
|
+
"""Base class providing an interface with a camera attached to the system.
|
|
405
|
+
|
|
406
|
+
This interface handles the opening, closing, and reading of camera streams.
|
|
407
|
+
Subclasses provide a specific implementation for a camera interface.
|
|
408
|
+
|
|
409
|
+
Calls to any instance methods should be asynchronous and non-blocking,
|
|
410
|
+
returning immediately with the same data as before if no new frame data is
|
|
411
|
+
available. This is to ensure that the main thread is not blocked by the
|
|
412
|
+
camera interface and can continue to process other events.
|
|
413
|
+
|
|
414
|
+
Parameters
|
|
415
|
+
----------
|
|
416
|
+
device : Any
|
|
417
|
+
Camera device to open a stream with. The type of this value is platform
|
|
418
|
+
dependent. Calling `start()` will open a stream with this device.
|
|
419
|
+
Afterwards, `getRecentFrame()` can be called to get the most recent
|
|
420
|
+
frame from the camera.
|
|
421
|
+
|
|
422
|
+
"""
|
|
423
|
+
# default values for class variables, these are read-only and should not be
|
|
424
|
+
# changed at runtime
|
|
425
|
+
_cameraLib = u'Null'
|
|
426
|
+
_frameIndex = 0
|
|
427
|
+
_lastPTS = 0.0 # presentation timestamp of the last frame
|
|
428
|
+
_supportedPlatforms = ['linux', 'windows', 'darwin']
|
|
429
|
+
_device = None
|
|
430
|
+
_lastFrame = None
|
|
431
|
+
_isReady = False # `True` if the camera is 'hot' and yielding frames
|
|
432
|
+
|
|
433
|
+
def __init__(self, device):
|
|
434
|
+
self._device = device
|
|
435
|
+
self._mic = None
|
|
436
|
+
|
|
437
|
+
@staticmethod
|
|
438
|
+
def getCameras():
|
|
439
|
+
"""Get a list of devices this interface can open.
|
|
440
|
+
|
|
441
|
+
Returns
|
|
442
|
+
-------
|
|
443
|
+
list
|
|
444
|
+
List of objects which represent cameras that can be opened by this
|
|
445
|
+
interface. Pass any of these values to `device` to open a stream.
|
|
446
|
+
|
|
447
|
+
"""
|
|
448
|
+
return []
|
|
449
|
+
|
|
450
|
+
@property
|
|
451
|
+
def device(self):
|
|
452
|
+
"""Camera device this interface is using (`Any`).
|
|
453
|
+
"""
|
|
454
|
+
return self._device
|
|
455
|
+
|
|
456
|
+
@property
|
|
457
|
+
def frameCount(self):
|
|
458
|
+
"""Number of new frames read from the camera since initialization
|
|
459
|
+
(`int`).
|
|
460
|
+
"""
|
|
461
|
+
return self._frameCount
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def streamTime(self):
|
|
465
|
+
"""Current stream time in seconds (`float`). This time increases
|
|
466
|
+
monotonically from startup.
|
|
467
|
+
"""
|
|
468
|
+
return self._streamTime
|
|
469
|
+
|
|
470
|
+
def lastFrame(self):
|
|
471
|
+
"""The last frame read from the camera. If `None`, no frames have been
|
|
472
|
+
read yet.
|
|
473
|
+
"""
|
|
474
|
+
return self._lastFrame
|
|
475
|
+
|
|
476
|
+
def _assertMediaPlayer(self):
|
|
477
|
+
"""Assert that the media player is available.
|
|
478
|
+
|
|
479
|
+
Returns
|
|
480
|
+
-------
|
|
481
|
+
bool
|
|
482
|
+
`True` if the media player is available.
|
|
483
|
+
|
|
484
|
+
"""
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
def open(self):
|
|
488
|
+
"""Open the camera stream.
|
|
489
|
+
"""
|
|
490
|
+
pass
|
|
491
|
+
|
|
492
|
+
def isOpen(self):
|
|
493
|
+
"""Check if the camera stream is open.
|
|
494
|
+
|
|
495
|
+
Returns
|
|
496
|
+
-------
|
|
497
|
+
bool
|
|
498
|
+
`True` if the camera stream is open.
|
|
499
|
+
|
|
500
|
+
"""
|
|
501
|
+
return False
|
|
502
|
+
|
|
503
|
+
def enable(self):
|
|
504
|
+
"""Enable passing camera frames to the main thread.
|
|
505
|
+
"""
|
|
506
|
+
pass
|
|
507
|
+
|
|
508
|
+
def disable(self):
|
|
509
|
+
"""Disable passing camera frames to the main thread.
|
|
510
|
+
"""
|
|
511
|
+
pass
|
|
512
|
+
|
|
513
|
+
def close(self):
|
|
514
|
+
"""Close the camera stream.
|
|
515
|
+
"""
|
|
516
|
+
pass
|
|
517
|
+
|
|
518
|
+
def getMetadata(self):
|
|
519
|
+
"""Get metadata about the camera stream.
|
|
520
|
+
|
|
521
|
+
Returns
|
|
522
|
+
-------
|
|
523
|
+
dict
|
|
524
|
+
Dictionary containing metadata about the camera stream. Returns an
|
|
525
|
+
empty dictionary if no metadata is available.
|
|
526
|
+
|
|
527
|
+
"""
|
|
528
|
+
return {}
|
|
529
|
+
|
|
530
|
+
def _enqueueFrame(self):
|
|
531
|
+
"""Enqueue a frame from the camera stream.
|
|
532
|
+
"""
|
|
533
|
+
pass
|
|
534
|
+
|
|
535
|
+
def update(self):
|
|
536
|
+
"""Update the camera stream.
|
|
537
|
+
"""
|
|
538
|
+
pass
|
|
539
|
+
|
|
540
|
+
def getRecentFrame(self):
|
|
541
|
+
"""Get the most recent frame from the camera stream.
|
|
542
|
+
|
|
543
|
+
Returns
|
|
544
|
+
-------
|
|
545
|
+
numpy.ndarray
|
|
546
|
+
Most recent frame from the camera stream. Returns `None` if no
|
|
547
|
+
frames are available.
|
|
548
|
+
|
|
549
|
+
"""
|
|
550
|
+
return NULL_MOVIE_FRAME_INFO
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class CameraInterfaceFFmpeg(CameraInterface):
|
|
554
|
+
"""Camera interface using FFmpeg (ffpyplayer) to open and read camera
|
|
555
|
+
streams.
|
|
556
|
+
|
|
557
|
+
Parameters
|
|
558
|
+
----------
|
|
559
|
+
device : CameraInfo
|
|
560
|
+
Camera device to open a stream with. Calling `start()` will open a
|
|
561
|
+
stream with this device. Afterwards, `getRecentFrame()` can be called
|
|
562
|
+
to get the most recent frame from the camera.
|
|
563
|
+
mic : MicrophoneInterface or None
|
|
564
|
+
Microphone interface to use for audio recording. If `None`, no audio
|
|
565
|
+
recording is performed.
|
|
566
|
+
|
|
567
|
+
"""
|
|
568
|
+
_cameraLib = u'ffpyplayer'
|
|
569
|
+
|
|
570
|
+
def __init__(self, device, mic=None):
|
|
571
|
+
super().__init__(device=device)
|
|
572
|
+
|
|
573
|
+
self._bufferSecs = 0.5 # number of seconds to buffer
|
|
574
|
+
self._cameraInfo = device
|
|
575
|
+
self._mic = mic # microphone interface
|
|
576
|
+
self._frameQueue = queue.Queue()
|
|
577
|
+
self._enableEvent = threading.Event()
|
|
578
|
+
self._enableEvent.clear()
|
|
579
|
+
self._exitEvent = threading.Event()
|
|
580
|
+
self._exitEvent.clear()
|
|
581
|
+
self._syncBarrier = None
|
|
582
|
+
self._recordBarrier = None # created in `open()`
|
|
583
|
+
self._playerThread = None
|
|
584
|
+
|
|
585
|
+
def _assertMediaPlayer(self):
|
|
586
|
+
return self._playerThread is not None
|
|
587
|
+
|
|
588
|
+
def _getCameraInfo(self):
|
|
589
|
+
"""Get camera information in the format expected by FFmpeg.
|
|
590
|
+
"""
|
|
591
|
+
pass
|
|
592
|
+
|
|
593
|
+
def getCameras():
|
|
594
|
+
"""Get a list of devices this interface can open.
|
|
595
|
+
|
|
596
|
+
Returns
|
|
597
|
+
-------
|
|
598
|
+
list
|
|
599
|
+
List of objects which represent cameras that can be opened by this
|
|
600
|
+
interface. Pass any of these values to `device` to open a stream.
|
|
601
|
+
|
|
602
|
+
"""
|
|
603
|
+
global _cameraGetterFuncTbl
|
|
604
|
+
systemName = platform.system() # get the system name
|
|
605
|
+
|
|
606
|
+
# lookup the function for the given platform
|
|
607
|
+
getCamerasFunc = _cameraGetterFuncTbl.get(systemName, None)
|
|
608
|
+
if getCamerasFunc is None: # if unsupported
|
|
609
|
+
raise OSError(
|
|
610
|
+
"Cannot get cameras, unsupported platform '{}'.".format(
|
|
611
|
+
systemName))
|
|
612
|
+
|
|
613
|
+
return getCamerasFunc()
|
|
614
|
+
|
|
615
|
+
@property
|
|
616
|
+
def frameRate(self):
|
|
617
|
+
"""Frame rate of the camera stream (`float`).
|
|
618
|
+
"""
|
|
619
|
+
return self._cameraInfo.frameRate
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def frameSize(self):
|
|
623
|
+
"""Frame size of the camera stream (`tuple`).
|
|
624
|
+
"""
|
|
625
|
+
return self._cameraInfo.frameSize
|
|
626
|
+
|
|
627
|
+
@property
|
|
628
|
+
def framesWaiting(self):
|
|
629
|
+
"""Get the number of frames currently buffered (`int`).
|
|
630
|
+
|
|
631
|
+
Returns the number of frames which have been pulled from the stream and
|
|
632
|
+
are waiting to be processed. This value is decremented by calls to
|
|
633
|
+
`_enqueueFrame()`.
|
|
634
|
+
|
|
635
|
+
"""
|
|
636
|
+
return self._frameQueue.qsize()
|
|
637
|
+
|
|
638
|
+
def isOpen(self):
|
|
639
|
+
"""Check if the camera stream is open (`bool`).
|
|
640
|
+
"""
|
|
641
|
+
if self._playerThread is not None:
|
|
642
|
+
return self._playerThread.is_alive()
|
|
643
|
+
|
|
644
|
+
return False
|
|
645
|
+
|
|
646
|
+
def open(self):
|
|
647
|
+
"""Open the camera stream and begin decoding frames (if available).
|
|
648
|
+
|
|
649
|
+
The value of `lastFrame` will be updated as new frames from the camera
|
|
650
|
+
arrive.
|
|
651
|
+
|
|
652
|
+
"""
|
|
653
|
+
if self._playerThread is not None:
|
|
654
|
+
raise RuntimeError('Cannot open `MediaPlayer`, already opened.')
|
|
655
|
+
|
|
656
|
+
self._exitEvent.clear() # signal the thread to stop
|
|
657
|
+
|
|
658
|
+
def _frameGetterAsync(videoCapture, frameQueue, exitEvent, recordEvent,
|
|
659
|
+
warmUpBarrier, recordingBarrier, audioCapture):
|
|
660
|
+
"""Get frames from the camera stream asynchronously.
|
|
661
|
+
|
|
662
|
+
Parameters
|
|
663
|
+
----------
|
|
664
|
+
videoCapture : ffpyplayer.player.MediaPlayer
|
|
665
|
+
FFmpeg media player object. This object will be under direct
|
|
666
|
+
control of this function.
|
|
667
|
+
frameQueue : queue.Queue
|
|
668
|
+
Queue to put frames into. The queue has an unlimited size, so
|
|
669
|
+
be careful with memory use. This queue should be flushed when
|
|
670
|
+
camera thread is paused.
|
|
671
|
+
exitEvent : threading.Event
|
|
672
|
+
Event used to signal the thread to stop.
|
|
673
|
+
recordEvent : threading.Event
|
|
674
|
+
Event used to signal the thread to pass frames along to the main
|
|
675
|
+
thread.
|
|
676
|
+
warmUpBarrier : threading.Barrier
|
|
677
|
+
Barrier which is used hold until camera capture is ready.
|
|
678
|
+
recordingBarrier : threading.Barrier
|
|
679
|
+
Barrier which is used to synchronize audio and video recording.
|
|
680
|
+
This ensures that the audio device is ready before buffering
|
|
681
|
+
frames captured by the camera.
|
|
682
|
+
audioCapture : psychopy.sound.Microphone or None
|
|
683
|
+
Microphone object to use for audio capture. This will be used to
|
|
684
|
+
synchronize the audio and video streams. If `None`, no audio
|
|
685
|
+
will be captured.
|
|
686
|
+
|
|
687
|
+
"""
|
|
688
|
+
# warmup the stream, wait for metadata
|
|
689
|
+
ptsStart = 0.0 # may be used in the future
|
|
690
|
+
while True:
|
|
691
|
+
frame, val = videoCapture.get_frame()
|
|
692
|
+
if frame is not None:
|
|
693
|
+
ptsStart = videoCapture.get_pts()
|
|
694
|
+
break
|
|
695
|
+
|
|
696
|
+
time.sleep(0.001)
|
|
697
|
+
|
|
698
|
+
# if we have a valid frame, determine the polling rate
|
|
699
|
+
metadata = videoCapture.get_metadata()
|
|
700
|
+
numer, divisor = metadata['frame_rate']
|
|
701
|
+
|
|
702
|
+
# poll interval is half the frame period, this makes sure we don't
|
|
703
|
+
# miss frames while not wasting CPU cycles
|
|
704
|
+
pollInterval = (1.0 / float(numer / divisor)) * 0.5
|
|
705
|
+
|
|
706
|
+
# holds main-thread execution until its ready for frames
|
|
707
|
+
# frameQueue.put((frame, val, metadata)) # put the first frame
|
|
708
|
+
|
|
709
|
+
warmUpBarrier.wait() # wait for main thread to be ready
|
|
710
|
+
|
|
711
|
+
# start capturing frames in background thread
|
|
712
|
+
isRecording = False
|
|
713
|
+
lastAbsTime = -1.0 # presentation timestamp of the last frame
|
|
714
|
+
while not exitEvent.is_set(): # quit if signaled
|
|
715
|
+
# pull a frame from the stream, we keep this running 'hot' so
|
|
716
|
+
# that we don't miss frames, we just discard them if we don't
|
|
717
|
+
# need them
|
|
718
|
+
frame, val = videoCapture.get_frame(force_refresh=False)
|
|
719
|
+
|
|
720
|
+
if val == 'eof': # thread should exit if stream is done
|
|
721
|
+
break
|
|
722
|
+
elif val == 'paused':
|
|
723
|
+
continue
|
|
724
|
+
elif frame is None:
|
|
725
|
+
continue
|
|
726
|
+
else:
|
|
727
|
+
# don't queue frames unless they are newer than the last
|
|
728
|
+
if isRecording:
|
|
729
|
+
thisFrameAbsTime = videoCapture.get_pts()
|
|
730
|
+
if lastAbsTime < thisFrameAbsTime:
|
|
731
|
+
frameQueue.put((frame, val, metadata))
|
|
732
|
+
lastAbsTime = thisFrameAbsTime
|
|
733
|
+
|
|
734
|
+
if recordEvent.is_set() and not isRecording:
|
|
735
|
+
if audioCapture is not None:
|
|
736
|
+
audioCapture.start(waitForStart=1)
|
|
737
|
+
recordingBarrier.wait()
|
|
738
|
+
isRecording = True
|
|
739
|
+
elif not recordEvent.is_set() and isRecording:
|
|
740
|
+
if audioCapture is not None:
|
|
741
|
+
audioCapture.stop(blockUntilStopped=1)
|
|
742
|
+
recordingBarrier.wait()
|
|
743
|
+
isRecording = False
|
|
744
|
+
|
|
745
|
+
if not isRecording:
|
|
746
|
+
time.sleep(pollInterval)
|
|
747
|
+
continue
|
|
748
|
+
|
|
749
|
+
if audioCapture is not None:
|
|
750
|
+
if audioCapture.isRecording:
|
|
751
|
+
audioCapture.poll()
|
|
752
|
+
|
|
753
|
+
time.sleep(pollInterval)
|
|
754
|
+
|
|
755
|
+
videoCapture.close_player()
|
|
756
|
+
|
|
757
|
+
if audioCapture is not None:
|
|
758
|
+
audioCapture.stop(blockUntilStopped=1)
|
|
759
|
+
|
|
760
|
+
# thread is dead when we get here
|
|
761
|
+
|
|
762
|
+
# configure the camera stream reader
|
|
763
|
+
ff_opts = {} # ffmpeg options
|
|
764
|
+
lib_opts = {} # ffpyplayer options
|
|
765
|
+
_camera = CAMERA_NULL_VALUE
|
|
766
|
+
_frameRate = CAMERA_NULL_VALUE
|
|
767
|
+
_cameraInfo = self._cameraInfo
|
|
768
|
+
|
|
769
|
+
# setup commands for FFMPEG
|
|
770
|
+
if _cameraInfo.cameraAPI == CAMERA_API_DIRECTSHOW: # windows
|
|
771
|
+
ff_opts['f'] = 'dshow'
|
|
772
|
+
_camera = 'video={}'.format(_cameraInfo.name)
|
|
773
|
+
_frameRate = _cameraInfo.frameRate
|
|
774
|
+
if _cameraInfo.pixelFormat:
|
|
775
|
+
ff_opts['pixel_format'] = _cameraInfo.pixelFormat
|
|
776
|
+
if _cameraInfo.codecFormat:
|
|
777
|
+
ff_opts['vcodec'] = _cameraInfo.codecFormat
|
|
778
|
+
elif _cameraInfo.cameraAPI == CAMERA_API_AVFOUNDATION: # darwin
|
|
779
|
+
ff_opts['f'] = 'avfoundation'
|
|
780
|
+
ff_opts['i'] = _camera = self._cameraInfo.name
|
|
781
|
+
|
|
782
|
+
# handle pixel formats using FourCC
|
|
783
|
+
global pixelFormatTbl
|
|
784
|
+
ffmpegPixFmt = pixelFormatTbl.get(_cameraInfo.pixelFormat, None)
|
|
785
|
+
|
|
786
|
+
if ffmpegPixFmt is None:
|
|
787
|
+
raise FormatNotFoundError(
|
|
788
|
+
"Cannot find suitable FFMPEG pixel format for '{}'. Try a "
|
|
789
|
+
"different format or camera.".format(
|
|
790
|
+
_cameraInfo.pixelFormat))
|
|
791
|
+
|
|
792
|
+
_cameraInfo.pixelFormat = ffmpegPixFmt
|
|
793
|
+
|
|
794
|
+
# this needs to be exactly specified if using NTSC
|
|
795
|
+
if math.isclose(CAMERA_FRAMERATE_NTSC, _cameraInfo.frameRate):
|
|
796
|
+
_frameRate = CAMERA_FRAMERATE_NOMINAL_NTSC
|
|
797
|
+
else:
|
|
798
|
+
_frameRate = str(_cameraInfo.frameRate)
|
|
799
|
+
|
|
800
|
+
# need these since hardware acceleration is not possible on Mac yet
|
|
801
|
+
lib_opts['fflags'] = 'nobuffer'
|
|
802
|
+
lib_opts['flags'] = 'low_delay'
|
|
803
|
+
lib_opts['pixel_format'] = _cameraInfo.pixelFormat
|
|
804
|
+
ff_opts['framedrop'] = True
|
|
805
|
+
ff_opts['fast'] = True
|
|
806
|
+
elif _cameraInfo.cameraAPI == CAMERA_API_VIDEO4LINUX2:
|
|
807
|
+
raise OSError(
|
|
808
|
+
"Sorry, camera does not support Linux at this time. However, "
|
|
809
|
+
"it will in future versions.")
|
|
810
|
+
|
|
811
|
+
else:
|
|
812
|
+
raise RuntimeError("Unsupported camera API specified.")
|
|
813
|
+
|
|
814
|
+
# set library options
|
|
815
|
+
camWidth = _cameraInfo.frameSize[0]
|
|
816
|
+
camHeight = _cameraInfo.frameSize[1]
|
|
817
|
+
|
|
818
|
+
# configure the real-time buffer size
|
|
819
|
+
_bufferSize = camWidth * camHeight * 3 * self._bufferSecs
|
|
820
|
+
|
|
821
|
+
# common settings across libraries
|
|
822
|
+
lib_opts['rtbufsize'] = str(int(_bufferSize))
|
|
823
|
+
lib_opts['video_size'] = _cameraInfo.frameSizeAsFormattedString()
|
|
824
|
+
lib_opts['framerate'] = str(_frameRate)
|
|
825
|
+
|
|
826
|
+
self._warmupBarrier = threading.Barrier(2)
|
|
827
|
+
self._recordBarrier = threading.Barrier(2)
|
|
828
|
+
|
|
829
|
+
# open the media player
|
|
830
|
+
from ffpyplayer.player import MediaPlayer
|
|
831
|
+
cap = MediaPlayer(_camera, ff_opts=ff_opts, lib_opts=lib_opts)
|
|
832
|
+
|
|
833
|
+
# open a stream thread and pause wait until ready
|
|
834
|
+
self._playerThread = threading.Thread(
|
|
835
|
+
target=_frameGetterAsync,
|
|
836
|
+
args=(cap,
|
|
837
|
+
self._frameQueue,
|
|
838
|
+
self._exitEvent,
|
|
839
|
+
self._enableEvent,
|
|
840
|
+
self._warmupBarrier,
|
|
841
|
+
self._recordBarrier,
|
|
842
|
+
self._mic))
|
|
843
|
+
self._playerThread.daemon=True
|
|
844
|
+
self._playerThread.start()
|
|
845
|
+
|
|
846
|
+
self._warmupBarrier.wait()
|
|
847
|
+
|
|
848
|
+
# pass off the player to the thread which will process the stream
|
|
849
|
+
self._enqueueFrame() # pull metadata from first frame
|
|
850
|
+
|
|
851
|
+
def _enqueueFrame(self):
|
|
852
|
+
"""Grab the latest frame from the stream.
|
|
853
|
+
|
|
854
|
+
Returns
|
|
855
|
+
-------
|
|
856
|
+
bool
|
|
857
|
+
`True` if a frame has been enqueued. Returns `False` if the camera
|
|
858
|
+
has not acquired a new frame yet.
|
|
859
|
+
|
|
860
|
+
"""
|
|
861
|
+
self._assertMediaPlayer()
|
|
862
|
+
|
|
863
|
+
try:
|
|
864
|
+
frameData = self._frameQueue.get_nowait()
|
|
865
|
+
except queue.Empty:
|
|
866
|
+
return False
|
|
867
|
+
|
|
868
|
+
frame, val, metadata = frameData # update the frame
|
|
869
|
+
|
|
870
|
+
if val == CAMERA_STATUS_EOF: # handle end of stream
|
|
871
|
+
return False
|
|
872
|
+
elif val == CAMERA_STATUS_PAUSED: # handle when paused
|
|
873
|
+
return False
|
|
874
|
+
elif frame is None: # handle when no frame is available
|
|
875
|
+
return False
|
|
876
|
+
|
|
877
|
+
frameImage, pts = frame # otherwise, unpack the frame
|
|
878
|
+
|
|
879
|
+
# if we have a new frame, update the frame information
|
|
880
|
+
videoBuffer = frameImage.to_bytearray()[0]
|
|
881
|
+
videoFrameArray = np.frombuffer(videoBuffer, dtype=np.uint8)
|
|
882
|
+
|
|
883
|
+
# provide the last frame
|
|
884
|
+
self._lastFrame = MovieFrame(
|
|
885
|
+
frameIndex=self._frameIndex,
|
|
886
|
+
absTime=pts,
|
|
887
|
+
# displayTime=self._recentMetadata['frame_size'],
|
|
888
|
+
size=frameImage.get_size(),
|
|
889
|
+
colorData=videoFrameArray,
|
|
890
|
+
audioChannels=0,
|
|
891
|
+
audioSamples=None,
|
|
892
|
+
metadata=metadata,
|
|
893
|
+
movieLib=self._cameraLib,
|
|
894
|
+
userData=None)
|
|
895
|
+
|
|
896
|
+
return True
|
|
897
|
+
|
|
898
|
+
def close(self):
|
|
899
|
+
"""Close the camera stream and release resources.
|
|
900
|
+
|
|
901
|
+
This blocks until the camera stream thread is no longer alive.
|
|
902
|
+
|
|
903
|
+
"""
|
|
904
|
+
if self._playerThread is None:
|
|
905
|
+
raise RuntimeError('Cannot close `MediaPlayer`, already closed.')
|
|
906
|
+
|
|
907
|
+
self._exitEvent.set() # signal the thread to stop
|
|
908
|
+
self._playerThread.join() # wait for the thread to stop
|
|
909
|
+
|
|
910
|
+
self._playerThread = None
|
|
911
|
+
|
|
912
|
+
@property
|
|
913
|
+
def isEnabled(self):
|
|
914
|
+
"""`True` if the camera is enabled.
|
|
915
|
+
"""
|
|
916
|
+
return self._enableEvent.is_set()
|
|
917
|
+
|
|
918
|
+
def enable(self, state=True):
|
|
919
|
+
"""Start passing frames to the frame queue.
|
|
920
|
+
|
|
921
|
+
This method returns when the video and audio stream are both starting to
|
|
922
|
+
record or stop recording.
|
|
923
|
+
|
|
924
|
+
Parameters
|
|
925
|
+
----------
|
|
926
|
+
state : bool
|
|
927
|
+
`True` to enable recording frames to the queue, `False` to disable.
|
|
928
|
+
On state change, the audio interface will be started or stopped.
|
|
929
|
+
|
|
930
|
+
"""
|
|
931
|
+
if state:
|
|
932
|
+
self._enableEvent.set()
|
|
933
|
+
else:
|
|
934
|
+
self._enableEvent.clear()
|
|
935
|
+
|
|
936
|
+
self._recordBarrier.wait()
|
|
937
|
+
self._enqueueFrame()
|
|
938
|
+
|
|
939
|
+
def disable(self):
|
|
940
|
+
"""Stop passing frames to the frame queue.
|
|
941
|
+
|
|
942
|
+
Calling this is equivalent to calling `enable(False)`.
|
|
943
|
+
|
|
944
|
+
"""
|
|
945
|
+
self.enable(False)
|
|
946
|
+
|
|
947
|
+
def getFrames(self):
|
|
948
|
+
"""Get all frames from the camera stream which are waiting to be
|
|
949
|
+
processed.
|
|
950
|
+
|
|
951
|
+
Returns
|
|
952
|
+
-------
|
|
953
|
+
list
|
|
954
|
+
List of `MovieFrame` objects. The most recent frame is the last one
|
|
955
|
+
in the list.
|
|
956
|
+
|
|
957
|
+
"""
|
|
958
|
+
self._assertMediaPlayer()
|
|
959
|
+
|
|
960
|
+
frames = []
|
|
961
|
+
while self._enqueueFrame():
|
|
962
|
+
frames.append(self._lastFrame)
|
|
963
|
+
|
|
964
|
+
return frames
|
|
965
|
+
|
|
966
|
+
def getRecentFrame(self):
|
|
967
|
+
"""Get the most recent frame captured from the camera, discarding all
|
|
968
|
+
others.
|
|
969
|
+
|
|
970
|
+
Returns
|
|
971
|
+
-------
|
|
972
|
+
MovieFrame
|
|
973
|
+
The most recent frame from the stream.
|
|
974
|
+
|
|
975
|
+
"""
|
|
976
|
+
while self._enqueueFrame():
|
|
977
|
+
pass
|
|
978
|
+
|
|
979
|
+
return self._lastFrame
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
class CameraInterfaceOpenCV(CameraInterface):
|
|
983
|
+
"""Camera interface using OpenCV to open and read camera streams.
|
|
984
|
+
|
|
985
|
+
Parameters
|
|
986
|
+
----------
|
|
987
|
+
device : int
|
|
988
|
+
Camera device to open a stream with. This value is platform dependent.
|
|
989
|
+
mic : MicrophoneInterface or None
|
|
990
|
+
Microphone interface to use for audio recording. If `None`, no audio
|
|
991
|
+
recording is performed.
|
|
992
|
+
|
|
993
|
+
"""
|
|
994
|
+
_cameraLib = u'opencv'
|
|
995
|
+
|
|
996
|
+
def __init__(self, device, mic=None):
|
|
997
|
+
super().__init__(device)
|
|
998
|
+
try:
|
|
999
|
+
import cv2 # just import to check if it's available
|
|
1000
|
+
except ImportError:
|
|
1001
|
+
raise ImportError(
|
|
1002
|
+
"Could not import `cv2`. Please install OpenCV2 to use this "
|
|
1003
|
+
"camera interface.")
|
|
1004
|
+
|
|
1005
|
+
self._cameraInfo = device
|
|
1006
|
+
self._mic = mic # microphone interface
|
|
1007
|
+
self._frameQueue = queue.Queue()
|
|
1008
|
+
self._enableEvent = threading.Event()
|
|
1009
|
+
self._exitEvent = threading.Event()
|
|
1010
|
+
self._warmUpBarrier = None
|
|
1011
|
+
self._recordBarrier = None
|
|
1012
|
+
|
|
1013
|
+
def _assertMediaPlayer(self):
|
|
1014
|
+
"""Assert that the media player thread is running.
|
|
1015
|
+
"""
|
|
1016
|
+
return self._playerThread is not None
|
|
1017
|
+
|
|
1018
|
+
@staticmethod
|
|
1019
|
+
def getCameras(maxCameraEnum=16):
|
|
1020
|
+
"""Get information about available cameras.
|
|
1021
|
+
|
|
1022
|
+
OpenCV is not capable of enumerating cameras and getting information
|
|
1023
|
+
about them. Therefore, we must open a stream with each camera index
|
|
1024
|
+
and query the information from the stream. This process is quite slow
|
|
1025
|
+
on systems with many cameras. It's best to run this function once and
|
|
1026
|
+
save the results for later use if the camera configuration is not
|
|
1027
|
+
expected to change.
|
|
1028
|
+
|
|
1029
|
+
Parameters
|
|
1030
|
+
----------
|
|
1031
|
+
maxCameraEnum : int
|
|
1032
|
+
Maximum number of cameras to check. This is the maximum camera index
|
|
1033
|
+
to check. For example, if `maxCameraEnum` is 16, then cameras 0-15
|
|
1034
|
+
will be checked.
|
|
1035
|
+
|
|
1036
|
+
Returns
|
|
1037
|
+
-------
|
|
1038
|
+
dict
|
|
1039
|
+
Mapping containing information about each camera. The keys are the
|
|
1040
|
+
camera index, and the values are `CameraInfo` objects.
|
|
1041
|
+
|
|
1042
|
+
"""
|
|
1043
|
+
import cv2
|
|
1044
|
+
|
|
1045
|
+
# recommended camera drivers for each platform
|
|
1046
|
+
cameraPlatformDrivers = {
|
|
1047
|
+
'Linux': (cv2.CAP_V4L2, CAMERA_API_VIDEO4LINUX2),
|
|
1048
|
+
'Windows': (cv2.CAP_DSHOW, CAMERA_API_DIRECTSHOW),
|
|
1049
|
+
'Darwin': (cv2.CAP_AVFOUNDATION, CAMERA_API_AVFOUNDATION)
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
# select the camera interface for the platform
|
|
1053
|
+
cameraDriver, cameraAPI = cameraPlatformDrivers.get(
|
|
1054
|
+
platform.system(), (cv2.CAP_ANY, CAMERA_API_ANY))
|
|
1055
|
+
|
|
1056
|
+
logging.info(
|
|
1057
|
+
'Searching for connected cameras, this may take a while...')
|
|
1058
|
+
|
|
1059
|
+
cameras = {}
|
|
1060
|
+
for cameraIndex in range(maxCameraEnum):
|
|
1061
|
+
# open a camera
|
|
1062
|
+
thisCamera = cv2.VideoCapture(cameraIndex, cameraDriver)
|
|
1063
|
+
|
|
1064
|
+
# if the camera is not opened, we're done
|
|
1065
|
+
if not thisCamera.isOpened():
|
|
1066
|
+
break
|
|
1067
|
+
|
|
1068
|
+
# get information about camera capabilities
|
|
1069
|
+
frameRate = thisCamera.get(cv2.CAP_PROP_FPS)
|
|
1070
|
+
frameSize = (
|
|
1071
|
+
int(thisCamera.get(cv2.CAP_PROP_FRAME_WIDTH)),
|
|
1072
|
+
int(thisCamera.get(cv2.CAP_PROP_FRAME_HEIGHT)))
|
|
1073
|
+
|
|
1074
|
+
genName = 'camera:{}'.format(cameraIndex)
|
|
1075
|
+
cameraInfo = CameraInfo(
|
|
1076
|
+
index=cameraIndex,
|
|
1077
|
+
name=genName,
|
|
1078
|
+
frameSize=frameSize or (-1, -1),
|
|
1079
|
+
frameRate=frameRate or -1.0,
|
|
1080
|
+
pixelFormat='bgr24', # always BGR with 8 bpc for OpenCV
|
|
1081
|
+
cameraLib=CameraInterfaceOpenCV._cameraLib,
|
|
1082
|
+
cameraAPI=cameraAPI
|
|
1083
|
+
)
|
|
1084
|
+
|
|
1085
|
+
cameras.update({genName: [cameraInfo]})
|
|
1086
|
+
thisCamera.release()
|
|
1087
|
+
|
|
1088
|
+
logging.info('Found {} cameras.'.format(len(cameras)))
|
|
1089
|
+
|
|
1090
|
+
return cameras
|
|
1091
|
+
|
|
1092
|
+
@property
|
|
1093
|
+
def framesWaiting(self):
|
|
1094
|
+
"""Get the number of frames currently buffered (`int`).
|
|
1095
|
+
|
|
1096
|
+
Returns the number of frames which have been pulled from the stream and
|
|
1097
|
+
are waiting to be processed. This value is decremented by calls to
|
|
1098
|
+
`_enqueueFrame()`.
|
|
1099
|
+
|
|
1100
|
+
"""
|
|
1101
|
+
return self._frameQueue.qsize()
|
|
1102
|
+
|
|
1103
|
+
@property
|
|
1104
|
+
def frameRate(self):
|
|
1105
|
+
"""Get the frame rate of the camera stream (`float`).
|
|
1106
|
+
"""
|
|
1107
|
+
if self._cameraInfo is None:
|
|
1108
|
+
return -1.0
|
|
1109
|
+
|
|
1110
|
+
return self._cameraInfo.frameRate
|
|
1111
|
+
|
|
1112
|
+
@property
|
|
1113
|
+
def frameSize(self):
|
|
1114
|
+
"""Get the frame size of the camera stream (`tuple`).
|
|
1115
|
+
"""
|
|
1116
|
+
if self._cameraInfo is None:
|
|
1117
|
+
return (-1, -1)
|
|
1118
|
+
|
|
1119
|
+
return self._cameraInfo.frameSize
|
|
1120
|
+
|
|
1121
|
+
def isOpen(self):
|
|
1122
|
+
"""Check if the camera stream is open (`bool`).
|
|
1123
|
+
"""
|
|
1124
|
+
if self._playerThread is not None:
|
|
1125
|
+
return self._playerThread.is_alive()
|
|
1126
|
+
|
|
1127
|
+
return False
|
|
1128
|
+
|
|
1129
|
+
def open(self):
|
|
1130
|
+
"""Open the camera stream and start reading frames using OpenCV2.
|
|
1131
|
+
"""
|
|
1132
|
+
import cv2
|
|
1133
|
+
|
|
1134
|
+
def _frameGetterAsync(videoCapture, frameQueue, exitEvent, recordEvent,
|
|
1135
|
+
warmUpBarrier, recordingBarrier, audioCapture):
|
|
1136
|
+
"""Get frames asynchronously from the camera stream.
|
|
1137
|
+
|
|
1138
|
+
Parameters
|
|
1139
|
+
----------
|
|
1140
|
+
videoCapture : cv2.VideoCapture
|
|
1141
|
+
Handle for the video capture object. This is opened outside the
|
|
1142
|
+
thread and passed in.
|
|
1143
|
+
frameQueue : queue.Queue
|
|
1144
|
+
Queue to store frames in.
|
|
1145
|
+
exitEvent : threading.Event
|
|
1146
|
+
Event to signal when the thread should stop.
|
|
1147
|
+
recordEvent : threading.Event
|
|
1148
|
+
Event used to signal the thread to pass frames along to the main
|
|
1149
|
+
thread.
|
|
1150
|
+
warmUpBarrier : threading.Barrier
|
|
1151
|
+
Barrier which is used hold until camera capture is ready.
|
|
1152
|
+
recordingBarrier : threading.Barrier
|
|
1153
|
+
Barrier which is used to synchronize audio and video recording.
|
|
1154
|
+
This ensures that the audio device is ready before buffering
|
|
1155
|
+
frames captured by the camera.
|
|
1156
|
+
audioCapture : psychopy.sound.Microphone or None
|
|
1157
|
+
Microphone object to use for audio capture. This will be used to
|
|
1158
|
+
synchronize the audio and video streams. If `None`, no audio
|
|
1159
|
+
will be captured.
|
|
1160
|
+
|
|
1161
|
+
"""
|
|
1162
|
+
# poll interval is half the frame period, this makes sure we don't
|
|
1163
|
+
# miss frames while not wasting CPU cycles
|
|
1164
|
+
# fps = videoCapture.get(cv2.CAP_PROP_FPS)
|
|
1165
|
+
# if fps > 0.0:
|
|
1166
|
+
# pollInterval = (1.0 / fps) * 0.5
|
|
1167
|
+
# else:
|
|
1168
|
+
# pollInterval = 1 / 60.0
|
|
1169
|
+
|
|
1170
|
+
# if the camera is opened, wait until the main thread is ready to
|
|
1171
|
+
# take frames
|
|
1172
|
+
warmUpBarrier.wait()
|
|
1173
|
+
|
|
1174
|
+
# start capturing frames
|
|
1175
|
+
isRecording = False
|
|
1176
|
+
while not exitEvent.is_set():
|
|
1177
|
+
# Capture frame-by-frame
|
|
1178
|
+
ret, frame = videoCapture.read()
|
|
1179
|
+
|
|
1180
|
+
# if frame is read correctly ret is True
|
|
1181
|
+
if not ret: # eol or something else
|
|
1182
|
+
# val = 'eof'
|
|
1183
|
+
break
|
|
1184
|
+
else:
|
|
1185
|
+
# don't queue frames unless they are newer than the last
|
|
1186
|
+
if isRecording:
|
|
1187
|
+
# color conversion is done in the thread here
|
|
1188
|
+
colorData = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
1189
|
+
# colorData = frame
|
|
1190
|
+
frameQueue.put((colorData, 0.0, None))
|
|
1191
|
+
|
|
1192
|
+
# check if we should start or stop recording
|
|
1193
|
+
if recordEvent.is_set() and not isRecording:
|
|
1194
|
+
if audioCapture is not None:
|
|
1195
|
+
audioCapture.start(waitForStart=1)
|
|
1196
|
+
recordingBarrier.wait()
|
|
1197
|
+
isRecording = True
|
|
1198
|
+
elif not recordEvent.is_set() and isRecording:
|
|
1199
|
+
if audioCapture is not None:
|
|
1200
|
+
audioCapture.stop(blockUntilStopped=1)
|
|
1201
|
+
recordingBarrier.wait()
|
|
1202
|
+
isRecording = False
|
|
1203
|
+
|
|
1204
|
+
if not isRecording:
|
|
1205
|
+
# time.sleep(pollInterval)
|
|
1206
|
+
continue
|
|
1207
|
+
|
|
1208
|
+
if audioCapture is not None:
|
|
1209
|
+
if audioCapture.isRecording:
|
|
1210
|
+
audioCapture.poll()
|
|
1211
|
+
|
|
1212
|
+
# when everything done, release the capture device
|
|
1213
|
+
videoCapture.release()
|
|
1214
|
+
|
|
1215
|
+
if audioCapture is not None: # stop audio capture
|
|
1216
|
+
audioCapture.stop(blockUntilStopped=1)
|
|
1217
|
+
|
|
1218
|
+
# thread is dead if we get here
|
|
1219
|
+
|
|
1220
|
+
# barriers used for synchronizing
|
|
1221
|
+
parties = 2 # main + recording threads
|
|
1222
|
+
self._warmUpBarrier = threading.Barrier(parties) # camera is ready
|
|
1223
|
+
self._recordBarrier = threading.Barrier(parties) # audio/video is ready
|
|
1224
|
+
|
|
1225
|
+
# drivers for the given camera API
|
|
1226
|
+
cameraDrivers = {
|
|
1227
|
+
CAMERA_API_ANY: cv2.CAP_ANY,
|
|
1228
|
+
CAMERA_API_VIDEO4LINUX2: cv2.CAP_V4L2,
|
|
1229
|
+
CAMERA_API_DIRECTSHOW: cv2.CAP_DSHOW,
|
|
1230
|
+
CAMERA_API_AVFOUNDATION: cv2.CAP_AVFOUNDATION
|
|
1231
|
+
}
|
|
1232
|
+
_cameraInfo = self._cameraInfo
|
|
1233
|
+
|
|
1234
|
+
# create the camera capture object, we keep this internal to the thread
|
|
1235
|
+
# so that we can control when it is released
|
|
1236
|
+
cap = cv2.VideoCapture(
|
|
1237
|
+
_cameraInfo.index,
|
|
1238
|
+
cameraDrivers[_cameraInfo.cameraAPI])
|
|
1239
|
+
|
|
1240
|
+
# check if the camera is opened
|
|
1241
|
+
if not cap.isOpened():
|
|
1242
|
+
raise RuntimeError("Cannot open camera using `cv2`")
|
|
1243
|
+
|
|
1244
|
+
# if the user didn't specify a frame rate or size, use the defaults
|
|
1245
|
+
# pulled from the camera
|
|
1246
|
+
usingDefaults = False
|
|
1247
|
+
if _cameraInfo.frameRate is None:
|
|
1248
|
+
_cameraInfo.frameRate = cap.get(cv2.CAP_PROP_FPS)
|
|
1249
|
+
usingDefaults = True
|
|
1250
|
+
|
|
1251
|
+
if _cameraInfo.frameSize is None:
|
|
1252
|
+
_cameraInfo.frameSize = (
|
|
1253
|
+
int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
|
|
1254
|
+
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
|
|
1255
|
+
usingDefaults = True
|
|
1256
|
+
|
|
1257
|
+
if not usingDefaults:
|
|
1258
|
+
# set frame rate and size and check if they were set correctly
|
|
1259
|
+
cap.set(cv2.CAP_PROP_FPS, _cameraInfo.frameRate)
|
|
1260
|
+
cap.set(cv2.CAP_PROP_FRAME_WIDTH, _cameraInfo.frameSize[0])
|
|
1261
|
+
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, _cameraInfo.frameSize[1])
|
|
1262
|
+
|
|
1263
|
+
if cap.get(cv2.CAP_PROP_FPS) != _cameraInfo.frameRate:
|
|
1264
|
+
raise CameraFormatNotSupportedError(
|
|
1265
|
+
"Unsupported frame rate (%s), try %s instead." % (
|
|
1266
|
+
_cameraInfo.frameRate, cap.get(cv2.CAP_PROP_FPS)))
|
|
1267
|
+
|
|
1268
|
+
frameSizeMismatch = (
|
|
1269
|
+
cap.get(cv2.CAP_PROP_FRAME_WIDTH) != _cameraInfo.frameSize[0] or
|
|
1270
|
+
cap.get(cv2.CAP_PROP_FRAME_HEIGHT) != _cameraInfo.frameSize[1])
|
|
1271
|
+
if frameSizeMismatch:
|
|
1272
|
+
raise CameraFormatNotSupportedError(
|
|
1273
|
+
"Unsupported frame size: %s" % str(_cameraInfo.frameSize))
|
|
1274
|
+
|
|
1275
|
+
# open a stream and pause it until ready
|
|
1276
|
+
self._playerThread = threading.Thread(
|
|
1277
|
+
target=_frameGetterAsync,
|
|
1278
|
+
args=(cap,
|
|
1279
|
+
self._frameQueue,
|
|
1280
|
+
self._exitEvent,
|
|
1281
|
+
self._enableEvent,
|
|
1282
|
+
self._warmUpBarrier,
|
|
1283
|
+
self._recordBarrier,
|
|
1284
|
+
self._mic))
|
|
1285
|
+
self._playerThread.daemon=True
|
|
1286
|
+
self._playerThread.start()
|
|
1287
|
+
|
|
1288
|
+
self._warmUpBarrier.wait() # wait until the camera is ready
|
|
1289
|
+
|
|
1290
|
+
# pass off the player to the thread which will process the stream
|
|
1291
|
+
self._enqueueFrame() # pull metadata from first frame
|
|
1292
|
+
|
|
1293
|
+
def _enqueueFrame(self):
|
|
1294
|
+
"""Grab the latest frame from the stream.
|
|
1295
|
+
|
|
1296
|
+
Returns
|
|
1297
|
+
-------
|
|
1298
|
+
bool
|
|
1299
|
+
`True` if a frame has been enqueued. Returns `False` if the camera
|
|
1300
|
+
has not acquired a new frame yet.
|
|
1301
|
+
|
|
1302
|
+
"""
|
|
1303
|
+
self._assertMediaPlayer()
|
|
1304
|
+
|
|
1305
|
+
try:
|
|
1306
|
+
frameData = self._frameQueue.get_nowait()
|
|
1307
|
+
except queue.Empty:
|
|
1308
|
+
return False
|
|
1309
|
+
|
|
1310
|
+
frame, val, _ = frameData # update the frame
|
|
1311
|
+
|
|
1312
|
+
if val == 'eof': # handle end of stream
|
|
1313
|
+
return False
|
|
1314
|
+
elif val == 'paused': # handle when paused, not used for OpenCV yet
|
|
1315
|
+
return False
|
|
1316
|
+
elif frame is None: # handle when no frame is available
|
|
1317
|
+
return False
|
|
1318
|
+
|
|
1319
|
+
frameImage = frame # otherwise, unpack the frame
|
|
1320
|
+
|
|
1321
|
+
# if we have a new frame, update the frame information
|
|
1322
|
+
# videoBuffer = frameImage.to_bytearray()[0]
|
|
1323
|
+
videoFrameArray = np.ascontiguousarray(
|
|
1324
|
+
frameImage.flatten(), dtype=np.uint8)
|
|
1325
|
+
|
|
1326
|
+
# provide the last frame
|
|
1327
|
+
self._lastFrame = MovieFrame(
|
|
1328
|
+
frameIndex=self._frameIndex,
|
|
1329
|
+
absTime=0.0,
|
|
1330
|
+
# displayTime=self._recentMetadata['frame_size'],
|
|
1331
|
+
size=self._cameraInfo.frameSize,
|
|
1332
|
+
colorFormat='rgb24', # converted in thread
|
|
1333
|
+
colorData=videoFrameArray,
|
|
1334
|
+
audioChannels=0,
|
|
1335
|
+
audioSamples=None,
|
|
1336
|
+
metadata=None,
|
|
1337
|
+
movieLib=self._cameraLib,
|
|
1338
|
+
userData=None)
|
|
1339
|
+
|
|
1340
|
+
return True
|
|
1341
|
+
|
|
1342
|
+
def close(self):
|
|
1343
|
+
"""Close the camera stream and release resources.
|
|
1344
|
+
"""
|
|
1345
|
+
self._exitEvent.set() # signal the thread to stop
|
|
1346
|
+
self._playerThread.join() # hold the thread until it stops
|
|
1347
|
+
|
|
1348
|
+
self._playerThread = None
|
|
1349
|
+
|
|
1350
|
+
@property
|
|
1351
|
+
def isEnabled(self):
|
|
1352
|
+
"""`True` if the camera is enabled.
|
|
1353
|
+
"""
|
|
1354
|
+
return self._enableEvent.is_set()
|
|
1355
|
+
|
|
1356
|
+
def enable(self, state=True):
|
|
1357
|
+
"""Start passing frames to the frame queue.
|
|
1358
|
+
|
|
1359
|
+
This method returns when the video and audio stream are both starting to
|
|
1360
|
+
record or stop recording. If no audio stream is being recorded, this
|
|
1361
|
+
method returns quicker.
|
|
1362
|
+
|
|
1363
|
+
Parameters
|
|
1364
|
+
----------
|
|
1365
|
+
state : bool
|
|
1366
|
+
`True` to enable recording frames to the queue, `False` to disable.
|
|
1367
|
+
On state change, the audio interface will be started or stopped.
|
|
1368
|
+
|
|
1369
|
+
"""
|
|
1370
|
+
if state:
|
|
1371
|
+
self._enableEvent.set()
|
|
1372
|
+
else:
|
|
1373
|
+
self._enableEvent.clear()
|
|
1374
|
+
|
|
1375
|
+
self._recordBarrier.wait()
|
|
1376
|
+
self._enqueueFrame()
|
|
1377
|
+
|
|
1378
|
+
def disable(self):
|
|
1379
|
+
"""Stop passing frames to the frame queue.
|
|
1380
|
+
|
|
1381
|
+
Calling this is equivalent to calling `enable(False)`.
|
|
1382
|
+
|
|
1383
|
+
"""
|
|
1384
|
+
self.enable(False)
|
|
1385
|
+
|
|
1386
|
+
def getFrames(self):
|
|
1387
|
+
"""Get all frames from the camera stream which are waiting to be
|
|
1388
|
+
processed.
|
|
1389
|
+
|
|
1390
|
+
Returns
|
|
1391
|
+
-------
|
|
1392
|
+
list
|
|
1393
|
+
List of `MovieFrame` objects. The most recent frame is the last one
|
|
1394
|
+
in the list.
|
|
1395
|
+
|
|
1396
|
+
"""
|
|
1397
|
+
self._assertMediaPlayer()
|
|
1398
|
+
|
|
1399
|
+
frames = []
|
|
1400
|
+
while self._enqueueFrame():
|
|
1401
|
+
frames.append(self._lastFrame)
|
|
1402
|
+
|
|
1403
|
+
return frames
|
|
1404
|
+
|
|
1405
|
+
def getRecentFrame(self):
|
|
1406
|
+
"""Get the most recent frame captured from the camera, discarding all
|
|
1407
|
+
others.
|
|
1408
|
+
|
|
1409
|
+
Returns
|
|
1410
|
+
-------
|
|
1411
|
+
MovieFrame
|
|
1412
|
+
The most recent frame from the stream.
|
|
1413
|
+
|
|
1414
|
+
"""
|
|
1415
|
+
while self._enqueueFrame():
|
|
1416
|
+
pass
|
|
1417
|
+
|
|
1418
|
+
return self._lastFrame
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
# keep track of camera devices that are opened
|
|
1422
|
+
_openCameras = {}
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
class Camera:
|
|
1426
|
+
"""Class for displaying and recording video from a USB/PCI connected camera.
|
|
1427
|
+
|
|
1428
|
+
This class is capable of opening, recording, and saving camera video streams
|
|
1429
|
+
to disk. Camera stream reading/writing is done in a separate thread,
|
|
1430
|
+
allowing capture to occur in the background while the main thread is free to
|
|
1431
|
+
perform other tasks. This allows for capture to occur at higher frame rates
|
|
1432
|
+
than the display refresh rate. Audio recording is also supported if a
|
|
1433
|
+
microphone interface is provided, where recording will be synchronized with
|
|
1434
|
+
the video stream (as best as possible). Video and audio can be saved to disk
|
|
1435
|
+
either as a single file or as separate files.
|
|
1436
|
+
|
|
1437
|
+
GNU/Linux is supported only by the OpenCV backend (`cameraLib='opencv'`).
|
|
1438
|
+
|
|
1439
|
+
Parameters
|
|
1440
|
+
----------
|
|
1441
|
+
device : str or int
|
|
1442
|
+
Camera to open a stream with. If the ID is not valid, an error will be
|
|
1443
|
+
raised when `open()` is called. Value can be a string or number. String
|
|
1444
|
+
values are platform-dependent: a DirectShow URI or camera name on
|
|
1445
|
+
Windows, or a camera name/index on MacOS. Specifying a number (>=0) is a
|
|
1446
|
+
platform-independent means of selecting a camera. PsychoPy enumerates
|
|
1447
|
+
possible camera devices and makes them selectable without explicitly
|
|
1448
|
+
having the name of the cameras attached to the system. Use caution when
|
|
1449
|
+
specifying an integer, as the same index may not reference the same
|
|
1450
|
+
camera every time.
|
|
1451
|
+
mic : :class:`~psychopy.sound.microphone.Microphone` or None
|
|
1452
|
+
Microphone to record audio samples from during recording. The microphone
|
|
1453
|
+
input device must not be in use when `record()` is called. The audio
|
|
1454
|
+
track will be merged with the video upon calling `save()`. Make sure
|
|
1455
|
+
that `Microphone.maxRecordingSize` is specified to a reasonable value to
|
|
1456
|
+
prevent the audio track from being truncated. Specifying a microphone
|
|
1457
|
+
adds some latency to starting and stopping camera recording due to the
|
|
1458
|
+
added overhead involved with synchronizing the audio and video streams.
|
|
1459
|
+
frameRate : int or None
|
|
1460
|
+
Frame rate to record the camera stream at. If `None`, the camera's
|
|
1461
|
+
default frame rate will be used.
|
|
1462
|
+
frameSize : tuple or None
|
|
1463
|
+
Size (width, height) of the camera stream frames to record. If `None`,
|
|
1464
|
+
the camera's default frame size will be used.
|
|
1465
|
+
cameraLib : str
|
|
1466
|
+
Interface library (backend) to use for accessing the camera. May either
|
|
1467
|
+
be `ffpyplayer` or `opencv`. If `None`, the default library for the
|
|
1468
|
+
recommended by the PsychoPy developers will be used. Switching camera
|
|
1469
|
+
libraries could help resolve issues with camera compatibility. More
|
|
1470
|
+
camera libraries may be installed via extension packages.
|
|
1471
|
+
bufferSecs : float
|
|
1472
|
+
Size of the real-time camera stream buffer specified in seconds (only
|
|
1473
|
+
valid on Windows and MacOS). This is not the same as the recording
|
|
1474
|
+
buffer size. This option might not be available for all camera
|
|
1475
|
+
libraries.
|
|
1476
|
+
win : :class:`~psychopy.visual.Window` or None
|
|
1477
|
+
Optional window associated with this camera. Some functionality may
|
|
1478
|
+
require an OpenGL context for presenting frames to the screen. If you
|
|
1479
|
+
are not planning to display the camera stream, this parameter can be
|
|
1480
|
+
safely ignored.
|
|
1481
|
+
name : str
|
|
1482
|
+
Label for the camera for logging purposes.
|
|
1483
|
+
|
|
1484
|
+
Examples
|
|
1485
|
+
--------
|
|
1486
|
+
Opening a camera stream and closing it::
|
|
1487
|
+
|
|
1488
|
+
camera = Camera(device=0)
|
|
1489
|
+
camera.open() # exception here on invalid camera
|
|
1490
|
+
camera.close()
|
|
1491
|
+
|
|
1492
|
+
Recording 5 seconds of video and saving it to disk::
|
|
1493
|
+
|
|
1494
|
+
cam = Camera(0)
|
|
1495
|
+
cam.open()
|
|
1496
|
+
cam.record() # starts recording
|
|
1497
|
+
|
|
1498
|
+
while cam.recordingTime < 5.0: # record for 5 seconds
|
|
1499
|
+
if event.getKeys('q'):
|
|
1500
|
+
break
|
|
1501
|
+
cam.update()
|
|
1502
|
+
|
|
1503
|
+
cam.stop() # stops recording
|
|
1504
|
+
cam.save('myVideo.mp4')
|
|
1505
|
+
cam.close()
|
|
1506
|
+
|
|
1507
|
+
Providing a microphone as follows enables audio recording::
|
|
1508
|
+
|
|
1509
|
+
mic = Microphone(0)
|
|
1510
|
+
cam = Camera(0, mic=mic)
|
|
1511
|
+
|
|
1512
|
+
Overriding the default frame rate and size (if `cameraLib` supports it)::
|
|
1513
|
+
|
|
1514
|
+
cam = Camera(0, frameRate=30, frameSize=(640, 480), cameraLib=u'opencv')
|
|
1515
|
+
|
|
1516
|
+
"""
|
|
1517
|
+
def __init__(self, device=0, mic=None, cameraLib=u'ffpyplayer',
|
|
1518
|
+
frameRate=None, frameSize=None, bufferSecs=4, win=None,
|
|
1519
|
+
name='cam'):
|
|
1520
|
+
# add attributes for setters
|
|
1521
|
+
self.__dict__.update(
|
|
1522
|
+
{'_device': None,
|
|
1523
|
+
'_captureThread': None,
|
|
1524
|
+
'_mic': None,
|
|
1525
|
+
'_outFile': None,
|
|
1526
|
+
'_mode': u'video',
|
|
1527
|
+
'_frameRate': None,
|
|
1528
|
+
'_frameRateFrac': None,
|
|
1529
|
+
'_size': None,
|
|
1530
|
+
'_cameraLib': u''})
|
|
1531
|
+
|
|
1532
|
+
# ----------------------------------------------------------------------
|
|
1533
|
+
# Process camera settings
|
|
1534
|
+
#
|
|
1535
|
+
|
|
1536
|
+
# camera library in use
|
|
1537
|
+
self._cameraLib = cameraLib
|
|
1538
|
+
|
|
1539
|
+
if self._cameraLib == u'opencv':
|
|
1540
|
+
if device in (None, "None", "none", "Default", "default"):
|
|
1541
|
+
device = 0 # use the first enumerated camera
|
|
1542
|
+
|
|
1543
|
+
# handle all possible input for `frameRate` and `frameSize`
|
|
1544
|
+
if frameRate is None:
|
|
1545
|
+
pass # no change
|
|
1546
|
+
elif isinstance(frameRate, str):
|
|
1547
|
+
if frameRate in ("None", "none", "Default", "default"):
|
|
1548
|
+
frameRate = None
|
|
1549
|
+
elif frameRate.lower() == 'ntsc':
|
|
1550
|
+
frameRate = CAMERA_FRAMERATE_NTSC
|
|
1551
|
+
else:
|
|
1552
|
+
try: # try and convert to float
|
|
1553
|
+
frameRate = float(frameRate)
|
|
1554
|
+
except ValueError:
|
|
1555
|
+
raise ValueError(
|
|
1556
|
+
"`frameRate` must be a number, string or None")
|
|
1557
|
+
|
|
1558
|
+
# catch the value converted to float and process it
|
|
1559
|
+
if isinstance(frameRate, (int, float)):
|
|
1560
|
+
if frameRate <= 0:
|
|
1561
|
+
raise ValueError("`frameRate` must be a positive number")
|
|
1562
|
+
|
|
1563
|
+
if frameSize is None:
|
|
1564
|
+
pass # use the camera default
|
|
1565
|
+
elif isinstance(frameSize, str):
|
|
1566
|
+
if frameSize in ("None", "none", "Default", "default"):
|
|
1567
|
+
frameSize = None
|
|
1568
|
+
elif len(frameSize.split('x')) == 2:
|
|
1569
|
+
frameSize = tuple(map(int, frameSize.split('x')))
|
|
1570
|
+
elif frameSize.upper() in movietools.VIDEO_RESOLUTIONS.keys():
|
|
1571
|
+
frameSize = movietools.VIDEO_RESOLUTIONS[frameSize.upper()]
|
|
1572
|
+
else:
|
|
1573
|
+
raise ValueError("`frameSize` specified incorrectly")
|
|
1574
|
+
elif isinstance(frameSize, (tuple, list)):
|
|
1575
|
+
if len(frameSize) != 2:
|
|
1576
|
+
raise ValueError("`frameSize` must be a 2-tuple or 2-list")
|
|
1577
|
+
frameSize = tuple(map(int, frameSize))
|
|
1578
|
+
else:
|
|
1579
|
+
raise ValueError("`frameSize` specified incorrectly")
|
|
1580
|
+
|
|
1581
|
+
# recommended camera drivers for each platform
|
|
1582
|
+
cameraPlatformDrivers = {
|
|
1583
|
+
'Linux': CAMERA_API_VIDEO4LINUX2,
|
|
1584
|
+
'Windows': CAMERA_API_DIRECTSHOW,
|
|
1585
|
+
'Darwin': CAMERA_API_AVFOUNDATION
|
|
1586
|
+
}
|
|
1587
|
+
# get the recommended camera driver for the current platform
|
|
1588
|
+
cameraAPI = cameraPlatformDrivers[platform.system()]
|
|
1589
|
+
|
|
1590
|
+
self._cameraInfo = CameraInfo(
|
|
1591
|
+
index=device,
|
|
1592
|
+
frameRate=frameRate, # dummy value
|
|
1593
|
+
frameSize=frameSize, # dummy value
|
|
1594
|
+
pixelFormat='bgr24',
|
|
1595
|
+
cameraLib=cameraLib,
|
|
1596
|
+
cameraAPI=cameraAPI)
|
|
1597
|
+
|
|
1598
|
+
self._device = self._cameraInfo.description()
|
|
1599
|
+
|
|
1600
|
+
elif self._cameraLib == u'ffpyplayer':
|
|
1601
|
+
supportedCameraSettings = CameraInterfaceFFmpeg.getCameras()
|
|
1602
|
+
|
|
1603
|
+
# create a mapping of supported camera formats
|
|
1604
|
+
_formatMapping = dict()
|
|
1605
|
+
for _, formats in supportedCameraSettings.items():
|
|
1606
|
+
for _format in formats:
|
|
1607
|
+
desc = _format.description()
|
|
1608
|
+
_formatMapping[desc] = _format
|
|
1609
|
+
# sort formats by resolution then frame rate
|
|
1610
|
+
orderedFormats = list(_formatMapping.values())
|
|
1611
|
+
orderedFormats.sort(key=lambda obj: obj.frameRate, reverse=True)
|
|
1612
|
+
orderedFormats.sort(key=lambda obj: np.prod(obj.frameSize),
|
|
1613
|
+
reverse=True)
|
|
1614
|
+
|
|
1615
|
+
# list of devices
|
|
1616
|
+
devList = list(_formatMapping)
|
|
1617
|
+
|
|
1618
|
+
if not devList: # no cameras found if list is empty
|
|
1619
|
+
raise CameraNotFoundError('No cameras found of the system!')
|
|
1620
|
+
|
|
1621
|
+
# Get best device
|
|
1622
|
+
bestDevice = _formatMapping[devList[-1]]
|
|
1623
|
+
for mode in orderedFormats:
|
|
1624
|
+
sameFrameRate = mode.frameRate == frameRate or frameRate is None
|
|
1625
|
+
sameFrameSize = mode.frameSize == frameSize or frameSize is None
|
|
1626
|
+
if sameFrameRate and sameFrameSize:
|
|
1627
|
+
bestDevice = mode
|
|
1628
|
+
break
|
|
1629
|
+
|
|
1630
|
+
# if given just device name, use frameRate and frameSize to match it
|
|
1631
|
+
# to a mode
|
|
1632
|
+
if device in supportedCameraSettings:
|
|
1633
|
+
match = None
|
|
1634
|
+
for mode in supportedCameraSettings[device]:
|
|
1635
|
+
sameFrameRate = \
|
|
1636
|
+
mode.frameRate == frameRate or frameRate is None
|
|
1637
|
+
sameFrameSize = \
|
|
1638
|
+
mode.frameSize == frameSize or frameSize is None
|
|
1639
|
+
if sameFrameRate and sameFrameSize:
|
|
1640
|
+
match = mode
|
|
1641
|
+
if match is not None:
|
|
1642
|
+
device = match
|
|
1643
|
+
else:
|
|
1644
|
+
# if no match found, find closest
|
|
1645
|
+
byWidth = sorted(
|
|
1646
|
+
supportedCameraSettings[device],
|
|
1647
|
+
key=lambda mode: abs(frameSize[0] - mode.frameSize[0])
|
|
1648
|
+
)
|
|
1649
|
+
byHeight = sorted(
|
|
1650
|
+
supportedCameraSettings[device],
|
|
1651
|
+
key=lambda mode: abs(frameSize[1] - mode.frameSize[1])
|
|
1652
|
+
)
|
|
1653
|
+
byFrameRate = sorted(
|
|
1654
|
+
supportedCameraSettings[device],
|
|
1655
|
+
key=lambda mode: abs(mode.frameRate)
|
|
1656
|
+
)
|
|
1657
|
+
deltas = [
|
|
1658
|
+
byWidth.index(mode) + byHeight.index(mode) + byFrameRate.index(mode)
|
|
1659
|
+
for mode in supportedCameraSettings[device]
|
|
1660
|
+
]
|
|
1661
|
+
i = deltas.index(min(deltas))
|
|
1662
|
+
closest = supportedCameraSettings[device][i]
|
|
1663
|
+
# log warning that settings won't match requested
|
|
1664
|
+
logging.warn(_translate(
|
|
1665
|
+
"Device {device} does not support frame rate of "
|
|
1666
|
+
"{frameRate} and frame size of {frameSize}, using "
|
|
1667
|
+
"closest supported format: {desc}"
|
|
1668
|
+
).format(device=device,
|
|
1669
|
+
frameRate=frameRate,
|
|
1670
|
+
frameSize=frameSize,
|
|
1671
|
+
desc=closest.description()))
|
|
1672
|
+
# use closest
|
|
1673
|
+
device = closest
|
|
1674
|
+
|
|
1675
|
+
# self._origDevSpecifier = device # what the user provided
|
|
1676
|
+
self._device = None # device identifier
|
|
1677
|
+
|
|
1678
|
+
# alias device None or Default as being device 0
|
|
1679
|
+
if device in (None, "None", "none", "Default", "default"):
|
|
1680
|
+
self._device = bestDevice.description()
|
|
1681
|
+
elif isinstance(device, CameraInfo):
|
|
1682
|
+
if self._cameraLib != device.cameraLib:
|
|
1683
|
+
raise CameraFormatNotSupportedError(
|
|
1684
|
+
'Wrong configuration for camera library!')
|
|
1685
|
+
self._device = device.description()
|
|
1686
|
+
else:
|
|
1687
|
+
# resolve getting the camera identifier
|
|
1688
|
+
if isinstance(device, int): # get camera if integer
|
|
1689
|
+
try:
|
|
1690
|
+
self._device = devList[device]
|
|
1691
|
+
except IndexError:
|
|
1692
|
+
raise CameraNotFoundError(
|
|
1693
|
+
'Cannot find camera at index={}'.format(device))
|
|
1694
|
+
elif isinstance(device, str):
|
|
1695
|
+
self._device = device
|
|
1696
|
+
else:
|
|
1697
|
+
raise TypeError(
|
|
1698
|
+
f"Incorrect type for `camera`, expected `int` or `str` but received {repr(device)}")
|
|
1699
|
+
|
|
1700
|
+
# get the camera information
|
|
1701
|
+
if self._device in _formatMapping:
|
|
1702
|
+
self._cameraInfo = _formatMapping[self._device]
|
|
1703
|
+
else:
|
|
1704
|
+
# raise error if couldn't find matching camera info
|
|
1705
|
+
raise CameraFormatNotSupportedError(
|
|
1706
|
+
f'Specified camera format {repr(self._device)} is not supported.')
|
|
1707
|
+
|
|
1708
|
+
# # operating mode
|
|
1709
|
+
# if mode not in (CAMERA_MODE_VIDEO, CAMERA_MODE_CV, CAMERA_MODE_PHOTO):
|
|
1710
|
+
# raise ValueError(
|
|
1711
|
+
# "Invalid value for parameter `mode`, expected one of `'video'` "
|
|
1712
|
+
# "`'cv'` or `'photo'`.")
|
|
1713
|
+
# self._mode = mode
|
|
1714
|
+
|
|
1715
|
+
_requestedMic = mic
|
|
1716
|
+
# if not given a Microphone or MicrophoneDevice, get it from DeviceManager
|
|
1717
|
+
if not isinstance(mic, (Microphone, MicrophoneDevice)):
|
|
1718
|
+
mic = DeviceManager.getDevice(mic)
|
|
1719
|
+
# if not known by name, try index
|
|
1720
|
+
if mic is None:
|
|
1721
|
+
mic = DeviceManager.getDeviceBy("index", mic, deviceClass="microphone")
|
|
1722
|
+
# if not known by name or index, raise error
|
|
1723
|
+
if mic is None:
|
|
1724
|
+
raise SystemError(f"Could not find microphone {_requestedMic}")
|
|
1725
|
+
|
|
1726
|
+
# current camera frame since the start of recording
|
|
1727
|
+
self._player = None # media player instance
|
|
1728
|
+
self._status = NOT_STARTED
|
|
1729
|
+
self._isRecording = False
|
|
1730
|
+
self._bufferSecs = float(bufferSecs)
|
|
1731
|
+
self._lastFrame = None # use None to avoid imports for ImageStim
|
|
1732
|
+
|
|
1733
|
+
# microphone instance, this is controlled by the camera interface and
|
|
1734
|
+
# is not meant to be used by the user
|
|
1735
|
+
self.mic = mic
|
|
1736
|
+
# other information
|
|
1737
|
+
self.name = name
|
|
1738
|
+
# timestamp data
|
|
1739
|
+
self._streamTime = 0.0
|
|
1740
|
+
# store win (unused but needs to be set/got safely for parity with JS)
|
|
1741
|
+
self.win = win
|
|
1742
|
+
|
|
1743
|
+
# movie writer instance, this runs in a separate thread
|
|
1744
|
+
self._movieWriter = None
|
|
1745
|
+
# if we begin receiving frames, change this flag to `True`
|
|
1746
|
+
self._captureThread = None
|
|
1747
|
+
# self._audioThread = None
|
|
1748
|
+
self._captureFrames = [] # array for storing frames
|
|
1749
|
+
# thread for polling the microphone
|
|
1750
|
+
self._audioTrack = None # audio track from the recent recording
|
|
1751
|
+
# used to sync threads spawned by this class, created on `open()`
|
|
1752
|
+
self._syncBarrier = None
|
|
1753
|
+
# keep track of the last video file saved
|
|
1754
|
+
self._lastVideoFile = None
|
|
1755
|
+
|
|
1756
|
+
def authorize(self):
|
|
1757
|
+
"""Get permission to access the camera. Not implemented locally yet.
|
|
1758
|
+
"""
|
|
1759
|
+
pass # NOP
|
|
1760
|
+
|
|
1761
|
+
@property
|
|
1762
|
+
def isReady(self):
|
|
1763
|
+
"""Is the camera ready (`bool`)?
|
|
1764
|
+
|
|
1765
|
+
The camera is ready when the following conditions are met. First, we've
|
|
1766
|
+
created a player interface and opened it. Second, we have received
|
|
1767
|
+
metadata about the stream. At this point we can assume that the camera
|
|
1768
|
+
is 'hot' and the stream is being read.
|
|
1769
|
+
|
|
1770
|
+
This is a legacy property used to support older versions of PsychoPy.
|
|
1771
|
+
The `isOpened` property should be used instead.
|
|
1772
|
+
|
|
1773
|
+
"""
|
|
1774
|
+
return self.isStarted
|
|
1775
|
+
|
|
1776
|
+
@property
|
|
1777
|
+
def frameSize(self):
|
|
1778
|
+
"""Size of the video frame obtained from recent metadata (`float` or
|
|
1779
|
+
`None`).
|
|
1780
|
+
|
|
1781
|
+
Only valid after an `open()` and successive `_enqueueFrame()` call as
|
|
1782
|
+
metadata needs to be obtained from the stream. Returns `None` if not
|
|
1783
|
+
valid.
|
|
1784
|
+
"""
|
|
1785
|
+
if self._cameraInfo is None:
|
|
1786
|
+
return None
|
|
1787
|
+
|
|
1788
|
+
return self._cameraInfo.frameSize
|
|
1789
|
+
|
|
1790
|
+
@property
|
|
1791
|
+
def frameRate(self):
|
|
1792
|
+
"""Frame rate of the video stream (`float` or `None`).
|
|
1793
|
+
|
|
1794
|
+
Only valid after an `open()` and successive `_enqueueFrame()` call as
|
|
1795
|
+
metadata needs to be obtained from the stream. Returns `None` if not
|
|
1796
|
+
valid.
|
|
1797
|
+
|
|
1798
|
+
"""
|
|
1799
|
+
if self._cameraInfo is None:
|
|
1800
|
+
return None
|
|
1801
|
+
|
|
1802
|
+
return self._cameraInfo.frameRate
|
|
1803
|
+
|
|
1804
|
+
def _assertCameraReady(self):
|
|
1805
|
+
"""Assert that the camera is ready. Raises a `CameraNotReadyError` if
|
|
1806
|
+
the camera is not ready.
|
|
1807
|
+
"""
|
|
1808
|
+
if not self.isReady:
|
|
1809
|
+
raise CameraNotReadyError("Camera is not ready.")
|
|
1810
|
+
|
|
1811
|
+
@property
|
|
1812
|
+
def isRecording(self):
|
|
1813
|
+
"""`True` if the video is presently recording (`bool`)."""
|
|
1814
|
+
# Status flags as properties are pretty useful for users since they are
|
|
1815
|
+
# self documenting and prevent the user from touching the status flag
|
|
1816
|
+
# attribute directly.
|
|
1817
|
+
#
|
|
1818
|
+
return self._isRecording
|
|
1819
|
+
|
|
1820
|
+
@property
|
|
1821
|
+
def isStarted(self):
|
|
1822
|
+
"""`True` if the stream has started (`bool`). This status is given after
|
|
1823
|
+
`open()` has been called on this object.
|
|
1824
|
+
"""
|
|
1825
|
+
if self._captureThread is None:
|
|
1826
|
+
return False
|
|
1827
|
+
|
|
1828
|
+
return self._captureThread.isOpen()
|
|
1829
|
+
|
|
1830
|
+
@property
|
|
1831
|
+
def isNotStarted(self):
|
|
1832
|
+
"""`True` if the stream may not have started yet (`bool`). This status
|
|
1833
|
+
is given before `open()` or after `close()` has been called on this
|
|
1834
|
+
object.
|
|
1835
|
+
"""
|
|
1836
|
+
return not self.isStarted
|
|
1837
|
+
|
|
1838
|
+
@property
|
|
1839
|
+
def isStopped(self):
|
|
1840
|
+
"""`True` if the recording has stopped (`bool`). This does not mean that
|
|
1841
|
+
the stream has stopped, `getVideoFrame()` will still yield frames until
|
|
1842
|
+
`close()` is called.
|
|
1843
|
+
"""
|
|
1844
|
+
return not self._isRecording
|
|
1845
|
+
|
|
1846
|
+
@property
|
|
1847
|
+
def metadata(self):
|
|
1848
|
+
"""Video metadata retrieved during the last frame update
|
|
1849
|
+
(`MovieMetadata`).
|
|
1850
|
+
"""
|
|
1851
|
+
return self.getMetadata()
|
|
1852
|
+
|
|
1853
|
+
def getMetadata(self):
|
|
1854
|
+
"""Get stream metadata.
|
|
1855
|
+
|
|
1856
|
+
Returns
|
|
1857
|
+
-------
|
|
1858
|
+
MovieMetadata
|
|
1859
|
+
Metadata about the video stream, retrieved during the last frame
|
|
1860
|
+
update (`_enqueueFrame` call).
|
|
1861
|
+
|
|
1862
|
+
"""
|
|
1863
|
+
return self._recentMetadata
|
|
1864
|
+
|
|
1865
|
+
# @property
|
|
1866
|
+
# def mode(self):
|
|
1867
|
+
# """Operating mode in use for this camera.
|
|
1868
|
+
# """
|
|
1869
|
+
# return self._mode
|
|
1870
|
+
|
|
1871
|
+
_getCamerasCache = {}
|
|
1872
|
+
|
|
1873
|
+
@staticmethod
|
|
1874
|
+
def getCameras(cameraLib=None):
|
|
1875
|
+
"""Get information about installed cameras on this system.
|
|
1876
|
+
|
|
1877
|
+
Returns
|
|
1878
|
+
-------
|
|
1879
|
+
dict
|
|
1880
|
+
Mapping of camera information objects.
|
|
1881
|
+
|
|
1882
|
+
"""
|
|
1883
|
+
# not pluggable yet, needs to be made available via extensions
|
|
1884
|
+
if cameraLib == 'opencv':
|
|
1885
|
+
if 'opencv' not in Camera._getCamerasCache:
|
|
1886
|
+
Camera._getCamerasCache['opencv'] = \
|
|
1887
|
+
CameraInterfaceOpenCV.getCameras()
|
|
1888
|
+
return Camera._getCamerasCache['opencv']
|
|
1889
|
+
elif cameraLib == 'ffpyplayer':
|
|
1890
|
+
if 'ffpyplayer' not in Camera._getCamerasCache:
|
|
1891
|
+
Camera._getCamerasCache['ffpyplayer'] = \
|
|
1892
|
+
CameraInterfaceFFmpeg.getCameras()
|
|
1893
|
+
return Camera._getCamerasCache['ffpyplayer']
|
|
1894
|
+
else:
|
|
1895
|
+
raise ValueError("Invalid value for parameter `cameraLib`")
|
|
1896
|
+
|
|
1897
|
+
@staticmethod
|
|
1898
|
+
def getAvailableDevices():
|
|
1899
|
+
devices = []
|
|
1900
|
+
for dev in st.getCameras():
|
|
1901
|
+
for spec in dev:
|
|
1902
|
+
devices.append({
|
|
1903
|
+
'device': spec['index'],
|
|
1904
|
+
'frameRate': spec['frameRate'],
|
|
1905
|
+
'frameSize': spec['frameSize'],
|
|
1906
|
+
})
|
|
1907
|
+
|
|
1908
|
+
return devices
|
|
1909
|
+
|
|
1910
|
+
@staticmethod
|
|
1911
|
+
def getCameraDescriptions(collapse=False):
|
|
1912
|
+
"""Get a mapping or list of camera descriptions.
|
|
1913
|
+
|
|
1914
|
+
Camera descriptions are a compact way of representing camera settings
|
|
1915
|
+
and formats. Description strings can be used to specify which camera
|
|
1916
|
+
device and format to use with it to the `Camera` class.
|
|
1917
|
+
|
|
1918
|
+
Descriptions have the following format (example)::
|
|
1919
|
+
|
|
1920
|
+
'[Live! Cam Sync 1080p] 160x120@30fps, mjpeg'
|
|
1921
|
+
|
|
1922
|
+
This shows a specific camera format for the 'Live! Cam Sync 1080p'
|
|
1923
|
+
webcam which supports 160x120 frame size at 30 frames per second. The
|
|
1924
|
+
last value is the codec or pixel format used to decode the stream.
|
|
1925
|
+
Different pixel formats and codecs vary in performance.
|
|
1926
|
+
|
|
1927
|
+
Parameters
|
|
1928
|
+
----------
|
|
1929
|
+
collapse : bool
|
|
1930
|
+
Return camera information as string descriptions instead of
|
|
1931
|
+
`CameraInfo` objects. This provides a more compact way of
|
|
1932
|
+
representing camera formats in a (reasonably) human-readable format.
|
|
1933
|
+
|
|
1934
|
+
Returns
|
|
1935
|
+
-------
|
|
1936
|
+
dict or list
|
|
1937
|
+
Mapping (`dict`) of camera descriptions, where keys are camera names
|
|
1938
|
+
(`str`) and values are a `list` of format description strings
|
|
1939
|
+
associated with the camera. If `collapse=True`, all descriptions
|
|
1940
|
+
will be returned in a single flat list. This might be more useful
|
|
1941
|
+
for specifying camera formats from a single GUI list control.
|
|
1942
|
+
|
|
1943
|
+
"""
|
|
1944
|
+
return getCameraDescriptions(collapse=collapse)
|
|
1945
|
+
|
|
1946
|
+
@property
|
|
1947
|
+
def status(self):
|
|
1948
|
+
"""Status flag for the camera (`int`).
|
|
1949
|
+
|
|
1950
|
+
Can be either `RECORDING`, `STOPPED`, `STOPPING`, or `NOT_STARTED`. This
|
|
1951
|
+
property used in Builder output scripts and does not update on its own.
|
|
1952
|
+
|
|
1953
|
+
"""
|
|
1954
|
+
return self._status
|
|
1955
|
+
|
|
1956
|
+
@status.setter
|
|
1957
|
+
def status(self, value):
|
|
1958
|
+
self._status = value
|
|
1959
|
+
|
|
1960
|
+
@property
|
|
1961
|
+
def device(self):
|
|
1962
|
+
"""Camera to use (`str` or `None`).
|
|
1963
|
+
|
|
1964
|
+
String specifying the name of the camera to open a stream with. This
|
|
1965
|
+
must be set prior to calling `start()`. If the name is not valid, an
|
|
1966
|
+
error will be raised when `start()` is called.
|
|
1967
|
+
|
|
1968
|
+
"""
|
|
1969
|
+
return self._device
|
|
1970
|
+
|
|
1971
|
+
@device.setter
|
|
1972
|
+
def device(self, value):
|
|
1973
|
+
if value in (None, "None", "none", "Default", "default"):
|
|
1974
|
+
value = 0
|
|
1975
|
+
|
|
1976
|
+
self._device = value
|
|
1977
|
+
|
|
1978
|
+
@property
|
|
1979
|
+
def _hasPlayer(self):
|
|
1980
|
+
"""`True` if we have an active media player instance.
|
|
1981
|
+
"""
|
|
1982
|
+
# deprecated - remove in future versions and use `isStarted` instead
|
|
1983
|
+
return self.isStarted
|
|
1984
|
+
|
|
1985
|
+
@property
|
|
1986
|
+
def mic(self):
|
|
1987
|
+
"""Microphone to record audio samples from during recording
|
|
1988
|
+
(:class:`~psychopy.sound.microphone.Microphone` or `None`).
|
|
1989
|
+
|
|
1990
|
+
If `None`, no audio will be recorded. Cannot be set after opening a
|
|
1991
|
+
camera stream.
|
|
1992
|
+
"""
|
|
1993
|
+
return self._mic
|
|
1994
|
+
|
|
1995
|
+
@mic.setter
|
|
1996
|
+
def mic(self, value):
|
|
1997
|
+
if self.isStarted:
|
|
1998
|
+
raise CameraError("Cannot set microphone after starting camera.")
|
|
1999
|
+
|
|
2000
|
+
self._mic = value
|
|
2001
|
+
|
|
2002
|
+
@property
|
|
2003
|
+
def _hasAudio(self):
|
|
2004
|
+
"""`True` if we have a microphone object for audio recording.
|
|
2005
|
+
"""
|
|
2006
|
+
return self._mic is not None
|
|
2007
|
+
|
|
2008
|
+
@property
|
|
2009
|
+
def win(self):
|
|
2010
|
+
"""Window which frames are being presented (`psychopy.visual.Window` or
|
|
2011
|
+
`None`).
|
|
2012
|
+
"""
|
|
2013
|
+
return self._win
|
|
2014
|
+
|
|
2015
|
+
@win.setter
|
|
2016
|
+
def win(self, value):
|
|
2017
|
+
self._win = value
|
|
2018
|
+
|
|
2019
|
+
@property
|
|
2020
|
+
def frameCount(self):
|
|
2021
|
+
"""Number of frames captured in the present recording (`int`).
|
|
2022
|
+
"""
|
|
2023
|
+
if not self._isRecording:
|
|
2024
|
+
return 0
|
|
2025
|
+
|
|
2026
|
+
totalFramesBuffered = (
|
|
2027
|
+
len(self._captureFrames) + self._captureThread.framesWaiting)
|
|
2028
|
+
|
|
2029
|
+
return totalFramesBuffered
|
|
2030
|
+
|
|
2031
|
+
@property
|
|
2032
|
+
def streamTime(self):
|
|
2033
|
+
"""Current stream time in seconds (`float`). This time increases
|
|
2034
|
+
monotonically from startup.
|
|
2035
|
+
|
|
2036
|
+
This is `-1.0` if there is no active stream running or if the backend
|
|
2037
|
+
does not support this feature.
|
|
2038
|
+
|
|
2039
|
+
"""
|
|
2040
|
+
if self.isStarted and hasattr(self._captureThread, "streamTime"):
|
|
2041
|
+
return self._captureThread.streamTime
|
|
2042
|
+
|
|
2043
|
+
return -1.0
|
|
2044
|
+
|
|
2045
|
+
@property
|
|
2046
|
+
def recordingTime(self):
|
|
2047
|
+
"""Current recording timestamp (`float`).
|
|
2048
|
+
|
|
2049
|
+
This returns the timestamp of the last frame captured in the recording.
|
|
2050
|
+
|
|
2051
|
+
This value increases monotonically from the last `record()` call. It
|
|
2052
|
+
will reset once `stop()` is called. This value is invalid outside
|
|
2053
|
+
`record()` and `stop()` calls.
|
|
2054
|
+
|
|
2055
|
+
"""
|
|
2056
|
+
if not self._isRecording:
|
|
2057
|
+
return 0.0
|
|
2058
|
+
|
|
2059
|
+
frameInterval = 1.0 / float(self._captureThread.frameRate)
|
|
2060
|
+
|
|
2061
|
+
return self.frameCount * frameInterval
|
|
2062
|
+
|
|
2063
|
+
@property
|
|
2064
|
+
def recordingBytes(self):
|
|
2065
|
+
"""Current size of the recording in bytes (`int`).
|
|
2066
|
+
"""
|
|
2067
|
+
if not self._isRecording:
|
|
2068
|
+
return 0
|
|
2069
|
+
|
|
2070
|
+
return self._captureThread.recordingBytes
|
|
2071
|
+
|
|
2072
|
+
def _assertMediaPlayer(self):
|
|
2073
|
+
"""Assert that we have a media player instance open.
|
|
2074
|
+
|
|
2075
|
+
This will raise a `RuntimeError` if there is no player open. Use this
|
|
2076
|
+
function to ensure that a player is present before running subsequent
|
|
2077
|
+
code.
|
|
2078
|
+
"""
|
|
2079
|
+
if self._captureThread is not None:
|
|
2080
|
+
return
|
|
2081
|
+
|
|
2082
|
+
raise PlayerNotAvailableError('Media player not initialized.')
|
|
2083
|
+
|
|
2084
|
+
def _enqueueFrame(self):
|
|
2085
|
+
"""Pull waiting frames from the capture thread.
|
|
2086
|
+
|
|
2087
|
+
This function will pull frames from the capture thread and add them to
|
|
2088
|
+
the buffer. The last frame in the buffer will be set as the most recent
|
|
2089
|
+
frame (`lastFrame`).
|
|
2090
|
+
|
|
2091
|
+
Returns
|
|
2092
|
+
-------
|
|
2093
|
+
bool
|
|
2094
|
+
`True` if a frame has been enqueued. Returns `False` if the camera
|
|
2095
|
+
is not ready or if the stream was closed.
|
|
2096
|
+
|
|
2097
|
+
"""
|
|
2098
|
+
if self._captureThread is None:
|
|
2099
|
+
return False
|
|
2100
|
+
|
|
2101
|
+
newFrames = self._captureThread.getFrames()
|
|
2102
|
+
if not newFrames:
|
|
2103
|
+
return False
|
|
2104
|
+
|
|
2105
|
+
# add frames the the buffer
|
|
2106
|
+
self._captureFrames.extend(newFrames)
|
|
2107
|
+
|
|
2108
|
+
# set the last frame in the buffer as the most recent
|
|
2109
|
+
self._lastFrame = self._captureFrames[-1]
|
|
2110
|
+
|
|
2111
|
+
return True
|
|
2112
|
+
|
|
2113
|
+
def open(self):
|
|
2114
|
+
"""Open the camera stream and begin decoding frames (if available).
|
|
2115
|
+
|
|
2116
|
+
This function returns when the camera is ready to start getting
|
|
2117
|
+
frames.
|
|
2118
|
+
|
|
2119
|
+
Call `record()` to start recording frames to memory. Captured frames
|
|
2120
|
+
came be saved to disk using `save()`.
|
|
2121
|
+
|
|
2122
|
+
"""
|
|
2123
|
+
if self._hasPlayer:
|
|
2124
|
+
raise RuntimeError('Cannot open `MediaPlayer`, already opened.')
|
|
2125
|
+
|
|
2126
|
+
# Camera interface to use, these are hard coded but support for each is
|
|
2127
|
+
# provided by an extension.
|
|
2128
|
+
desc = self._cameraInfo.description()
|
|
2129
|
+
if self._cameraLib == u'ffpyplayer':
|
|
2130
|
+
logging.debug(
|
|
2131
|
+
"Opening camera stream using FFmpeg. (device={})".format(desc))
|
|
2132
|
+
self._captureThread = CameraInterfaceFFmpeg(
|
|
2133
|
+
device=self._cameraInfo,
|
|
2134
|
+
mic=self._mic)
|
|
2135
|
+
elif self._cameraLib == u'opencv':
|
|
2136
|
+
logging.debug(
|
|
2137
|
+
"Opening camera stream using OpenCV. (device={})".format(desc))
|
|
2138
|
+
self._captureThread = CameraInterfaceOpenCV(
|
|
2139
|
+
device=self._cameraInfo,
|
|
2140
|
+
mic=self._mic)
|
|
2141
|
+
else:
|
|
2142
|
+
raise ValueError(
|
|
2143
|
+
"Invalid value for parameter `cameraLib`, expected one of "
|
|
2144
|
+
"`'ffpyplayer'` or `'opencv'`.")
|
|
2145
|
+
|
|
2146
|
+
self._captureThread.open()
|
|
2147
|
+
|
|
2148
|
+
# def snapshot(self):
|
|
2149
|
+
# """Take a photo with the camera. The camera must be in `'photo'` mode
|
|
2150
|
+
# to use this method.
|
|
2151
|
+
# """
|
|
2152
|
+
# pass
|
|
2153
|
+
|
|
2154
|
+
def record(self):
|
|
2155
|
+
"""Start recording frames.
|
|
2156
|
+
|
|
2157
|
+
This function will start recording frames and audio (if available). The
|
|
2158
|
+
value of `lastFrame` will be updated as new frames arrive and the
|
|
2159
|
+
`frameCount` will increase. You can access image data for the most
|
|
2160
|
+
recent frame to be captured using `lastFrame`.
|
|
2161
|
+
|
|
2162
|
+
If this is called before `open()` the camera stream will be opened
|
|
2163
|
+
automatically. This is not recommended as it may incur a longer than
|
|
2164
|
+
expected delay in the recording start time.
|
|
2165
|
+
|
|
2166
|
+
Warnings
|
|
2167
|
+
--------
|
|
2168
|
+
If a recording has been previously made without calling `save()` it will
|
|
2169
|
+
be discarded if `record()` is called again.
|
|
2170
|
+
|
|
2171
|
+
"""
|
|
2172
|
+
if self.isNotStarted:
|
|
2173
|
+
self.open() # open the camera stream if we call record() first
|
|
2174
|
+
logging.warning(
|
|
2175
|
+
"Called `Camera.record()` before opening the camera stream, "
|
|
2176
|
+
"opening now. This is not recommended as it may incur a longer "
|
|
2177
|
+
"than expected delay in the recording start time."
|
|
2178
|
+
)
|
|
2179
|
+
|
|
2180
|
+
self._audioTrack = None
|
|
2181
|
+
self._lastFrame = None
|
|
2182
|
+
|
|
2183
|
+
# start recording audio if available
|
|
2184
|
+
if self._mic is not None:
|
|
2185
|
+
logging.debug(
|
|
2186
|
+
"Microphone interface available, starting audio recording.")
|
|
2187
|
+
else:
|
|
2188
|
+
logging.debug(
|
|
2189
|
+
"No microphone interface provided, not recording audio.")
|
|
2190
|
+
|
|
2191
|
+
self._captureThread.enable() # start passing frames to queue
|
|
2192
|
+
self._enqueueFrame()
|
|
2193
|
+
|
|
2194
|
+
self._isRecording = True
|
|
2195
|
+
|
|
2196
|
+
def stop(self):
|
|
2197
|
+
"""Stop recording frames and audio (if available).
|
|
2198
|
+
"""
|
|
2199
|
+
if not self._captureThread.isOpen():
|
|
2200
|
+
raise RuntimeError("Cannot stop recording, stream is not open.")
|
|
2201
|
+
|
|
2202
|
+
self._captureThread.disable() # stop passing frames to queue
|
|
2203
|
+
self._enqueueFrame()
|
|
2204
|
+
|
|
2205
|
+
# # stop audio recording if `mic` is available
|
|
2206
|
+
if self._mic is not None:
|
|
2207
|
+
self._audioTrack = self._mic.getRecording()
|
|
2208
|
+
|
|
2209
|
+
self._isRecording = False
|
|
2210
|
+
|
|
2211
|
+
def close(self):
|
|
2212
|
+
"""Close the camera.
|
|
2213
|
+
|
|
2214
|
+
This will close the camera stream and free up any resources used by the
|
|
2215
|
+
device. If the camera is currently recording, this will stop the
|
|
2216
|
+
recording, but will not discard any frames. You may still call `save()`
|
|
2217
|
+
to save the frames to disk.
|
|
2218
|
+
|
|
2219
|
+
"""
|
|
2220
|
+
if not self._captureThread.isOpen():
|
|
2221
|
+
raise RuntimeError("Cannot close stream, stream is not open.")
|
|
2222
|
+
|
|
2223
|
+
if self._isRecording:
|
|
2224
|
+
logging.warning(
|
|
2225
|
+
"Closing camera stream while recording, stopping recording "
|
|
2226
|
+
"first.")
|
|
2227
|
+
self.stop()
|
|
2228
|
+
|
|
2229
|
+
self._captureThread.close()
|
|
2230
|
+
self._captureThread = None
|
|
2231
|
+
|
|
2232
|
+
def save(self, filename, useThreads=True, mergeAudio=True,
|
|
2233
|
+
encoderLib=None, encoderOpts=None):
|
|
2234
|
+
"""Save the last recording to file.
|
|
2235
|
+
|
|
2236
|
+
This will write frames to `filename` acquired since the last call of
|
|
2237
|
+
`record()` and subsequent `stop()`. If `record()` is called again before
|
|
2238
|
+
`save()`, the previous recording will be deleted and lost.
|
|
2239
|
+
|
|
2240
|
+
This is a slow operation and will block for some time depending on the
|
|
2241
|
+
length of the video. This can be sped up by setting `useThreads=True`.
|
|
2242
|
+
|
|
2243
|
+
Parameters
|
|
2244
|
+
----------
|
|
2245
|
+
filename : str
|
|
2246
|
+
File to save the resulting video to, should include the extension.
|
|
2247
|
+
useThreads : bool
|
|
2248
|
+
Use threading where possible to speed up the saving process. If
|
|
2249
|
+
`True`, the video will be saved and composited in a separate thread
|
|
2250
|
+
and this function will return quickly. If `False`, the video will
|
|
2251
|
+
be saved and composited in the main thread and this function will
|
|
2252
|
+
block until the video is saved. Default is `True`.
|
|
2253
|
+
mergeAudio : bool
|
|
2254
|
+
Merge the audio track from the microphone with the video. If `True`,
|
|
2255
|
+
the audio track will be merged with the video. If `False`, the
|
|
2256
|
+
audio track will be saved to a separate file. Default is `True`.
|
|
2257
|
+
encoderLib : str or None
|
|
2258
|
+
Encoder library to use for saving the video. This can be either
|
|
2259
|
+
`'ffpyplayer'` or `'opencv'`. If `None`, the same library that was
|
|
2260
|
+
used to open the camera stream. Default is `None`.
|
|
2261
|
+
encoderOpts : dict
|
|
2262
|
+
Options to pass to the encoder. This is a dictionary of options
|
|
2263
|
+
specific to the encoder library being used. See the documentation
|
|
2264
|
+
for `~psychopy.tools.movietools.MovieFileWriter` for more details.
|
|
2265
|
+
|
|
2266
|
+
"""
|
|
2267
|
+
if self._isRecording:
|
|
2268
|
+
raise RuntimeError(
|
|
2269
|
+
"Attempting to call `save()` before calling `stop()`.")
|
|
2270
|
+
|
|
2271
|
+
# check if a file exists at the given path, if so, delete it
|
|
2272
|
+
if os.path.exists(filename):
|
|
2273
|
+
msg = (
|
|
2274
|
+
"Video file '{}' already exists, overwriting.".format(filename))
|
|
2275
|
+
logging.warning(msg)
|
|
2276
|
+
os.remove(filename)
|
|
2277
|
+
|
|
2278
|
+
# determine if the `encoderLib` to use
|
|
2279
|
+
if encoderLib is None:
|
|
2280
|
+
encoderLib = self._cameraLib
|
|
2281
|
+
|
|
2282
|
+
logging.debug(
|
|
2283
|
+
"Using encoder library '{}' to save video.".format(encoderLib))
|
|
2284
|
+
|
|
2285
|
+
# check if the encoder library name string is valid
|
|
2286
|
+
if encoderLib not in ('ffpyplayer', 'opencv'):
|
|
2287
|
+
raise ValueError(
|
|
2288
|
+
"Invalid value for parameter `encoderLib`, expected one of "
|
|
2289
|
+
"`'ffpyplayer'` or `'opencv'`.")
|
|
2290
|
+
|
|
2291
|
+
# check if we have an audio track to save
|
|
2292
|
+
hasAudio = self._audioTrack is not None
|
|
2293
|
+
|
|
2294
|
+
# create a temporary file names for the video and audio
|
|
2295
|
+
if hasAudio:
|
|
2296
|
+
if mergeAudio:
|
|
2297
|
+
tempPrefix = (uuid.uuid4().hex)[:16] # 16 char prefix
|
|
2298
|
+
videoFileName = "{}_video.mp4".format(tempPrefix)
|
|
2299
|
+
audioFileName = "{}_audio.wav".format(tempPrefix)
|
|
2300
|
+
else:
|
|
2301
|
+
videoFileName = audioFileName = filename
|
|
2302
|
+
audioFileName += '.wav'
|
|
2303
|
+
else:
|
|
2304
|
+
videoFileName = filename
|
|
2305
|
+
audioFileName = None
|
|
2306
|
+
|
|
2307
|
+
# make sure filenames are absolute paths
|
|
2308
|
+
videoFileName = os.path.abspath(videoFileName)
|
|
2309
|
+
if audioFileName is not None:
|
|
2310
|
+
audioFileName = os.path.abspath(audioFileName)
|
|
2311
|
+
|
|
2312
|
+
# flush outstanding frames from the camera queue
|
|
2313
|
+
self._enqueueFrame()
|
|
2314
|
+
|
|
2315
|
+
# contain video and not audio
|
|
2316
|
+
logging.debug("Saving video to file: {}".format(videoFileName))
|
|
2317
|
+
self._movieWriter = movietools.MovieFileWriter(
|
|
2318
|
+
filename=videoFileName,
|
|
2319
|
+
size=self._cameraInfo.frameSize, # match camera params
|
|
2320
|
+
fps=self._cameraInfo.frameRate,
|
|
2321
|
+
codec=None, # mp4
|
|
2322
|
+
pixelFormat='rgb24',
|
|
2323
|
+
encoderLib=encoderLib,
|
|
2324
|
+
encoderOpts=encoderOpts)
|
|
2325
|
+
self._movieWriter.open() # blocks main thread until opened and ready
|
|
2326
|
+
|
|
2327
|
+
# flush remaining frames to the writer thread, this is really fast since
|
|
2328
|
+
# frames are not copied and don't require much conversion
|
|
2329
|
+
for frame in self._captureFrames:
|
|
2330
|
+
self._movieWriter.addFrame(frame.colorData)
|
|
2331
|
+
|
|
2332
|
+
# push all frames to the queue for the movie recorder
|
|
2333
|
+
self._movieWriter.close() # thread-safe call
|
|
2334
|
+
self._movieWriter = None
|
|
2335
|
+
|
|
2336
|
+
# save audio track if available
|
|
2337
|
+
if hasAudio:
|
|
2338
|
+
logging.debug(
|
|
2339
|
+
"Saving audio track to file: {}".format(audioFileName))
|
|
2340
|
+
self._audioTrack.save(audioFileName, 'wav')
|
|
2341
|
+
|
|
2342
|
+
# merge audio and video tracks
|
|
2343
|
+
if mergeAudio:
|
|
2344
|
+
logging.debug("Merging audio and video tracks.")
|
|
2345
|
+
movietools.addAudioToMovie(
|
|
2346
|
+
filename, # file after merging
|
|
2347
|
+
videoFileName,
|
|
2348
|
+
audioFileName,
|
|
2349
|
+
useThreads=useThreads,
|
|
2350
|
+
removeFiles=True) # disable threading for now
|
|
2351
|
+
|
|
2352
|
+
self._lastVideoFile = filename # remember the last video we saved
|
|
2353
|
+
|
|
2354
|
+
def _upload(self):
|
|
2355
|
+
"""Upload video file to an online repository. Not implemented locally,
|
|
2356
|
+
needed for auto translate to JS.
|
|
2357
|
+
"""
|
|
2358
|
+
pass # NOP
|
|
2359
|
+
|
|
2360
|
+
def _download(self):
|
|
2361
|
+
"""Download video file to an online repository. Not implemented locally,
|
|
2362
|
+
needed for auto translate to JS.
|
|
2363
|
+
"""
|
|
2364
|
+
pass # NOP
|
|
2365
|
+
|
|
2366
|
+
@property
|
|
2367
|
+
def lastClip(self):
|
|
2368
|
+
"""File path to the last recording (`str` or `None`).
|
|
2369
|
+
|
|
2370
|
+
This value is only valid if a previous recording has been saved
|
|
2371
|
+
successfully (`save()` was called), otherwise it will be set to `None`.
|
|
2372
|
+
|
|
2373
|
+
"""
|
|
2374
|
+
return self.getLastClip()
|
|
2375
|
+
|
|
2376
|
+
def getLastClip(self):
|
|
2377
|
+
"""File path to the last saved recording.
|
|
2378
|
+
|
|
2379
|
+
This value is only valid if a previous recording has been saved to disk
|
|
2380
|
+
(`save()` was called).
|
|
2381
|
+
|
|
2382
|
+
Returns
|
|
2383
|
+
-------
|
|
2384
|
+
str or None
|
|
2385
|
+
Path to the file the most recent call to `save()` created. Returns
|
|
2386
|
+
`None` if no file is ready.
|
|
2387
|
+
|
|
2388
|
+
"""
|
|
2389
|
+
return self._lastVideoFile
|
|
2390
|
+
|
|
2391
|
+
@property
|
|
2392
|
+
def lastFrame(self):
|
|
2393
|
+
"""Most recent frame pulled from the camera (`VideoFrame`) since the
|
|
2394
|
+
last call of `getVideoFrame`.
|
|
2395
|
+
"""
|
|
2396
|
+
return self._lastFrame
|
|
2397
|
+
|
|
2398
|
+
def update(self):
|
|
2399
|
+
"""Acquire the newest data from the camera stream. If the `Camera`
|
|
2400
|
+
object is not being monitored by a `ImageStim`, this must be explicitly
|
|
2401
|
+
called.
|
|
2402
|
+
"""
|
|
2403
|
+
self._assertMediaPlayer()
|
|
2404
|
+
self._enqueueFrame()
|
|
2405
|
+
|
|
2406
|
+
def getVideoFrame(self):
|
|
2407
|
+
"""Pull the most recent frame from the stream (if available).
|
|
2408
|
+
|
|
2409
|
+
Returns
|
|
2410
|
+
-------
|
|
2411
|
+
MovieFrame
|
|
2412
|
+
Most recent video frame. Returns `NULL_MOVIE_FRAME_INFO` if no
|
|
2413
|
+
frame was available, or we timed out.
|
|
2414
|
+
|
|
2415
|
+
"""
|
|
2416
|
+
self.update()
|
|
2417
|
+
|
|
2418
|
+
return self._lastFrame
|
|
2419
|
+
|
|
2420
|
+
def __del__(self):
|
|
2421
|
+
"""Try to cleanly close the camera and output file.
|
|
2422
|
+
"""
|
|
2423
|
+
|
|
2424
|
+
if hasattr(self, '_captureThread'):
|
|
2425
|
+
if self._captureThread is not None:
|
|
2426
|
+
try:
|
|
2427
|
+
self._captureThread.close()
|
|
2428
|
+
except AttributeError:
|
|
2429
|
+
pass
|
|
2430
|
+
|
|
2431
|
+
# close the microphone during teardown too
|
|
2432
|
+
if hasattr(self, '_mic'):
|
|
2433
|
+
if self._mic is not None:
|
|
2434
|
+
try:
|
|
2435
|
+
self._mic.close()
|
|
2436
|
+
except AttributeError:
|
|
2437
|
+
pass
|
|
2438
|
+
|
|
2439
|
+
|
|
2440
|
+
DeviceManager.registerClassAlias("camera", "psychopy.hardware.camera.Camera")
|
|
2441
|
+
|
|
2442
|
+
|
|
2443
|
+
# ------------------------------------------------------------------------------
|
|
2444
|
+
# Functions
|
|
2445
|
+
#
|
|
2446
|
+
|
|
2447
|
+
def _getCameraInfoMacOS():
|
|
2448
|
+
"""Get a list of capabilities associated with a camera attached to the
|
|
2449
|
+
system.
|
|
2450
|
+
|
|
2451
|
+
This is used by `getCameraInfo()` for querying camera details on MacOS.
|
|
2452
|
+
Don't call this function directly unless testing.
|
|
2453
|
+
|
|
2454
|
+
Returns
|
|
2455
|
+
-------
|
|
2456
|
+
list of CameraInfo
|
|
2457
|
+
List of camera descriptors.
|
|
2458
|
+
|
|
2459
|
+
"""
|
|
2460
|
+
if platform.system() != 'Darwin':
|
|
2461
|
+
raise OSError(
|
|
2462
|
+
"Cannot query cameras with this function, platform not 'Darwin'.")
|
|
2463
|
+
|
|
2464
|
+
# import objc # may be needed in the future for more advanced stuff
|
|
2465
|
+
import AVFoundation as avf # only works on MacOS
|
|
2466
|
+
import CoreMedia as cm
|
|
2467
|
+
|
|
2468
|
+
# get a list of capture devices
|
|
2469
|
+
allDevices = avf.AVCaptureDevice.devices()
|
|
2470
|
+
|
|
2471
|
+
# get video devices
|
|
2472
|
+
videoDevices = {}
|
|
2473
|
+
devIdx = 0
|
|
2474
|
+
for device in allDevices:
|
|
2475
|
+
devFormats = device.formats()
|
|
2476
|
+
if devFormats[0].mediaType() != 'vide': # not a video device
|
|
2477
|
+
continue
|
|
2478
|
+
|
|
2479
|
+
# camera details
|
|
2480
|
+
cameraName = device.localizedName()
|
|
2481
|
+
|
|
2482
|
+
# found video formats
|
|
2483
|
+
supportedFormats = []
|
|
2484
|
+
for _format in devFormats:
|
|
2485
|
+
# get the format description object
|
|
2486
|
+
formatDesc = _format.formatDescription()
|
|
2487
|
+
|
|
2488
|
+
# get dimensions in pixels of the video format
|
|
2489
|
+
dimensions = cm.CMVideoFormatDescriptionGetDimensions(formatDesc)
|
|
2490
|
+
frameHeight = dimensions.height
|
|
2491
|
+
frameWidth = dimensions.width
|
|
2492
|
+
|
|
2493
|
+
# Extract the codec in use, pretty useless since FFMPEG uses its
|
|
2494
|
+
# own conventions, we'll need to map these ourselves to those
|
|
2495
|
+
# values
|
|
2496
|
+
codecType = cm.CMFormatDescriptionGetMediaSubType(formatDesc)
|
|
2497
|
+
|
|
2498
|
+
# Convert codec code to a FourCC code using the following byte
|
|
2499
|
+
# operations.
|
|
2500
|
+
#
|
|
2501
|
+
# fourCC = ((codecCode >> 24) & 0xff,
|
|
2502
|
+
# (codecCode >> 16) & 0xff,
|
|
2503
|
+
# (codecCode >> 8) & 0xff,
|
|
2504
|
+
# codecCode & 0xff)
|
|
2505
|
+
#
|
|
2506
|
+
pixelFormat4CC = ''.join(
|
|
2507
|
+
[chr((codecType >> bits) & 0xff) for bits in (24, 16, 8, 0)])
|
|
2508
|
+
|
|
2509
|
+
# Get the range of supported framerate, use the largest since the
|
|
2510
|
+
# ranges are rarely variable within a format.
|
|
2511
|
+
frameRateRange = _format.videoSupportedFrameRateRanges()[0]
|
|
2512
|
+
frameRateMax = frameRateRange.maxFrameRate()
|
|
2513
|
+
# frameRateMin = frameRateRange.minFrameRate() # don't use for now
|
|
2514
|
+
|
|
2515
|
+
# Create a new camera descriptor
|
|
2516
|
+
thisCamInfo = CameraInfo(
|
|
2517
|
+
index=devIdx,
|
|
2518
|
+
name=cameraName,
|
|
2519
|
+
pixelFormat=pixelFormat4CC, # macs only use pixel format
|
|
2520
|
+
codecFormat=CAMERA_NULL_VALUE,
|
|
2521
|
+
frameSize=(int(frameWidth), int(frameHeight)),
|
|
2522
|
+
frameRate=frameRateMax,
|
|
2523
|
+
cameraAPI=CAMERA_API_AVFOUNDATION,
|
|
2524
|
+
cameraLib="ffpyplayer",
|
|
2525
|
+
)
|
|
2526
|
+
|
|
2527
|
+
supportedFormats.append(thisCamInfo)
|
|
2528
|
+
|
|
2529
|
+
devIdx += 1
|
|
2530
|
+
|
|
2531
|
+
# add to output dictionary
|
|
2532
|
+
videoDevices[cameraName] = supportedFormats
|
|
2533
|
+
|
|
2534
|
+
return videoDevices
|
|
2535
|
+
|
|
2536
|
+
|
|
2537
|
+
def _getCameraInfoWindows():
|
|
2538
|
+
"""Get a list of capabilities for the specified associated with a camera
|
|
2539
|
+
attached to the system.
|
|
2540
|
+
|
|
2541
|
+
This is used by `getCameraInfo()` for querying camera details on Windows.
|
|
2542
|
+
Don't call this function directly unless testing.
|
|
2543
|
+
|
|
2544
|
+
Returns
|
|
2545
|
+
-------
|
|
2546
|
+
list of CameraInfo
|
|
2547
|
+
List of camera descriptors.
|
|
2548
|
+
|
|
2549
|
+
"""
|
|
2550
|
+
if platform.system() != 'Windows':
|
|
2551
|
+
raise OSError(
|
|
2552
|
+
"Cannot query cameras with this function, platform not 'Windows'.")
|
|
2553
|
+
|
|
2554
|
+
# FFPyPlayer can query the OS via DirectShow for Windows cameras
|
|
2555
|
+
from ffpyplayer.tools import list_dshow_devices
|
|
2556
|
+
videoDevs, _, names = list_dshow_devices()
|
|
2557
|
+
|
|
2558
|
+
# get all the supported modes for the camera
|
|
2559
|
+
videoDevices = {}
|
|
2560
|
+
|
|
2561
|
+
# iterate over names
|
|
2562
|
+
devIndex = 0
|
|
2563
|
+
for devURI in videoDevs.keys():
|
|
2564
|
+
supportedFormats = []
|
|
2565
|
+
cameraName = names[devURI]
|
|
2566
|
+
for _format in videoDevs[devURI]:
|
|
2567
|
+
pixelFormat, codecFormat, frameSize, frameRateRng = _format
|
|
2568
|
+
_, frameRateMax = frameRateRng
|
|
2569
|
+
temp = CameraInfo(
|
|
2570
|
+
index=devIndex,
|
|
2571
|
+
name=cameraName,
|
|
2572
|
+
pixelFormat=pixelFormat,
|
|
2573
|
+
codecFormat=codecFormat,
|
|
2574
|
+
frameSize=frameSize,
|
|
2575
|
+
frameRate=frameRateMax,
|
|
2576
|
+
cameraAPI=CAMERA_API_DIRECTSHOW,
|
|
2577
|
+
cameraLib="ffpyplayer",
|
|
2578
|
+
)
|
|
2579
|
+
supportedFormats.append(temp)
|
|
2580
|
+
devIndex += 1
|
|
2581
|
+
|
|
2582
|
+
videoDevices[names[devURI]] = supportedFormats
|
|
2583
|
+
|
|
2584
|
+
return videoDevices
|
|
2585
|
+
|
|
2586
|
+
|
|
2587
|
+
# Mapping for platform specific camera getter functions used by `getCameras`.
|
|
2588
|
+
_cameraGetterFuncTbl = {
|
|
2589
|
+
'Darwin': _getCameraInfoMacOS,
|
|
2590
|
+
'Windows': _getCameraInfoWindows
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
|
|
2594
|
+
def getCameras():
|
|
2595
|
+
"""Get information about installed cameras and their formats on this system.
|
|
2596
|
+
|
|
2597
|
+
Use `getCameraDescriptions` to get a mapping or list of human-readable
|
|
2598
|
+
camera formats.
|
|
2599
|
+
|
|
2600
|
+
Returns
|
|
2601
|
+
-------
|
|
2602
|
+
dict
|
|
2603
|
+
Mapping where camera names (`str`) are keys and values are and array of
|
|
2604
|
+
`CameraInfo` objects.
|
|
2605
|
+
|
|
2606
|
+
"""
|
|
2607
|
+
systemName = platform.system() # get the system name
|
|
2608
|
+
|
|
2609
|
+
# lookup the function for the given platform
|
|
2610
|
+
getCamerasFunc = _cameraGetterFuncTbl.get(systemName, None)
|
|
2611
|
+
if getCamerasFunc is None: # if unsupported
|
|
2612
|
+
raise OSError(
|
|
2613
|
+
"Cannot get cameras, unsupported platform '{}'.".format(
|
|
2614
|
+
systemName))
|
|
2615
|
+
|
|
2616
|
+
return getCamerasFunc()
|
|
2617
|
+
|
|
2618
|
+
|
|
2619
|
+
def getCameraDescriptions(collapse=False):
|
|
2620
|
+
"""Get a mapping or list of camera descriptions.
|
|
2621
|
+
|
|
2622
|
+
Camera descriptions are a compact way of representing camera settings and
|
|
2623
|
+
formats. Description strings can be used to specify which camera device and
|
|
2624
|
+
format to use with it to the `Camera` class.
|
|
2625
|
+
|
|
2626
|
+
Descriptions have the following format (example)::
|
|
2627
|
+
|
|
2628
|
+
'[Live! Cam Sync 1080p] 160x120@30fps, mjpeg'
|
|
2629
|
+
|
|
2630
|
+
This shows a specific camera format for the 'Live! Cam Sync 1080p' webcam
|
|
2631
|
+
which supports 160x120 frame size at 30 frames per second. The last value
|
|
2632
|
+
is the codec or pixel format used to decode the stream. Different pixel
|
|
2633
|
+
formats and codecs vary in performance.
|
|
2634
|
+
|
|
2635
|
+
Parameters
|
|
2636
|
+
----------
|
|
2637
|
+
collapse : bool
|
|
2638
|
+
Return camera information as string descriptions instead of `CameraInfo`
|
|
2639
|
+
objects. This provides a more compact way of representing camera formats
|
|
2640
|
+
in a (reasonably) human-readable format.
|
|
2641
|
+
|
|
2642
|
+
Returns
|
|
2643
|
+
-------
|
|
2644
|
+
dict or list
|
|
2645
|
+
Mapping (`dict`) of camera descriptions, where keys are camera names
|
|
2646
|
+
(`str`) and values are a `list` of format description strings associated
|
|
2647
|
+
with the camera. If `collapse=True`, all descriptions will be returned
|
|
2648
|
+
in a single flat list. This might be more useful for specifying camera
|
|
2649
|
+
formats from a single GUI list control.
|
|
2650
|
+
|
|
2651
|
+
"""
|
|
2652
|
+
connectedCameras = getCameras()
|
|
2653
|
+
|
|
2654
|
+
cameraDescriptions = {}
|
|
2655
|
+
for devName, formats in connectedCameras.items():
|
|
2656
|
+
cameraDescriptions[devName] = [
|
|
2657
|
+
_format.description() for _format in formats]
|
|
2658
|
+
|
|
2659
|
+
if not collapse:
|
|
2660
|
+
return cameraDescriptions
|
|
2661
|
+
|
|
2662
|
+
# collapse to a list if requested
|
|
2663
|
+
collapsedList = []
|
|
2664
|
+
for _, formatDescs in cameraDescriptions.items():
|
|
2665
|
+
collapsedList.extend(formatDescs)
|
|
2666
|
+
|
|
2667
|
+
return collapsedList
|
|
2668
|
+
|
|
2669
|
+
|
|
2670
|
+
def getFormatsForDevice(device):
|
|
2671
|
+
"""Get a list of formats available for the given device.
|
|
2672
|
+
|
|
2673
|
+
Parameters
|
|
2674
|
+
----------
|
|
2675
|
+
device : str or int
|
|
2676
|
+
Name or index of the device
|
|
2677
|
+
|
|
2678
|
+
Returns
|
|
2679
|
+
-------
|
|
2680
|
+
list
|
|
2681
|
+
List of formats, specified as strings in the format
|
|
2682
|
+
`{width}x{height}@{frame rate}fps`
|
|
2683
|
+
"""
|
|
2684
|
+
# get all devices
|
|
2685
|
+
connectedCameras = getCameras()
|
|
2686
|
+
# get formats for this device
|
|
2687
|
+
formats = connectedCameras.get(device, [])
|
|
2688
|
+
# sanitize
|
|
2689
|
+
formats = [f"{_format.frameSize[0]}x{_format.frameSize[1]}@{_format.frameRate}fps" for _format in formats]
|
|
2690
|
+
|
|
2691
|
+
return formats
|
|
2692
|
+
|
|
2693
|
+
|
|
2694
|
+
def getAllCameraInterfaces():
|
|
2695
|
+
"""Get a list of all camera interfaces supported by the system.
|
|
2696
|
+
|
|
2697
|
+
Returns
|
|
2698
|
+
-------
|
|
2699
|
+
dict
|
|
2700
|
+
Mapping of camera interface class names and references to the class.
|
|
2701
|
+
|
|
2702
|
+
"""
|
|
2703
|
+
# get all classes in this module
|
|
2704
|
+
classes = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
|
2705
|
+
|
|
2706
|
+
# filter for classes that are camera interfaces
|
|
2707
|
+
cameraInterfaces = {}
|
|
2708
|
+
for name, cls in classes:
|
|
2709
|
+
if issubclass(cls, CameraInterface):
|
|
2710
|
+
cameraInterfaces[name] = cls
|
|
2711
|
+
|
|
2712
|
+
return cameraInterfaces
|
|
2713
|
+
|
|
2714
|
+
|
|
2715
|
+
def getOpenCameras():
|
|
2716
|
+
"""Get a list of all open cameras.
|
|
2717
|
+
|
|
2718
|
+
Returns
|
|
2719
|
+
-------
|
|
2720
|
+
list
|
|
2721
|
+
List of references to open camera objects.
|
|
2722
|
+
|
|
2723
|
+
"""
|
|
2724
|
+
global _openCameras
|
|
2725
|
+
|
|
2726
|
+
return _openCameras.copy()
|
|
2727
|
+
|
|
2728
|
+
|
|
2729
|
+
def closeAllOpenCameras():
|
|
2730
|
+
"""Close all open cameras.
|
|
2731
|
+
|
|
2732
|
+
This closes all open cameras and releases any resources associated with
|
|
2733
|
+
them. This should only be called before exiting the application or after you
|
|
2734
|
+
are done using the cameras.
|
|
2735
|
+
|
|
2736
|
+
This is automatically called when the application exits to cleanly free up
|
|
2737
|
+
resources, as it is registered with `atexit` when the module is imported.
|
|
2738
|
+
|
|
2739
|
+
Returns
|
|
2740
|
+
-------
|
|
2741
|
+
int
|
|
2742
|
+
Number of cameras closed. Useful for debugging to ensure all cameras
|
|
2743
|
+
were closed.
|
|
2744
|
+
|
|
2745
|
+
"""
|
|
2746
|
+
global _openCameras
|
|
2747
|
+
|
|
2748
|
+
numCameras = len(_openCameras)
|
|
2749
|
+
for cam in _openCameras:
|
|
2750
|
+
cam.close()
|
|
2751
|
+
|
|
2752
|
+
_openCameras.clear()
|
|
2753
|
+
|
|
2754
|
+
return numCameras
|
|
2755
|
+
|
|
2756
|
+
|
|
2757
|
+
def renderVideo(outputFile, videoFile, audioFile=None, removeFiles=False):
|
|
2758
|
+
"""Render a video.
|
|
2759
|
+
|
|
2760
|
+
Combine visual and audio streams into a single movie file. This is used
|
|
2761
|
+
mainly for compositing video and audio data for the camera. Video and audio
|
|
2762
|
+
should have roughly the same duration.
|
|
2763
|
+
|
|
2764
|
+
This is a legacy function used originally for compositing video and audio
|
|
2765
|
+
data from the camera. It is not used anymore internally, but is kept here
|
|
2766
|
+
for reference and may be removed in the future. If you need to composite
|
|
2767
|
+
video and audio data, use `movietools.addAudioToMovie` instead.
|
|
2768
|
+
|
|
2769
|
+
Parameters
|
|
2770
|
+
----------
|
|
2771
|
+
outputFile : str
|
|
2772
|
+
Filename to write the movie to. Should have the extension of the file
|
|
2773
|
+
too.
|
|
2774
|
+
videoFile : str
|
|
2775
|
+
Video file path.
|
|
2776
|
+
audioFile : str or None
|
|
2777
|
+
Audio file path. If not provided the movie file will simply be copied
|
|
2778
|
+
to `outFile`.
|
|
2779
|
+
removeFiles : bool
|
|
2780
|
+
If `True`, the video (`videoFile`) and audio (`audioFile`) files will be
|
|
2781
|
+
deleted after the movie is rendered.
|
|
2782
|
+
|
|
2783
|
+
Returns
|
|
2784
|
+
-------
|
|
2785
|
+
int
|
|
2786
|
+
Size of the resulting file in bytes.
|
|
2787
|
+
|
|
2788
|
+
"""
|
|
2789
|
+
# if no audio file, just copy the video file
|
|
2790
|
+
if audioFile is None:
|
|
2791
|
+
import shutil
|
|
2792
|
+
shutil.copyfile(videoFile, outputFile)
|
|
2793
|
+
if removeFiles:
|
|
2794
|
+
os.remove(videoFile) # delete the old movie file
|
|
2795
|
+
return os.path.getsize(outputFile)
|
|
2796
|
+
|
|
2797
|
+
# merge video and audio, now using the new `movietools` module
|
|
2798
|
+
movietools.addAudioToMovie(
|
|
2799
|
+
videoFile,
|
|
2800
|
+
audioFile,
|
|
2801
|
+
outputFile,
|
|
2802
|
+
useThreads=False, # didn't use this before
|
|
2803
|
+
removeFiles=removeFiles)
|
|
2804
|
+
|
|
2805
|
+
return os.path.getsize(outputFile)
|
|
2806
|
+
|
|
2807
|
+
|
|
2808
|
+
if __name__ == "__main__":
|
|
2809
|
+
pass
|