psychopy 2025.2.4__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.
Files changed (3127) hide show
  1. psychopy/CHANGELOG.txt +3981 -0
  2. psychopy/GIT_SHA +1 -0
  3. psychopy/LICENSE.txt +11 -0
  4. psychopy/LICENSES.txt +55 -0
  5. psychopy/VERSION +1 -0
  6. psychopy/__init__.py +148 -0
  7. psychopy/alerts/__init__.py +65 -0
  8. psychopy/alerts/_alerts.py +224 -0
  9. psychopy/alerts/_errorHandler.py +50 -0
  10. psychopy/alerts/alertsCatalogue/1000.yaml +0 -0
  11. psychopy/alerts/alertsCatalogue/1100.yaml +0 -0
  12. psychopy/alerts/alertsCatalogue/1500.yaml +0 -0
  13. psychopy/alerts/alertsCatalogue/2000.yaml +0 -0
  14. psychopy/alerts/alertsCatalogue/2100.yaml +0 -0
  15. psychopy/alerts/alertsCatalogue/2110.yaml +0 -0
  16. psychopy/alerts/alertsCatalogue/2115.yaml +21 -0
  17. psychopy/alerts/alertsCatalogue/2120.yaml +22 -0
  18. psychopy/alerts/alertsCatalogue/2150.yaml +0 -0
  19. psychopy/alerts/alertsCatalogue/2155.yaml +21 -0
  20. psychopy/alerts/alertsCatalogue/2300.yaml +0 -0
  21. psychopy/alerts/alertsCatalogue/2500.yaml +0 -0
  22. psychopy/alerts/alertsCatalogue/3000.yaml +0 -0
  23. psychopy/alerts/alertsCatalogue/3100.yaml +0 -0
  24. psychopy/alerts/alertsCatalogue/3110.yaml +25 -0
  25. psychopy/alerts/alertsCatalogue/3115.yaml +24 -0
  26. psychopy/alerts/alertsCatalogue/3200.yaml +0 -0
  27. psychopy/alerts/alertsCatalogue/3210.yaml +27 -0
  28. psychopy/alerts/alertsCatalogue/3400.yaml +0 -0
  29. psychopy/alerts/alertsCatalogue/3600.yaml +0 -0
  30. psychopy/alerts/alertsCatalogue/3610.yaml +19 -0
  31. psychopy/alerts/alertsCatalogue/4000.yaml +0 -0
  32. psychopy/alerts/alertsCatalogue/4051.yaml +19 -0
  33. psychopy/alerts/alertsCatalogue/4052.yaml +19 -0
  34. psychopy/alerts/alertsCatalogue/4100.yaml +0 -0
  35. psychopy/alerts/alertsCatalogue/4105.yaml +18 -0
  36. psychopy/alerts/alertsCatalogue/4115.yaml +21 -0
  37. psychopy/alerts/alertsCatalogue/4120.yaml +19 -0
  38. psychopy/alerts/alertsCatalogue/4125.yaml +23 -0
  39. psychopy/alerts/alertsCatalogue/4130.yaml +19 -0
  40. psychopy/alerts/alertsCatalogue/4160.yaml +25 -0
  41. psychopy/alerts/alertsCatalogue/4200.yaml +0 -0
  42. psychopy/alerts/alertsCatalogue/4205.yaml +19 -0
  43. psychopy/alerts/alertsCatalogue/4210.yaml +19 -0
  44. psychopy/alerts/alertsCatalogue/4305.yaml +21 -0
  45. psychopy/alerts/alertsCatalogue/4310.yaml +21 -0
  46. psychopy/alerts/alertsCatalogue/4315.yaml +21 -0
  47. psychopy/alerts/alertsCatalogue/4320.yaml +19 -0
  48. psychopy/alerts/alertsCatalogue/4325.yaml +23 -0
  49. psychopy/alerts/alertsCatalogue/4330.yaml +19 -0
  50. psychopy/alerts/alertsCatalogue/4335.yaml +19 -0
  51. psychopy/alerts/alertsCatalogue/4340.yaml +19 -0
  52. psychopy/alerts/alertsCatalogue/4405.yaml +24 -0
  53. psychopy/alerts/alertsCatalogue/4505.yaml +22 -0
  54. psychopy/alerts/alertsCatalogue/4510.yaml +25 -0
  55. psychopy/alerts/alertsCatalogue/4520.yaml +26 -0
  56. psychopy/alerts/alertsCatalogue/4530.yaml +23 -0
  57. psychopy/alerts/alertsCatalogue/4540.yaml +19 -0
  58. psychopy/alerts/alertsCatalogue/4545.yaml +22 -0
  59. psychopy/alerts/alertsCatalogue/4550.yaml +19 -0
  60. psychopy/alerts/alertsCatalogue/4605.yaml +22 -0
  61. psychopy/alerts/alertsCatalogue/4610.yaml +22 -0
  62. psychopy/alerts/alertsCatalogue/4615.yaml +19 -0
  63. psychopy/alerts/alertsCatalogue/4705.yaml +19 -0
  64. psychopy/alerts/alertsCatalogue/4710.yaml +19 -0
  65. psychopy/alerts/alertsCatalogue/4810.yaml +19 -0
  66. psychopy/alerts/alertsCatalogue/5000.yaml +0 -0
  67. psychopy/alerts/alertsCatalogue/5055.yaml +25 -0
  68. psychopy/alerts/alertsCatalogue/6000.yaml +0 -0
  69. psychopy/alerts/alertsCatalogue/6105.yaml +18 -0
  70. psychopy/alerts/alertsCatalogue/7105.yaml +19 -0
  71. psychopy/alerts/alertsCatalogue/8105.yaml +21 -0
  72. psychopy/alerts/alertsCatalogue/8110.yaml +21 -0
  73. psychopy/alerts/alertsCatalogue/9998.yaml +19 -0
  74. psychopy/alerts/alertsCatalogue/9999.yaml +19 -0
  75. psychopy/alerts/alertsCatalogue/alertCategories.yaml +123 -0
  76. psychopy/alerts/alertsCatalogue/alertTemplate.yaml +19 -0
  77. psychopy/alerts/alertsCatalogue/alertmsg.py +173 -0
  78. psychopy/alerts/alertsCatalogue/generateAlertmsg.py +34 -0
  79. psychopy/alerts/alerttools.py +322 -0
  80. psychopy/app/Resources/README.md +12 -0
  81. psychopy/app/Resources/__init__.py +0 -0
  82. psychopy/app/Resources/betasplash.png +0 -0
  83. psychopy/app/Resources/betasplash@2x.png +0 -0
  84. psychopy/app/Resources/builder.ico +0 -0
  85. psychopy/app/Resources/classic/FlowBottom_CompLeft.png +0 -0
  86. psychopy/app/Resources/classic/FlowBottom_CompRight.png +0 -0
  87. psychopy/app/Resources/classic/FlowTop_CompLeft.png +0 -0
  88. psychopy/app/Resources/classic/FlowTop_CompRight.png +0 -0
  89. psychopy/app/Resources/classic/README.txt +9 -0
  90. psychopy/app/Resources/classic/__init__.py +0 -0
  91. psychopy/app/Resources/classic/_layouts.ai +1138 -1
  92. psychopy/app/Resources/classic/add.png +0 -0
  93. psychopy/app/Resources/classic/add@2x.png +0 -0
  94. psychopy/app/Resources/classic/addExp32.png +0 -0
  95. psychopy/app/Resources/classic/add_many.png +0 -0
  96. psychopy/app/Resources/classic/add_many@2x.png +0 -0
  97. psychopy/app/Resources/classic/alerts.png +0 -0
  98. psychopy/app/Resources/classic/beta.png +0 -0
  99. psychopy/app/Resources/classic/browser.png +0 -0
  100. psychopy/app/Resources/classic/browser@2x.png +0 -0
  101. psychopy/app/Resources/classic/bug16.png +0 -0
  102. psychopy/app/Resources/classic/case.png +0 -0
  103. psychopy/app/Resources/classic/case@2x.png +0 -0
  104. psychopy/app/Resources/classic/circle_mask.png +0 -0
  105. psychopy/app/Resources/classic/circle_mask@2x.png +0 -0
  106. psychopy/app/Resources/classic/clear.png +0 -0
  107. psychopy/app/Resources/classic/clear@2x.png +0 -0
  108. psychopy/app/Resources/classic/coderclass16.png +0 -0
  109. psychopy/app/Resources/classic/coderfunc16.png +0 -0
  110. psychopy/app/Resources/classic/coderimport16.png +0 -0
  111. psychopy/app/Resources/classic/coderjs16.png +0 -0
  112. psychopy/app/Resources/classic/coderpython16.png +0 -0
  113. psychopy/app/Resources/classic/codervar16.png +0 -0
  114. psychopy/app/Resources/classic/cogwindow32.png +0 -0
  115. psychopy/app/Resources/classic/color32.png +0 -0
  116. psychopy/app/Resources/classic/compile32.png +0 -0
  117. psychopy/app/Resources/classic/compile_js.png +0 -0
  118. psychopy/app/Resources/classic/compile_js@2x.png +0 -0
  119. psychopy/app/Resources/classic/compile_py.png +0 -0
  120. psychopy/app/Resources/classic/compile_py@2x.png +0 -0
  121. psychopy/app/Resources/classic/copy16.png +0 -0
  122. psychopy/app/Resources/classic/currentFile.png +0 -0
  123. psychopy/app/Resources/classic/currentFile@2x.png +0 -0
  124. psychopy/app/Resources/classic/delete16.png +0 -0
  125. psychopy/app/Resources/classic/desktop.png +0 -0
  126. psychopy/app/Resources/classic/desktop@2x.png +0 -0
  127. psychopy/app/Resources/classic/devices.png +0 -0
  128. psychopy/app/Resources/classic/devices@2x.png +0 -0
  129. psychopy/app/Resources/classic/dirup16.png +0 -0
  130. psychopy/app/Resources/classic/docclose16.png +0 -0
  131. psychopy/app/Resources/classic/download.png +0 -0
  132. psychopy/app/Resources/classic/edit.png +0 -0
  133. psychopy/app/Resources/classic/edit@2x.png +0 -0
  134. psychopy/app/Resources/classic/editbtn.png +0 -0
  135. psychopy/app/Resources/classic/editbtn16.png +0 -0
  136. psychopy/app/Resources/classic/editbtn16@2x.png +0 -0
  137. psychopy/app/Resources/classic/editbtn@2x.png +0 -0
  138. psychopy/app/Resources/classic/email.png +0 -0
  139. psychopy/app/Resources/classic/email@2x.png +0 -0
  140. psychopy/app/Resources/classic/experiment.png +0 -0
  141. psychopy/app/Resources/classic/experiment@2x.png +0 -0
  142. psychopy/app/Resources/classic/expsettings.png +0 -0
  143. psychopy/app/Resources/classic/expsettings@2x.png +0 -0
  144. psychopy/app/Resources/classic/file.png +0 -0
  145. psychopy/app/Resources/classic/file@2x.png +0 -0
  146. psychopy/app/Resources/classic/fileaudio.png +0 -0
  147. psychopy/app/Resources/classic/fileaudio@2x.png +0 -0
  148. psychopy/app/Resources/classic/filecss.png +0 -0
  149. psychopy/app/Resources/classic/filecss@2x.png +0 -0
  150. psychopy/app/Resources/classic/filecsv.png +0 -0
  151. psychopy/app/Resources/classic/filecsv@2x.png +0 -0
  152. psychopy/app/Resources/classic/filedesign.png +0 -0
  153. psychopy/app/Resources/classic/filedesign@2x.png +0 -0
  154. psychopy/app/Resources/classic/filefont.png +0 -0
  155. psychopy/app/Resources/classic/filefont@2x.png +0 -0
  156. psychopy/app/Resources/classic/filegit.png +0 -0
  157. psychopy/app/Resources/classic/filegit@2x.png +0 -0
  158. psychopy/app/Resources/classic/filehtml.png +0 -0
  159. psychopy/app/Resources/classic/filehtml@2x.png +0 -0
  160. psychopy/app/Resources/classic/fileimage.png +0 -0
  161. psychopy/app/Resources/classic/fileimage@2x.png +0 -0
  162. psychopy/app/Resources/classic/fileinfo.png +0 -0
  163. psychopy/app/Resources/classic/fileinfo@2x.png +0 -0
  164. psychopy/app/Resources/classic/filejs.png +0 -0
  165. psychopy/app/Resources/classic/filejs@2x.png +0 -0
  166. psychopy/app/Resources/classic/filejson.png +0 -0
  167. psychopy/app/Resources/classic/filejson@2x.png +0 -0
  168. psychopy/app/Resources/classic/filenew.png +0 -0
  169. psychopy/app/Resources/classic/filenew32.png +0 -0
  170. psychopy/app/Resources/classic/filenew@2x.png +0 -0
  171. psychopy/app/Resources/classic/fileopen.png +0 -0
  172. psychopy/app/Resources/classic/fileopen32.png +0 -0
  173. psychopy/app/Resources/classic/fileopen@2x.png +0 -0
  174. psychopy/app/Resources/classic/filepkg.png +0 -0
  175. psychopy/app/Resources/classic/filepkg@2x.png +0 -0
  176. psychopy/app/Resources/classic/filepsyexp.png +0 -0
  177. psychopy/app/Resources/classic/filepsyexp@2x.png +0 -0
  178. psychopy/app/Resources/classic/filepy.png +0 -0
  179. psychopy/app/Resources/classic/filepy@2x.png +0 -0
  180. psychopy/app/Resources/classic/filesave.png +0 -0
  181. psychopy/app/Resources/classic/filesave32.png +0 -0
  182. psychopy/app/Resources/classic/filesave@2x.png +0 -0
  183. psychopy/app/Resources/classic/filesaveas.png +0 -0
  184. psychopy/app/Resources/classic/filesaveas32.png +0 -0
  185. psychopy/app/Resources/classic/filesaveas@2x.png +0 -0
  186. psychopy/app/Resources/classic/filetxt.png +0 -0
  187. psychopy/app/Resources/classic/filetxt@2x.png +0 -0
  188. psychopy/app/Resources/classic/fileunknown.png +0 -0
  189. psychopy/app/Resources/classic/fileunknown@2x.png +0 -0
  190. psychopy/app/Resources/classic/filevideo.png +0 -0
  191. psychopy/app/Resources/classic/filevideo@2x.png +0 -0
  192. psychopy/app/Resources/classic/filter.png +0 -0
  193. psychopy/app/Resources/classic/filter@2x.png +0 -0
  194. psychopy/app/Resources/classic/find.png +0 -0
  195. psychopy/app/Resources/classic/find@2x.png +0 -0
  196. psychopy/app/Resources/classic/folder-open16.png +0 -0
  197. psychopy/app/Resources/classic/folder16.png +0 -0
  198. psychopy/app/Resources/classic/foldernew16.png +0 -0
  199. psychopy/app/Resources/classic/fork.png +0 -0
  200. psychopy/app/Resources/classic/fork@2x.png +0 -0
  201. psychopy/app/Resources/classic/github.png +0 -0
  202. psychopy/app/Resources/classic/github@2x.png +0 -0
  203. psychopy/app/Resources/classic/globe.png +0 -0
  204. psychopy/app/Resources/classic/globe@2x.png +0 -0
  205. psychopy/app/Resources/classic/globe_bug.png +0 -0
  206. psychopy/app/Resources/classic/globe_bug@2x.png +0 -0
  207. psychopy/app/Resources/classic/globe_greensync.png +0 -0
  208. psychopy/app/Resources/classic/globe_greensync@2x.png +0 -0
  209. psychopy/app/Resources/classic/globe_info.png +0 -0
  210. psychopy/app/Resources/classic/globe_info@2x.png +0 -0
  211. psychopy/app/Resources/classic/globe_magnifier.png +0 -0
  212. psychopy/app/Resources/classic/globe_magnifier@2x.png +0 -0
  213. psychopy/app/Resources/classic/globe_run.png +0 -0
  214. psychopy/app/Resources/classic/globe_run@2x.png +0 -0
  215. psychopy/app/Resources/classic/globe_user.png +0 -0
  216. psychopy/app/Resources/classic/globe_user@2x.png +0 -0
  217. psychopy/app/Resources/classic/goto16.png +0 -0
  218. psychopy/app/Resources/classic/greendot.png +0 -0
  219. psychopy/app/Resources/classic/greendot@2x.png +0 -0
  220. psychopy/app/Resources/classic/greenglobe.png +0 -0
  221. psychopy/app/Resources/classic/greenglobe@2x.png +0 -0
  222. psychopy/app/Resources/classic/greenglobe_bug.png +0 -0
  223. psychopy/app/Resources/classic/greenglobe_bug@2x.png +0 -0
  224. psychopy/app/Resources/classic/greenglobe_greensync.png +0 -0
  225. psychopy/app/Resources/classic/greenglobe_greensync@2x.png +0 -0
  226. psychopy/app/Resources/classic/greenglobe_info.png +0 -0
  227. psychopy/app/Resources/classic/greenglobe_info@2x.png +0 -0
  228. psychopy/app/Resources/classic/greenglobe_magnifier.png +0 -0
  229. psychopy/app/Resources/classic/greenglobe_magnifier@2x.png +0 -0
  230. psychopy/app/Resources/classic/greenglobe_run.png +0 -0
  231. psychopy/app/Resources/classic/greenglobe_run@2x.png +0 -0
  232. psychopy/app/Resources/classic/greenglobe_user.png +0 -0
  233. psychopy/app/Resources/classic/greenglobe_user@2x.png +0 -0
  234. psychopy/app/Resources/classic/greydot.png +0 -0
  235. psychopy/app/Resources/classic/greydot@2x.png +0 -0
  236. psychopy/app/Resources/classic/greytick.png +0 -0
  237. psychopy/app/Resources/classic/greytick@2x.png +0 -0
  238. psychopy/app/Resources/classic/invalid_img.png +0 -0
  239. psychopy/app/Resources/classic/jsPilot.png +0 -0
  240. psychopy/app/Resources/classic/jsPilot@2x.png +0 -0
  241. psychopy/app/Resources/classic/jsRun.png +0 -0
  242. psychopy/app/Resources/classic/jsRun@2x.png +0 -0
  243. psychopy/app/Resources/classic/libroot.png +0 -0
  244. psychopy/app/Resources/classic/libroot@2x.png +0 -0
  245. psychopy/app/Resources/classic/loop.png +0 -0
  246. psychopy/app/Resources/classic/loop@2x.png +0 -0
  247. psychopy/app/Resources/classic/monitor16.png +0 -0
  248. psychopy/app/Resources/classic/monitors.png +0 -0
  249. psychopy/app/Resources/classic/monitors16.png +0 -0
  250. psychopy/app/Resources/classic/monitors32.png +0 -0
  251. psychopy/app/Resources/classic/monitors@2x.png +0 -0
  252. psychopy/app/Resources/classic/orangedot.png +0 -0
  253. psychopy/app/Resources/classic/orangedot@2x.png +0 -0
  254. psychopy/app/Resources/classic/pavlovia.png +0 -0
  255. psychopy/app/Resources/classic/pavlovia16.png +0 -0
  256. psychopy/app/Resources/classic/pavlovia16@2x.png +0 -0
  257. psychopy/app/Resources/classic/pavlovia@2x.png +0 -0
  258. psychopy/app/Resources/classic/pavsync.png +0 -0
  259. psychopy/app/Resources/classic/pavsync@2x.png +0 -0
  260. psychopy/app/Resources/classic/person_off.png +0 -0
  261. psychopy/app/Resources/classic/person_off@2x.png +0 -0
  262. psychopy/app/Resources/classic/person_on.png +0 -0
  263. psychopy/app/Resources/classic/person_on@2x.png +0 -0
  264. psychopy/app/Resources/classic/photometer.png +0 -0
  265. psychopy/app/Resources/classic/photometer@2x.png +0 -0
  266. psychopy/app/Resources/classic/plugin16.png +0 -0
  267. psychopy/app/Resources/classic/plugin16@2x.png +0 -0
  268. psychopy/app/Resources/classic/plugins32.png +0 -0
  269. psychopy/app/Resources/classic/plus.png +0 -0
  270. psychopy/app/Resources/classic/plus@2x.png +0 -0
  271. psychopy/app/Resources/classic/preferences-app.png +0 -0
  272. psychopy/app/Resources/classic/preferences-app48.png +0 -0
  273. psychopy/app/Resources/classic/preferences-app@2x.png +0 -0
  274. psychopy/app/Resources/classic/preferences-conn.png +0 -0
  275. psychopy/app/Resources/classic/preferences-conn48.png +0 -0
  276. psychopy/app/Resources/classic/preferences-conn@2x.png +0 -0
  277. psychopy/app/Resources/classic/preferences-debug.png +0 -0
  278. psychopy/app/Resources/classic/preferences-debug@2x.png +0 -0
  279. psychopy/app/Resources/classic/preferences-general.png +0 -0
  280. psychopy/app/Resources/classic/preferences-general48.png +0 -0
  281. psychopy/app/Resources/classic/preferences-general@2x.png +0 -0
  282. psychopy/app/Resources/classic/preferences-hardware.png +0 -0
  283. psychopy/app/Resources/classic/preferences-hardware48.png +0 -0
  284. psychopy/app/Resources/classic/preferences-hardware@2x.png +0 -0
  285. psychopy/app/Resources/classic/preferences-keyboard.png +0 -0
  286. psychopy/app/Resources/classic/preferences-keyboard48.png +0 -0
  287. psychopy/app/Resources/classic/preferences-keyboard@2x.png +0 -0
  288. psychopy/app/Resources/classic/preferences-pilot.png +0 -0
  289. psychopy/app/Resources/classic/preferences-pilot@2x.png +0 -0
  290. psychopy/app/Resources/classic/preferences32.png +0 -0
  291. psychopy/app/Resources/classic/pyPilot.png +0 -0
  292. psychopy/app/Resources/classic/pyPilot@2x.png +0 -0
  293. psychopy/app/Resources/classic/pyRun.png +0 -0
  294. psychopy/app/Resources/classic/pyRun@2x.png +0 -0
  295. psychopy/app/Resources/classic/reddot.png +0 -0
  296. psychopy/app/Resources/classic/reddot@2x.png +0 -0
  297. psychopy/app/Resources/classic/redglobe.png +0 -0
  298. psychopy/app/Resources/classic/redglobe@2x.png +0 -0
  299. psychopy/app/Resources/classic/redglobe_bug.png +0 -0
  300. psychopy/app/Resources/classic/redglobe_bug@2x.png +0 -0
  301. psychopy/app/Resources/classic/redglobe_greensync.png +0 -0
  302. psychopy/app/Resources/classic/redglobe_greensync@2x.png +0 -0
  303. psychopy/app/Resources/classic/redglobe_info.png +0 -0
  304. psychopy/app/Resources/classic/redglobe_info@2x.png +0 -0
  305. psychopy/app/Resources/classic/redglobe_magnifier.png +0 -0
  306. psychopy/app/Resources/classic/redglobe_magnifier@2x.png +0 -0
  307. psychopy/app/Resources/classic/redglobe_run.png +0 -0
  308. psychopy/app/Resources/classic/redglobe_run@2x.png +0 -0
  309. psychopy/app/Resources/classic/redglobe_user.png +0 -0
  310. psychopy/app/Resources/classic/redglobe_user@2x.png +0 -0
  311. psychopy/app/Resources/classic/redo.png +0 -0
  312. psychopy/app/Resources/classic/redo32.png +0 -0
  313. psychopy/app/Resources/classic/redo@2x.png +0 -0
  314. psychopy/app/Resources/classic/regex.png +0 -0
  315. psychopy/app/Resources/classic/regex@2x.png +0 -0
  316. psychopy/app/Resources/classic/removeExp32.png +0 -0
  317. psychopy/app/Resources/classic/rename16.png +0 -0
  318. psychopy/app/Resources/classic/restart.png +0 -0
  319. psychopy/app/Resources/classic/restart@2x.png +0 -0
  320. psychopy/app/Resources/classic/run.png +0 -0
  321. psychopy/app/Resources/classic/run@2x.png +0 -0
  322. psychopy/app/Resources/classic/runner.png +0 -0
  323. psychopy/app/Resources/classic/runner@2x.png +0 -0
  324. psychopy/app/Resources/classic/runnerPilot.png +0 -0
  325. psychopy/app/Resources/classic/runnerPilot@2x.png +0 -0
  326. psychopy/app/Resources/classic/savebtn.png +0 -0
  327. psychopy/app/Resources/classic/savebtn16.png +0 -0
  328. psychopy/app/Resources/classic/savebtn16@2x.png +0 -0
  329. psychopy/app/Resources/classic/savebtn@2x.png +0 -0
  330. psychopy/app/Resources/classic/search.png +0 -0
  331. psychopy/app/Resources/classic/search@2x.png +0 -0
  332. psychopy/app/Resources/classic/showBuilder.png +0 -0
  333. psychopy/app/Resources/classic/showBuilder@2x.png +0 -0
  334. psychopy/app/Resources/classic/showCoder.png +0 -0
  335. psychopy/app/Resources/classic/showCoder@2x.png +0 -0
  336. psychopy/app/Resources/classic/showRunner.png +0 -0
  337. psychopy/app/Resources/classic/showRunner@2x.png +0 -0
  338. psychopy/app/Resources/classic/starred.png +0 -0
  339. psychopy/app/Resources/classic/starred@2x.png +0 -0
  340. psychopy/app/Resources/classic/start.png +0 -0
  341. psychopy/app/Resources/classic/start@2x.png +0 -0
  342. psychopy/app/Resources/classic/stdout.png +0 -0
  343. psychopy/app/Resources/classic/stop.png +0 -0
  344. psychopy/app/Resources/classic/stop32.png +0 -0
  345. psychopy/app/Resources/classic/stop@2x.png +0 -0
  346. psychopy/app/Resources/classic/switchCtrlBot.png +0 -0
  347. psychopy/app/Resources/classic/switchCtrlBot@2x.png +0 -0
  348. psychopy/app/Resources/classic/switchCtrlLeft.png +0 -0
  349. psychopy/app/Resources/classic/switchCtrlLeft@2x.png +0 -0
  350. psychopy/app/Resources/classic/switchCtrlRight.png +0 -0
  351. psychopy/app/Resources/classic/switchCtrlRight@2x.png +0 -0
  352. psychopy/app/Resources/classic/switchCtrlTop.png +0 -0
  353. psychopy/app/Resources/classic/switchCtrlTop@2x.png +0 -0
  354. psychopy/app/Resources/classic/tick.png +0 -0
  355. psychopy/app/Resources/classic/tick@2x.png +0 -0
  356. psychopy/app/Resources/classic/undo.png +0 -0
  357. psychopy/app/Resources/classic/undo32.png +0 -0
  358. psychopy/app/Resources/classic/undo@2x.png +0 -0
  359. psychopy/app/Resources/classic/unstarred.png +0 -0
  360. psychopy/app/Resources/classic/unstarred@2x.png +0 -0
  361. psychopy/app/Resources/classic/user_none.png +0 -0
  362. psychopy/app/Resources/classic/view-refresh16.png +0 -0
  363. psychopy/app/Resources/classic/viewbtn.png +0 -0
  364. psychopy/app/Resources/classic/viewbtn16.png +0 -0
  365. psychopy/app/Resources/classic/viewbtn16@2x.png +0 -0
  366. psychopy/app/Resources/classic/viewbtn@2x.png +0 -0
  367. psychopy/app/Resources/classic/windows16.png +0 -0
  368. psychopy/app/Resources/classic/windows16@2x.png +0 -0
  369. psychopy/app/Resources/coder.ico +0 -0
  370. psychopy/app/Resources/dark/FlowBottom_CompLeft.png +0 -0
  371. psychopy/app/Resources/dark/FlowBottom_CompRight.png +0 -0
  372. psychopy/app/Resources/dark/FlowTop_CompLeft.png +0 -0
  373. psychopy/app/Resources/dark/FlowTop_CompRight.png +0 -0
  374. psychopy/app/Resources/dark/README.txt +3 -0
  375. psychopy/app/Resources/dark/__init__.py +0 -0
  376. psychopy/app/Resources/dark/add.png +0 -0
  377. psychopy/app/Resources/dark/add@2x.png +0 -0
  378. psychopy/app/Resources/dark/addExp32.png +0 -0
  379. psychopy/app/Resources/dark/addExp32@2x.png +0 -0
  380. psychopy/app/Resources/dark/add_many.png +0 -0
  381. psychopy/app/Resources/dark/add_many@2x.png +0 -0
  382. psychopy/app/Resources/dark/alerts.png +0 -0
  383. psychopy/app/Resources/dark/alerts@2x.png +0 -0
  384. psychopy/app/Resources/dark/beta.png +0 -0
  385. psychopy/app/Resources/dark/beta@2x.png +0 -0
  386. psychopy/app/Resources/dark/browser.png +0 -0
  387. psychopy/app/Resources/dark/browser@2x.png +0 -0
  388. psychopy/app/Resources/dark/bug16.png +0 -0
  389. psychopy/app/Resources/dark/bug16@2x.png +0 -0
  390. psychopy/app/Resources/dark/case.png +0 -0
  391. psychopy/app/Resources/dark/case@2x.png +0 -0
  392. psychopy/app/Resources/dark/circle_mask.png +0 -0
  393. psychopy/app/Resources/dark/circle_mask@2x.png +0 -0
  394. psychopy/app/Resources/dark/clear.png +0 -0
  395. psychopy/app/Resources/dark/clear@2x.png +0 -0
  396. psychopy/app/Resources/dark/coderclass16.png +0 -0
  397. psychopy/app/Resources/dark/coderclass16@2x.png +0 -0
  398. psychopy/app/Resources/dark/coderfunc16.png +0 -0
  399. psychopy/app/Resources/dark/coderfunc16@2x.png +0 -0
  400. psychopy/app/Resources/dark/coderimport16.png +0 -0
  401. psychopy/app/Resources/dark/coderimport16@2x.png +0 -0
  402. psychopy/app/Resources/dark/coderjs.png +0 -0
  403. psychopy/app/Resources/dark/coderjs@2x.png +0 -0
  404. psychopy/app/Resources/dark/coderpython.png +0 -0
  405. psychopy/app/Resources/dark/coderpython@2x.png +0 -0
  406. psychopy/app/Resources/dark/codervar16.png +0 -0
  407. psychopy/app/Resources/dark/codervar16@2x.png +0 -0
  408. psychopy/app/Resources/dark/cogwindow32.png +0 -0
  409. psychopy/app/Resources/dark/cogwindow32@2x.png +0 -0
  410. psychopy/app/Resources/dark/color32.png +0 -0
  411. psychopy/app/Resources/dark/color32@2x.png +0 -0
  412. psychopy/app/Resources/dark/compile_js.png +0 -0
  413. psychopy/app/Resources/dark/compile_js@2x.png +0 -0
  414. psychopy/app/Resources/dark/compile_py.png +0 -0
  415. psychopy/app/Resources/dark/compile_py@2x.png +0 -0
  416. psychopy/app/Resources/dark/copy16.png +0 -0
  417. psychopy/app/Resources/dark/copy16@2x.png +0 -0
  418. psychopy/app/Resources/dark/currentFile16.png +0 -0
  419. psychopy/app/Resources/dark/currentFile16@2x.png +0 -0
  420. psychopy/app/Resources/dark/delete16.png +0 -0
  421. psychopy/app/Resources/dark/delete16@2x.png +0 -0
  422. psychopy/app/Resources/dark/desktop.png +0 -0
  423. psychopy/app/Resources/dark/desktop@2x.png +0 -0
  424. psychopy/app/Resources/dark/devices.png +0 -0
  425. psychopy/app/Resources/dark/devices@2x.png +0 -0
  426. psychopy/app/Resources/dark/dirup16.png +0 -0
  427. psychopy/app/Resources/dark/dirup16@2x.png +0 -0
  428. psychopy/app/Resources/dark/docclose16.png +0 -0
  429. psychopy/app/Resources/dark/docclose16@2x.png +0 -0
  430. psychopy/app/Resources/dark/download.png +0 -0
  431. psychopy/app/Resources/dark/download@2x.png +0 -0
  432. psychopy/app/Resources/dark/edit.png +0 -0
  433. psychopy/app/Resources/dark/edit@2x.png +0 -0
  434. psychopy/app/Resources/dark/editbtn16.png +0 -0
  435. psychopy/app/Resources/dark/editbtn16@2x.png +0 -0
  436. psychopy/app/Resources/dark/email.png +0 -0
  437. psychopy/app/Resources/dark/email@2x.png +0 -0
  438. psychopy/app/Resources/dark/experiment.png +0 -0
  439. psychopy/app/Resources/dark/experiment@2x.png +0 -0
  440. psychopy/app/Resources/dark/expsettings.png +0 -0
  441. psychopy/app/Resources/dark/expsettings@2x.png +0 -0
  442. psychopy/app/Resources/dark/file.png +0 -0
  443. psychopy/app/Resources/dark/file@16w.png +0 -0
  444. psychopy/app/Resources/dark/file@16w@2x.png +0 -0
  445. psychopy/app/Resources/dark/file@2x.png +0 -0
  446. psychopy/app/Resources/dark/fileaudio.png +0 -0
  447. psychopy/app/Resources/dark/fileaudio@2x.png +0 -0
  448. psychopy/app/Resources/dark/filecss.png +0 -0
  449. psychopy/app/Resources/dark/filecss@2x.png +0 -0
  450. psychopy/app/Resources/dark/filecsv.png +0 -0
  451. psychopy/app/Resources/dark/filecsv@2x.png +0 -0
  452. psychopy/app/Resources/dark/filedesign.png +0 -0
  453. psychopy/app/Resources/dark/filedesign@2x.png +0 -0
  454. psychopy/app/Resources/dark/filefont.png +0 -0
  455. psychopy/app/Resources/dark/filefont@2x.png +0 -0
  456. psychopy/app/Resources/dark/filegit.png +0 -0
  457. psychopy/app/Resources/dark/filegit@2x.png +0 -0
  458. psychopy/app/Resources/dark/filehtml.png +0 -0
  459. psychopy/app/Resources/dark/filehtml@2x.png +0 -0
  460. psychopy/app/Resources/dark/fileimage.png +0 -0
  461. psychopy/app/Resources/dark/fileimage@2x.png +0 -0
  462. psychopy/app/Resources/dark/fileinfo.png +0 -0
  463. psychopy/app/Resources/dark/fileinfo@2x.png +0 -0
  464. psychopy/app/Resources/dark/filejs.png +0 -0
  465. psychopy/app/Resources/dark/filejs@2x.png +0 -0
  466. psychopy/app/Resources/dark/filejson.png +0 -0
  467. psychopy/app/Resources/dark/filejson@2x.png +0 -0
  468. psychopy/app/Resources/dark/filenew.png +0 -0
  469. psychopy/app/Resources/dark/filenew32.png +0 -0
  470. psychopy/app/Resources/dark/filenew32@2x.png +0 -0
  471. psychopy/app/Resources/dark/filenew@2x.png +0 -0
  472. psychopy/app/Resources/dark/fileopen.png +0 -0
  473. psychopy/app/Resources/dark/fileopen32.png +0 -0
  474. psychopy/app/Resources/dark/fileopen32@2x.png +0 -0
  475. psychopy/app/Resources/dark/fileopen@2x.png +0 -0
  476. psychopy/app/Resources/dark/filepkg.png +0 -0
  477. psychopy/app/Resources/dark/filepkg@2x.png +0 -0
  478. psychopy/app/Resources/dark/filepsyexp.png +0 -0
  479. psychopy/app/Resources/dark/filepsyexp@2x.png +0 -0
  480. psychopy/app/Resources/dark/filepy.png +0 -0
  481. psychopy/app/Resources/dark/filepy@2x.png +0 -0
  482. psychopy/app/Resources/dark/filesave.png +0 -0
  483. psychopy/app/Resources/dark/filesave32.png +0 -0
  484. psychopy/app/Resources/dark/filesave32@2x.png +0 -0
  485. psychopy/app/Resources/dark/filesave@2x.png +0 -0
  486. psychopy/app/Resources/dark/filesaveas.png +0 -0
  487. psychopy/app/Resources/dark/filesaveas32.png +0 -0
  488. psychopy/app/Resources/dark/filesaveas32@2x.png +0 -0
  489. psychopy/app/Resources/dark/filesaveas@2x.png +0 -0
  490. psychopy/app/Resources/dark/filetxt.png +0 -0
  491. psychopy/app/Resources/dark/filetxt@2x.png +0 -0
  492. psychopy/app/Resources/dark/fileunknown.png +0 -0
  493. psychopy/app/Resources/dark/fileunknown@2x.png +0 -0
  494. psychopy/app/Resources/dark/filevideo.png +0 -0
  495. psychopy/app/Resources/dark/filevideo@2x.png +0 -0
  496. psychopy/app/Resources/dark/filter.png +0 -0
  497. psychopy/app/Resources/dark/filter@2x.png +0 -0
  498. psychopy/app/Resources/dark/find.png +0 -0
  499. psychopy/app/Resources/dark/find@2x.png +0 -0
  500. psychopy/app/Resources/dark/folder-open16.png +0 -0
  501. psychopy/app/Resources/dark/folder-open16@2x.png +0 -0
  502. psychopy/app/Resources/dark/folder16.png +0 -0
  503. psychopy/app/Resources/dark/folder16@2x.png +0 -0
  504. psychopy/app/Resources/dark/foldernew16.png +0 -0
  505. psychopy/app/Resources/dark/foldernew16@2x.png +0 -0
  506. psychopy/app/Resources/dark/fork.png +0 -0
  507. psychopy/app/Resources/dark/fork@2x.png +0 -0
  508. psychopy/app/Resources/dark/github.png +0 -0
  509. psychopy/app/Resources/dark/github@2x.png +0 -0
  510. psychopy/app/Resources/dark/globe.png +0 -0
  511. psychopy/app/Resources/dark/globe@2x.png +0 -0
  512. psychopy/app/Resources/dark/globe_bug.png +0 -0
  513. psychopy/app/Resources/dark/globe_bug@2x.png +0 -0
  514. psychopy/app/Resources/dark/globe_greensync.png +0 -0
  515. psychopy/app/Resources/dark/globe_greensync@2x.png +0 -0
  516. psychopy/app/Resources/dark/globe_info.png +0 -0
  517. psychopy/app/Resources/dark/globe_info@2x.png +0 -0
  518. psychopy/app/Resources/dark/globe_magnifier.png +0 -0
  519. psychopy/app/Resources/dark/globe_magnifier@2x.png +0 -0
  520. psychopy/app/Resources/dark/globe_run.png +0 -0
  521. psychopy/app/Resources/dark/globe_run@2x.png +0 -0
  522. psychopy/app/Resources/dark/globe_user.png +0 -0
  523. psychopy/app/Resources/dark/globe_user@2x.png +0 -0
  524. psychopy/app/Resources/dark/goto.png +0 -0
  525. psychopy/app/Resources/dark/goto@2x.png +0 -0
  526. psychopy/app/Resources/dark/greendot.png +0 -0
  527. psychopy/app/Resources/dark/greendot@2x.png +0 -0
  528. psychopy/app/Resources/dark/greenglobe.png +0 -0
  529. psychopy/app/Resources/dark/greenglobe@2x.png +0 -0
  530. psychopy/app/Resources/dark/greenglobe_bug.png +0 -0
  531. psychopy/app/Resources/dark/greenglobe_bug@2x.png +0 -0
  532. psychopy/app/Resources/dark/greenglobe_greensync.png +0 -0
  533. psychopy/app/Resources/dark/greenglobe_greensync@2x.png +0 -0
  534. psychopy/app/Resources/dark/greenglobe_info.png +0 -0
  535. psychopy/app/Resources/dark/greenglobe_info@2x.png +0 -0
  536. psychopy/app/Resources/dark/greenglobe_magnifier.png +0 -0
  537. psychopy/app/Resources/dark/greenglobe_magnifier@2x.png +0 -0
  538. psychopy/app/Resources/dark/greenglobe_run.png +0 -0
  539. psychopy/app/Resources/dark/greenglobe_run@2x.png +0 -0
  540. psychopy/app/Resources/dark/greenglobe_user.png +0 -0
  541. psychopy/app/Resources/dark/greenglobe_user@2x.png +0 -0
  542. psychopy/app/Resources/dark/greydot.png +0 -0
  543. psychopy/app/Resources/dark/greydot@2x.png +0 -0
  544. psychopy/app/Resources/dark/greytick.png +0 -0
  545. psychopy/app/Resources/dark/greytick@2x.png +0 -0
  546. psychopy/app/Resources/dark/invalid_img.png +0 -0
  547. psychopy/app/Resources/dark/jsPilot.png +0 -0
  548. psychopy/app/Resources/dark/jsPilot@2x.png +0 -0
  549. psychopy/app/Resources/dark/jsRun.png +0 -0
  550. psychopy/app/Resources/dark/jsRun@2x.png +0 -0
  551. psychopy/app/Resources/dark/libroot16.png +0 -0
  552. psychopy/app/Resources/dark/libroot16@2x.png +0 -0
  553. psychopy/app/Resources/dark/loop.png +0 -0
  554. psychopy/app/Resources/dark/loop@2x.png +0 -0
  555. psychopy/app/Resources/dark/monitor16.png +0 -0
  556. psychopy/app/Resources/dark/monitor16@2x.png +0 -0
  557. psychopy/app/Resources/dark/monitors.png +0 -0
  558. psychopy/app/Resources/dark/monitors32.png +0 -0
  559. psychopy/app/Resources/dark/monitors32@2x.png +0 -0
  560. psychopy/app/Resources/dark/monitors@2x.png +0 -0
  561. psychopy/app/Resources/dark/orangedot.png +0 -0
  562. psychopy/app/Resources/dark/orangedot@2x.png +0 -0
  563. psychopy/app/Resources/dark/pavlovia.png +0 -0
  564. psychopy/app/Resources/dark/pavlovia16.png +0 -0
  565. psychopy/app/Resources/dark/pavlovia16@2x.png +0 -0
  566. psychopy/app/Resources/dark/pavlovia@2x.png +0 -0
  567. psychopy/app/Resources/dark/pavsync.png +0 -0
  568. psychopy/app/Resources/dark/pavsync@2x.png +0 -0
  569. psychopy/app/Resources/dark/person_off.png +0 -0
  570. psychopy/app/Resources/dark/person_off@2x.png +0 -0
  571. psychopy/app/Resources/dark/person_on.png +0 -0
  572. psychopy/app/Resources/dark/person_on@2x.png +0 -0
  573. psychopy/app/Resources/dark/photometer.png +0 -0
  574. psychopy/app/Resources/dark/photometer@2x.png +0 -0
  575. psychopy/app/Resources/dark/plugin16.png +0 -0
  576. psychopy/app/Resources/dark/plugin16@2x.png +0 -0
  577. psychopy/app/Resources/dark/plugins32.png +0 -0
  578. psychopy/app/Resources/dark/plugins32@2x.png +0 -0
  579. psychopy/app/Resources/dark/plus.png +0 -0
  580. psychopy/app/Resources/dark/plus@2x.png +0 -0
  581. psychopy/app/Resources/dark/preferences-app.png +0 -0
  582. psychopy/app/Resources/dark/preferences-app48.png +0 -0
  583. psychopy/app/Resources/dark/preferences-app48@2x.png +0 -0
  584. psychopy/app/Resources/dark/preferences-app@2x.png +0 -0
  585. psychopy/app/Resources/dark/preferences-conn.png +0 -0
  586. psychopy/app/Resources/dark/preferences-conn48.png +0 -0
  587. psychopy/app/Resources/dark/preferences-conn48@2x.png +0 -0
  588. psychopy/app/Resources/dark/preferences-conn@2x.png +0 -0
  589. psychopy/app/Resources/dark/preferences-debug.png +0 -0
  590. psychopy/app/Resources/dark/preferences-debug@2x.png +0 -0
  591. psychopy/app/Resources/dark/preferences-general.png +0 -0
  592. psychopy/app/Resources/dark/preferences-general48.png +0 -0
  593. psychopy/app/Resources/dark/preferences-general48@2x.png +0 -0
  594. psychopy/app/Resources/dark/preferences-general@2x.png +0 -0
  595. psychopy/app/Resources/dark/preferences-hardware.png +0 -0
  596. psychopy/app/Resources/dark/preferences-hardware48.png +0 -0
  597. psychopy/app/Resources/dark/preferences-hardware48@2x.png +0 -0
  598. psychopy/app/Resources/dark/preferences-hardware@2x.png +0 -0
  599. psychopy/app/Resources/dark/preferences-keyboard.png +0 -0
  600. psychopy/app/Resources/dark/preferences-keyboard48.png +0 -0
  601. psychopy/app/Resources/dark/preferences-keyboard48@2x.png +0 -0
  602. psychopy/app/Resources/dark/preferences-keyboard@2x.png +0 -0
  603. psychopy/app/Resources/dark/preferences-pilot.png +0 -0
  604. psychopy/app/Resources/dark/preferences-pilot@2x.png +0 -0
  605. psychopy/app/Resources/dark/preferences32.png +0 -0
  606. psychopy/app/Resources/dark/preferences32@2x.png +0 -0
  607. psychopy/app/Resources/dark/pyPilot.png +0 -0
  608. psychopy/app/Resources/dark/pyPilot@2x.png +0 -0
  609. psychopy/app/Resources/dark/pyRun.png +0 -0
  610. psychopy/app/Resources/dark/pyRun@2x.png +0 -0
  611. psychopy/app/Resources/dark/reddot.png +0 -0
  612. psychopy/app/Resources/dark/reddot@2x.png +0 -0
  613. psychopy/app/Resources/dark/redglobe.png +0 -0
  614. psychopy/app/Resources/dark/redglobe@2x.png +0 -0
  615. psychopy/app/Resources/dark/redglobe_bug.png +0 -0
  616. psychopy/app/Resources/dark/redglobe_bug@2x.png +0 -0
  617. psychopy/app/Resources/dark/redglobe_greensync.png +0 -0
  618. psychopy/app/Resources/dark/redglobe_greensync@2x.png +0 -0
  619. psychopy/app/Resources/dark/redglobe_info.png +0 -0
  620. psychopy/app/Resources/dark/redglobe_info@2x.png +0 -0
  621. psychopy/app/Resources/dark/redglobe_magnifier.png +0 -0
  622. psychopy/app/Resources/dark/redglobe_magnifier@2x.png +0 -0
  623. psychopy/app/Resources/dark/redglobe_run.png +0 -0
  624. psychopy/app/Resources/dark/redglobe_run@2x.png +0 -0
  625. psychopy/app/Resources/dark/redglobe_user.png +0 -0
  626. psychopy/app/Resources/dark/redglobe_user@2x.png +0 -0
  627. psychopy/app/Resources/dark/redo.png +0 -0
  628. psychopy/app/Resources/dark/redo32.png +0 -0
  629. psychopy/app/Resources/dark/redo32@2x.png +0 -0
  630. psychopy/app/Resources/dark/redo@2x.png +0 -0
  631. psychopy/app/Resources/dark/regex.png +0 -0
  632. psychopy/app/Resources/dark/regex@2x.png +0 -0
  633. psychopy/app/Resources/dark/removeExp32.png +0 -0
  634. psychopy/app/Resources/dark/removeExp32@2x.png +0 -0
  635. psychopy/app/Resources/dark/rename16.png +0 -0
  636. psychopy/app/Resources/dark/rename16@2x.png +0 -0
  637. psychopy/app/Resources/dark/restart.png +0 -0
  638. psychopy/app/Resources/dark/restart@2x.png +0 -0
  639. psychopy/app/Resources/dark/runner.png +0 -0
  640. psychopy/app/Resources/dark/runner@2x.png +0 -0
  641. psychopy/app/Resources/dark/runnerPilot.png +0 -0
  642. psychopy/app/Resources/dark/runnerPilot@2x.png +0 -0
  643. psychopy/app/Resources/dark/savebtn16.png +0 -0
  644. psychopy/app/Resources/dark/savebtn16@2x.png +0 -0
  645. psychopy/app/Resources/dark/search.png +0 -0
  646. psychopy/app/Resources/dark/search@2x.png +0 -0
  647. psychopy/app/Resources/dark/showBuilder.png +0 -0
  648. psychopy/app/Resources/dark/showBuilder@2x.png +0 -0
  649. psychopy/app/Resources/dark/showCoder.png +0 -0
  650. psychopy/app/Resources/dark/showCoder@2x.png +0 -0
  651. psychopy/app/Resources/dark/showRunner.png +0 -0
  652. psychopy/app/Resources/dark/showRunner@2x.png +0 -0
  653. psychopy/app/Resources/dark/starred.png +0 -0
  654. psychopy/app/Resources/dark/starred@2x.png +0 -0
  655. psychopy/app/Resources/dark/start.png +0 -0
  656. psychopy/app/Resources/dark/start@2x.png +0 -0
  657. psychopy/app/Resources/dark/stdout.png +0 -0
  658. psychopy/app/Resources/dark/stdout@2x.png +0 -0
  659. psychopy/app/Resources/dark/stop.png +0 -0
  660. psychopy/app/Resources/dark/stop32.png +0 -0
  661. psychopy/app/Resources/dark/stop32@2x.png +0 -0
  662. psychopy/app/Resources/dark/stop@2x.png +0 -0
  663. psychopy/app/Resources/dark/switchCtrlBot.png +0 -0
  664. psychopy/app/Resources/dark/switchCtrlBot@2x.png +0 -0
  665. psychopy/app/Resources/dark/switchCtrlLeft.png +0 -0
  666. psychopy/app/Resources/dark/switchCtrlLeft@2x.png +0 -0
  667. psychopy/app/Resources/dark/switchCtrlRight.png +0 -0
  668. psychopy/app/Resources/dark/switchCtrlRight@2x.png +0 -0
  669. psychopy/app/Resources/dark/switchCtrlTop.png +0 -0
  670. psychopy/app/Resources/dark/switchCtrlTop@2x.png +0 -0
  671. psychopy/app/Resources/dark/tick.png +0 -0
  672. psychopy/app/Resources/dark/tick@2x.png +0 -0
  673. psychopy/app/Resources/dark/undo.png +0 -0
  674. psychopy/app/Resources/dark/undo32.png +0 -0
  675. psychopy/app/Resources/dark/undo32@2x.png +0 -0
  676. psychopy/app/Resources/dark/undo@2x.png +0 -0
  677. psychopy/app/Resources/dark/unstarred.png +0 -0
  678. psychopy/app/Resources/dark/unstarred@2x.png +0 -0
  679. psychopy/app/Resources/dark/user_none.png +0 -0
  680. psychopy/app/Resources/dark/view-refresh16.png +0 -0
  681. psychopy/app/Resources/dark/view-refresh16@2x.png +0 -0
  682. psychopy/app/Resources/dark/viewbtn16.png +0 -0
  683. psychopy/app/Resources/dark/viewbtn16@2x.png +0 -0
  684. psychopy/app/Resources/dark/windows16.png +0 -0
  685. psychopy/app/Resources/dark/windows16@2x.png +0 -0
  686. psychopy/app/Resources/light/FlowBottom_CompLeft.png +0 -0
  687. psychopy/app/Resources/light/FlowBottom_CompRight.png +0 -0
  688. psychopy/app/Resources/light/FlowTop_CompLeft.png +0 -0
  689. psychopy/app/Resources/light/FlowTop_CompRight.png +0 -0
  690. psychopy/app/Resources/light/README.txt +3 -0
  691. psychopy/app/Resources/light/__init__.py +0 -0
  692. psychopy/app/Resources/light/add.png +0 -0
  693. psychopy/app/Resources/light/add@2x.png +0 -0
  694. psychopy/app/Resources/light/addExp32.png +0 -0
  695. psychopy/app/Resources/light/addExp32@2x.png +0 -0
  696. psychopy/app/Resources/light/add_many.png +0 -0
  697. psychopy/app/Resources/light/add_many@2x.png +0 -0
  698. psychopy/app/Resources/light/alerts.png +0 -0
  699. psychopy/app/Resources/light/alerts@2x.png +0 -0
  700. psychopy/app/Resources/light/beta.png +0 -0
  701. psychopy/app/Resources/light/beta@2x.png +0 -0
  702. psychopy/app/Resources/light/browser.png +0 -0
  703. psychopy/app/Resources/light/browser@2x.png +0 -0
  704. psychopy/app/Resources/light/bug16.png +0 -0
  705. psychopy/app/Resources/light/bug16@2x.png +0 -0
  706. psychopy/app/Resources/light/case.png +0 -0
  707. psychopy/app/Resources/light/case@2x.png +0 -0
  708. psychopy/app/Resources/light/circle_mask.png +0 -0
  709. psychopy/app/Resources/light/circle_mask@2x.png +0 -0
  710. psychopy/app/Resources/light/clear.png +0 -0
  711. psychopy/app/Resources/light/clear@2x.png +0 -0
  712. psychopy/app/Resources/light/coderclass16.png +0 -0
  713. psychopy/app/Resources/light/coderclass16@2x.png +0 -0
  714. psychopy/app/Resources/light/coderfunc16.png +0 -0
  715. psychopy/app/Resources/light/coderfunc16@2x.png +0 -0
  716. psychopy/app/Resources/light/coderimport16.png +0 -0
  717. psychopy/app/Resources/light/coderimport16@2x.png +0 -0
  718. psychopy/app/Resources/light/coderjs.png +0 -0
  719. psychopy/app/Resources/light/coderjs@2x.png +0 -0
  720. psychopy/app/Resources/light/coderpython.png +0 -0
  721. psychopy/app/Resources/light/coderpython@2x.png +0 -0
  722. psychopy/app/Resources/light/codervar16.png +0 -0
  723. psychopy/app/Resources/light/codervar16@2x.png +0 -0
  724. psychopy/app/Resources/light/cogwindow32.png +0 -0
  725. psychopy/app/Resources/light/cogwindow32@2x.png +0 -0
  726. psychopy/app/Resources/light/color16.png +0 -0
  727. psychopy/app/Resources/light/color16@2x.png +0 -0
  728. psychopy/app/Resources/light/color32.png +0 -0
  729. psychopy/app/Resources/light/color32@2x.png +0 -0
  730. psychopy/app/Resources/light/compile_js.png +0 -0
  731. psychopy/app/Resources/light/compile_js@2x.png +0 -0
  732. psychopy/app/Resources/light/compile_py.png +0 -0
  733. psychopy/app/Resources/light/compile_py@2x.png +0 -0
  734. psychopy/app/Resources/light/copy16.png +0 -0
  735. psychopy/app/Resources/light/copy16@2x.png +0 -0
  736. psychopy/app/Resources/light/currentFile16.png +0 -0
  737. psychopy/app/Resources/light/currentFile16@2x.png +0 -0
  738. psychopy/app/Resources/light/delete16.png +0 -0
  739. psychopy/app/Resources/light/delete16@2x.png +0 -0
  740. psychopy/app/Resources/light/delete8.png +0 -0
  741. psychopy/app/Resources/light/desktop.png +0 -0
  742. psychopy/app/Resources/light/desktop@2x.png +0 -0
  743. psychopy/app/Resources/light/devices.png +0 -0
  744. psychopy/app/Resources/light/devices@2x.png +0 -0
  745. psychopy/app/Resources/light/dirup16.png +0 -0
  746. psychopy/app/Resources/light/dirup16@2x.png +0 -0
  747. psychopy/app/Resources/light/docclose16.png +0 -0
  748. psychopy/app/Resources/light/docclose16@2x.png +0 -0
  749. psychopy/app/Resources/light/download.png +0 -0
  750. psychopy/app/Resources/light/download@2x.png +0 -0
  751. psychopy/app/Resources/light/edit.png +0 -0
  752. psychopy/app/Resources/light/edit@2x.png +0 -0
  753. psychopy/app/Resources/light/editbtn16.png +0 -0
  754. psychopy/app/Resources/light/editbtn16@2x.png +0 -0
  755. psychopy/app/Resources/light/email.png +0 -0
  756. psychopy/app/Resources/light/email@2x.png +0 -0
  757. psychopy/app/Resources/light/experiment.png +0 -0
  758. psychopy/app/Resources/light/experiment@2x.png +0 -0
  759. psychopy/app/Resources/light/expsettings.png +0 -0
  760. psychopy/app/Resources/light/expsettings@2x.png +0 -0
  761. psychopy/app/Resources/light/file.png +0 -0
  762. psychopy/app/Resources/light/file@2x.png +0 -0
  763. psychopy/app/Resources/light/fileaudio.png +0 -0
  764. psychopy/app/Resources/light/fileaudio@2x.png +0 -0
  765. psychopy/app/Resources/light/filecss.png +0 -0
  766. psychopy/app/Resources/light/filecss@2x.png +0 -0
  767. psychopy/app/Resources/light/filecsv.png +0 -0
  768. psychopy/app/Resources/light/filecsv@2x.png +0 -0
  769. psychopy/app/Resources/light/filedesign.png +0 -0
  770. psychopy/app/Resources/light/filedesign@2x.png +0 -0
  771. psychopy/app/Resources/light/filefont.png +0 -0
  772. psychopy/app/Resources/light/filefont@2x.png +0 -0
  773. psychopy/app/Resources/light/filegit.png +0 -0
  774. psychopy/app/Resources/light/filegit@2x.png +0 -0
  775. psychopy/app/Resources/light/filehtml.png +0 -0
  776. psychopy/app/Resources/light/filehtml@2x.png +0 -0
  777. psychopy/app/Resources/light/fileimage.png +0 -0
  778. psychopy/app/Resources/light/fileimage@2x.png +0 -0
  779. psychopy/app/Resources/light/fileinfo.png +0 -0
  780. psychopy/app/Resources/light/fileinfo@2x.png +0 -0
  781. psychopy/app/Resources/light/filejs.png +0 -0
  782. psychopy/app/Resources/light/filejs@2x.png +0 -0
  783. psychopy/app/Resources/light/filejson.png +0 -0
  784. psychopy/app/Resources/light/filejson@2x.png +0 -0
  785. psychopy/app/Resources/light/filenew.png +0 -0
  786. psychopy/app/Resources/light/filenew32.png +0 -0
  787. psychopy/app/Resources/light/filenew32@2x.png +0 -0
  788. psychopy/app/Resources/light/filenew@2x.png +0 -0
  789. psychopy/app/Resources/light/fileopen.png +0 -0
  790. psychopy/app/Resources/light/fileopen32.png +0 -0
  791. psychopy/app/Resources/light/fileopen32@2x.png +0 -0
  792. psychopy/app/Resources/light/fileopen@2x.png +0 -0
  793. psychopy/app/Resources/light/filepkg.png +0 -0
  794. psychopy/app/Resources/light/filepkg@2x.png +0 -0
  795. psychopy/app/Resources/light/filepsyexp.png +0 -0
  796. psychopy/app/Resources/light/filepsyexp@2x.png +0 -0
  797. psychopy/app/Resources/light/filepy.png +0 -0
  798. psychopy/app/Resources/light/filepy@2x.png +0 -0
  799. psychopy/app/Resources/light/filesave.png +0 -0
  800. psychopy/app/Resources/light/filesave32.png +0 -0
  801. psychopy/app/Resources/light/filesave32@2x.png +0 -0
  802. psychopy/app/Resources/light/filesave@2x.png +0 -0
  803. psychopy/app/Resources/light/filesaveas.png +0 -0
  804. psychopy/app/Resources/light/filesaveas32.png +0 -0
  805. psychopy/app/Resources/light/filesaveas32@2x.png +0 -0
  806. psychopy/app/Resources/light/filesaveas@2x.png +0 -0
  807. psychopy/app/Resources/light/filetxt.png +0 -0
  808. psychopy/app/Resources/light/filetxt@2x.png +0 -0
  809. psychopy/app/Resources/light/fileunknown.png +0 -0
  810. psychopy/app/Resources/light/fileunknown@2x.png +0 -0
  811. psychopy/app/Resources/light/filevideo.png +0 -0
  812. psychopy/app/Resources/light/filevideo@2x.png +0 -0
  813. psychopy/app/Resources/light/filter.png +0 -0
  814. psychopy/app/Resources/light/filter@2x.png +0 -0
  815. psychopy/app/Resources/light/find.png +0 -0
  816. psychopy/app/Resources/light/find@2x.png +0 -0
  817. psychopy/app/Resources/light/folder-open16.png +0 -0
  818. psychopy/app/Resources/light/folder-open16@2x.png +0 -0
  819. psychopy/app/Resources/light/folder16.png +0 -0
  820. psychopy/app/Resources/light/folder16@2x.png +0 -0
  821. psychopy/app/Resources/light/foldernew16.png +0 -0
  822. psychopy/app/Resources/light/foldernew16@2x.png +0 -0
  823. psychopy/app/Resources/light/fork.png +0 -0
  824. psychopy/app/Resources/light/fork@2x.png +0 -0
  825. psychopy/app/Resources/light/github.png +0 -0
  826. psychopy/app/Resources/light/github@2x.png +0 -0
  827. psychopy/app/Resources/light/globe.png +0 -0
  828. psychopy/app/Resources/light/globe@2x.png +0 -0
  829. psychopy/app/Resources/light/globe_bug.png +0 -0
  830. psychopy/app/Resources/light/globe_bug@2x.png +0 -0
  831. psychopy/app/Resources/light/globe_greensync.png +0 -0
  832. psychopy/app/Resources/light/globe_greensync@2x.png +0 -0
  833. psychopy/app/Resources/light/globe_info.png +0 -0
  834. psychopy/app/Resources/light/globe_info@2x.png +0 -0
  835. psychopy/app/Resources/light/globe_magnifier.png +0 -0
  836. psychopy/app/Resources/light/globe_magnifier@2x.png +0 -0
  837. psychopy/app/Resources/light/globe_run.png +0 -0
  838. psychopy/app/Resources/light/globe_run@2x.png +0 -0
  839. psychopy/app/Resources/light/globe_user.png +0 -0
  840. psychopy/app/Resources/light/globe_user@2x.png +0 -0
  841. psychopy/app/Resources/light/goto16.png +0 -0
  842. psychopy/app/Resources/light/goto16@2x.png +0 -0
  843. psychopy/app/Resources/light/greendot.png +0 -0
  844. psychopy/app/Resources/light/greendot@2x.png +0 -0
  845. psychopy/app/Resources/light/greenglobe.png +0 -0
  846. psychopy/app/Resources/light/greenglobe@2x.png +0 -0
  847. psychopy/app/Resources/light/greenglobe_bug.png +0 -0
  848. psychopy/app/Resources/light/greenglobe_bug@2x.png +0 -0
  849. psychopy/app/Resources/light/greenglobe_greensync.png +0 -0
  850. psychopy/app/Resources/light/greenglobe_greensync@2x.png +0 -0
  851. psychopy/app/Resources/light/greenglobe_info.png +0 -0
  852. psychopy/app/Resources/light/greenglobe_info@2x.png +0 -0
  853. psychopy/app/Resources/light/greenglobe_magnifier.png +0 -0
  854. psychopy/app/Resources/light/greenglobe_magnifier@2x.png +0 -0
  855. psychopy/app/Resources/light/greenglobe_run.png +0 -0
  856. psychopy/app/Resources/light/greenglobe_run@2x.png +0 -0
  857. psychopy/app/Resources/light/greenglobe_user.png +0 -0
  858. psychopy/app/Resources/light/greenglobe_user@2x.png +0 -0
  859. psychopy/app/Resources/light/greydot.png +0 -0
  860. psychopy/app/Resources/light/greydot@2x.png +0 -0
  861. psychopy/app/Resources/light/greytick.png +0 -0
  862. psychopy/app/Resources/light/greytick@2x.png +0 -0
  863. psychopy/app/Resources/light/invalid_img.png +0 -0
  864. psychopy/app/Resources/light/jsPilot.png +0 -0
  865. psychopy/app/Resources/light/jsPilot@2x.png +0 -0
  866. psychopy/app/Resources/light/jsRun.png +0 -0
  867. psychopy/app/Resources/light/jsRun@2x.png +0 -0
  868. psychopy/app/Resources/light/libroot16.png +0 -0
  869. psychopy/app/Resources/light/libroot16@2x.png +0 -0
  870. psychopy/app/Resources/light/loop.png +0 -0
  871. psychopy/app/Resources/light/loop@2x.png +0 -0
  872. psychopy/app/Resources/light/monitor16.png +0 -0
  873. psychopy/app/Resources/light/monitor16@2x.png +0 -0
  874. psychopy/app/Resources/light/monitors.png +0 -0
  875. psychopy/app/Resources/light/monitors32.png +0 -0
  876. psychopy/app/Resources/light/monitors32@2x.png +0 -0
  877. psychopy/app/Resources/light/monitors@2x.png +0 -0
  878. psychopy/app/Resources/light/orangedot.png +0 -0
  879. psychopy/app/Resources/light/orangedot@2x.png +0 -0
  880. psychopy/app/Resources/light/pavlovia.png +0 -0
  881. psychopy/app/Resources/light/pavlovia16.png +0 -0
  882. psychopy/app/Resources/light/pavlovia16@2x.png +0 -0
  883. psychopy/app/Resources/light/pavlovia@2x.png +0 -0
  884. psychopy/app/Resources/light/pavsync.png +0 -0
  885. psychopy/app/Resources/light/pavsync@2x.png +0 -0
  886. psychopy/app/Resources/light/person_off.png +0 -0
  887. psychopy/app/Resources/light/person_off@2x.png +0 -0
  888. psychopy/app/Resources/light/person_on.png +0 -0
  889. psychopy/app/Resources/light/person_on@2x.png +0 -0
  890. psychopy/app/Resources/light/photometer.png +0 -0
  891. psychopy/app/Resources/light/photometer@2x.png +0 -0
  892. psychopy/app/Resources/light/plugin16.png +0 -0
  893. psychopy/app/Resources/light/plugin16@2x.png +0 -0
  894. psychopy/app/Resources/light/plugins32.png +0 -0
  895. psychopy/app/Resources/light/plugins32@2x.png +0 -0
  896. psychopy/app/Resources/light/plus.png +0 -0
  897. psychopy/app/Resources/light/plus@2x.png +0 -0
  898. psychopy/app/Resources/light/preferences-app.png +0 -0
  899. psychopy/app/Resources/light/preferences-app48.png +0 -0
  900. psychopy/app/Resources/light/preferences-app48@2x.png +0 -0
  901. psychopy/app/Resources/light/preferences-app@2x.png +0 -0
  902. psychopy/app/Resources/light/preferences-conn.png +0 -0
  903. psychopy/app/Resources/light/preferences-conn48.png +0 -0
  904. psychopy/app/Resources/light/preferences-conn48@2x.png +0 -0
  905. psychopy/app/Resources/light/preferences-conn@2x.png +0 -0
  906. psychopy/app/Resources/light/preferences-general.png +0 -0
  907. psychopy/app/Resources/light/preferences-general48.png +0 -0
  908. psychopy/app/Resources/light/preferences-general48@2x.png +0 -0
  909. psychopy/app/Resources/light/preferences-general@2x.png +0 -0
  910. psychopy/app/Resources/light/preferences-hardware.png +0 -0
  911. psychopy/app/Resources/light/preferences-hardware48.png +0 -0
  912. psychopy/app/Resources/light/preferences-hardware48@2x.png +0 -0
  913. psychopy/app/Resources/light/preferences-hardware@2x.png +0 -0
  914. psychopy/app/Resources/light/preferences-keyboard.png +0 -0
  915. psychopy/app/Resources/light/preferences-keyboard48.png +0 -0
  916. psychopy/app/Resources/light/preferences-keyboard48@2x.png +0 -0
  917. psychopy/app/Resources/light/preferences-keyboard@2x.png +0 -0
  918. psychopy/app/Resources/light/preferences-pilot.png +0 -0
  919. psychopy/app/Resources/light/preferences-pilot@2x.png +0 -0
  920. psychopy/app/Resources/light/preferences32.png +0 -0
  921. psychopy/app/Resources/light/preferences32@2x.png +0 -0
  922. psychopy/app/Resources/light/pyPilot.png +0 -0
  923. psychopy/app/Resources/light/pyPilot@2x.png +0 -0
  924. psychopy/app/Resources/light/pyRun.png +0 -0
  925. psychopy/app/Resources/light/pyRun@2x.png +0 -0
  926. psychopy/app/Resources/light/reddot.png +0 -0
  927. psychopy/app/Resources/light/reddot@2x.png +0 -0
  928. psychopy/app/Resources/light/redglobe.png +0 -0
  929. psychopy/app/Resources/light/redglobe@2x.png +0 -0
  930. psychopy/app/Resources/light/redglobe_bug.png +0 -0
  931. psychopy/app/Resources/light/redglobe_bug@2x.png +0 -0
  932. psychopy/app/Resources/light/redglobe_greensync.png +0 -0
  933. psychopy/app/Resources/light/redglobe_greensync@2x.png +0 -0
  934. psychopy/app/Resources/light/redglobe_info.png +0 -0
  935. psychopy/app/Resources/light/redglobe_info@2x.png +0 -0
  936. psychopy/app/Resources/light/redglobe_magnifier.png +0 -0
  937. psychopy/app/Resources/light/redglobe_magnifier@2x.png +0 -0
  938. psychopy/app/Resources/light/redglobe_run.png +0 -0
  939. psychopy/app/Resources/light/redglobe_run@2x.png +0 -0
  940. psychopy/app/Resources/light/redglobe_user.png +0 -0
  941. psychopy/app/Resources/light/redglobe_user@2x.png +0 -0
  942. psychopy/app/Resources/light/redo.png +0 -0
  943. psychopy/app/Resources/light/redo32.png +0 -0
  944. psychopy/app/Resources/light/redo32@2x.png +0 -0
  945. psychopy/app/Resources/light/redo@2x.png +0 -0
  946. psychopy/app/Resources/light/regex.png +0 -0
  947. psychopy/app/Resources/light/regex@2x.png +0 -0
  948. psychopy/app/Resources/light/removeExp32.png +0 -0
  949. psychopy/app/Resources/light/removeExp32@2x.png +0 -0
  950. psychopy/app/Resources/light/rename16.png +0 -0
  951. psychopy/app/Resources/light/rename16@2x.png +0 -0
  952. psychopy/app/Resources/light/restart.png +0 -0
  953. psychopy/app/Resources/light/restart@2x.png +0 -0
  954. psychopy/app/Resources/light/runner.png +0 -0
  955. psychopy/app/Resources/light/runner@2x.png +0 -0
  956. psychopy/app/Resources/light/runnerPilot.png +0 -0
  957. psychopy/app/Resources/light/runnerPilot@2x.png +0 -0
  958. psychopy/app/Resources/light/savebtn16.png +0 -0
  959. psychopy/app/Resources/light/savebtn16@2x.png +0 -0
  960. psychopy/app/Resources/light/search.png +0 -0
  961. psychopy/app/Resources/light/search@2x.png +0 -0
  962. psychopy/app/Resources/light/showBuilder.png +0 -0
  963. psychopy/app/Resources/light/showBuilder@2x.png +0 -0
  964. psychopy/app/Resources/light/showCoder.png +0 -0
  965. psychopy/app/Resources/light/showCoder@2x.png +0 -0
  966. psychopy/app/Resources/light/showRunner.png +0 -0
  967. psychopy/app/Resources/light/showRunner@2x.png +0 -0
  968. psychopy/app/Resources/light/starred.png +0 -0
  969. psychopy/app/Resources/light/starred@2x.png +0 -0
  970. psychopy/app/Resources/light/start.png +0 -0
  971. psychopy/app/Resources/light/start@2x.png +0 -0
  972. psychopy/app/Resources/light/stdout.png +0 -0
  973. psychopy/app/Resources/light/stdout@2x.png +0 -0
  974. psychopy/app/Resources/light/stop.png +0 -0
  975. psychopy/app/Resources/light/stop32.png +0 -0
  976. psychopy/app/Resources/light/stop32@2x.png +0 -0
  977. psychopy/app/Resources/light/stop@2x.png +0 -0
  978. psychopy/app/Resources/light/switchCtrlBot.png +0 -0
  979. psychopy/app/Resources/light/switchCtrlBot@2x.png +0 -0
  980. psychopy/app/Resources/light/switchCtrlLeft.png +0 -0
  981. psychopy/app/Resources/light/switchCtrlLeft@2x.png +0 -0
  982. psychopy/app/Resources/light/switchCtrlRight.png +0 -0
  983. psychopy/app/Resources/light/switchCtrlRight@2x.png +0 -0
  984. psychopy/app/Resources/light/switchCtrlTop.png +0 -0
  985. psychopy/app/Resources/light/switchCtrlTop@2x.png +0 -0
  986. psychopy/app/Resources/light/tick.png +0 -0
  987. psychopy/app/Resources/light/tick@2x.png +0 -0
  988. psychopy/app/Resources/light/undo.png +0 -0
  989. psychopy/app/Resources/light/undo32.png +0 -0
  990. psychopy/app/Resources/light/undo32@2x.png +0 -0
  991. psychopy/app/Resources/light/undo@2x.png +0 -0
  992. psychopy/app/Resources/light/unstarred.png +0 -0
  993. psychopy/app/Resources/light/unstarred@2x.png +0 -0
  994. psychopy/app/Resources/light/user_none.png +0 -0
  995. psychopy/app/Resources/light/view-refresh16.png +0 -0
  996. psychopy/app/Resources/light/view-refresh16@2x.png +0 -0
  997. psychopy/app/Resources/light/viewbtn16.png +0 -0
  998. psychopy/app/Resources/light/viewbtn16@2x.png +0 -0
  999. psychopy/app/Resources/light/windows16.png +0 -0
  1000. psychopy/app/Resources/light/windows16@2x.png +0 -0
  1001. psychopy/app/Resources/moveComponentIcons.py +50 -0
  1002. psychopy/app/Resources/psychopy.desktop +13 -0
  1003. psychopy/app/Resources/psychopy.icns +0 -0
  1004. psychopy/app/Resources/psychopy.ico +0 -0
  1005. psychopy/app/Resources/psychopy.png +0 -0
  1006. psychopy/app/Resources/psychopy.xml +9 -0
  1007. psychopy/app/Resources/psychopy@2x.png +0 -0
  1008. psychopy/app/Resources/routine_templates/Basic.psyexp +344 -0
  1009. psychopy/app/Resources/routine_templates/Misc.psyexp +228 -0
  1010. psychopy/app/Resources/routine_templates/Online.psyexp +200 -0
  1011. psychopy/app/Resources/routine_templates/Trials.psyexp +735 -0
  1012. psychopy/app/Resources/routine_templates/readme.md +14 -0
  1013. psychopy/app/Resources/runner.ico +0 -0
  1014. psychopy/app/Resources/splash.png +0 -0
  1015. psychopy/app/Resources/splash@2x.png +0 -0
  1016. psychopy/app/Resources/tips.txt +45 -0
  1017. psychopy/app/Resources/tips_ar_001.txt +45 -0
  1018. psychopy/app/Resources/tips_fr_FR.txt +45 -0
  1019. psychopy/app/Resources/tips_ja_JP.txt +42 -0
  1020. psychopy/app/Resources/tips_zh_CN.txt +45 -0
  1021. psychopy/app/Resources/window.ico +0 -0
  1022. psychopy/app/__init__.py +339 -0
  1023. psychopy/app/__main__.py +3 -0
  1024. psychopy/app/_psychopyApp.py +1298 -0
  1025. psychopy/app/appData.spec +58 -0
  1026. psychopy/app/builder/__init__.py +5 -0
  1027. psychopy/app/builder/builder.py +4738 -0
  1028. psychopy/app/builder/dialogs/__init__.py +1910 -0
  1029. psychopy/app/builder/dialogs/dlgsCode.py +631 -0
  1030. psychopy/app/builder/dialogs/dlgsConditions.py +669 -0
  1031. psychopy/app/builder/dialogs/findDlg.py +275 -0
  1032. psychopy/app/builder/dialogs/paramCtrls.py +1802 -0
  1033. psychopy/app/builder/validators.py +589 -0
  1034. psychopy/app/coder/__init__.py +8 -0
  1035. psychopy/app/coder/codeEditorBase.py +460 -0
  1036. psychopy/app/coder/coder.py +3100 -0
  1037. psychopy/app/coder/fileBrowser.py +714 -0
  1038. psychopy/app/coder/folding.py +128 -0
  1039. psychopy/app/coder/psychoParser.py +139 -0
  1040. psychopy/app/coder/repl.py +496 -0
  1041. psychopy/app/coder/sourceTree.py +302 -0
  1042. psychopy/app/colorpicker/__init__.py +594 -0
  1043. psychopy/app/colorpicker/ui.py +336 -0
  1044. psychopy/app/connections/__init__.py +10 -0
  1045. psychopy/app/connections/news.py +104 -0
  1046. psychopy/app/connections/sendusage.py +61 -0
  1047. psychopy/app/connections/updates.py +642 -0
  1048. psychopy/app/console.py +164 -0
  1049. psychopy/app/deviceManager/__init__.py +1 -0
  1050. psychopy/app/deviceManager/addDialog.py +218 -0
  1051. psychopy/app/deviceManager/dialog.py +185 -0
  1052. psychopy/app/deviceManager/panel.py +191 -0
  1053. psychopy/app/deviceManager/utils.py +60 -0
  1054. psychopy/app/dialogs.py +664 -0
  1055. psychopy/app/errorDlg.py +238 -0
  1056. psychopy/app/frametracker.py +8 -0
  1057. psychopy/app/idle.py +181 -0
  1058. psychopy/app/jobs.py +676 -0
  1059. psychopy/app/linuxconfig/__init__.py +153 -0
  1060. psychopy/app/linuxconfig/ui.py +88 -0
  1061. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  1062. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +12695 -0
  1063. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  1064. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +10199 -0
  1065. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  1066. psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +10199 -0
  1067. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  1068. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +11221 -0
  1069. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  1070. psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +10200 -0
  1071. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  1072. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +10200 -0
  1073. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  1074. psychopy/app/locale/en_US/LC_MESSAGE/messages.po +10195 -0
  1075. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  1076. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +11917 -0
  1077. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  1078. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +11924 -0
  1079. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  1080. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +11917 -0
  1081. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  1082. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +11084 -0
  1083. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  1084. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +11590 -0
  1085. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  1086. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +10199 -0
  1087. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  1088. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +11091 -0
  1089. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  1090. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +11072 -0
  1091. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  1092. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +11071 -0
  1093. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  1094. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +10200 -0
  1095. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  1096. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +11072 -0
  1097. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  1098. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +11268 -0
  1099. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  1100. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +10199 -0
  1101. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  1102. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +11463 -0
  1103. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  1104. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +10200 -0
  1105. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  1106. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +10200 -0
  1107. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  1108. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +10200 -0
  1109. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  1110. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +11288 -0
  1111. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  1112. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +10200 -0
  1113. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  1114. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +10199 -0
  1115. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  1116. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +11441 -0
  1117. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  1118. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +11069 -0
  1119. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  1120. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +12085 -0
  1121. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  1122. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +11929 -0
  1123. psychopy/app/localizedStrings.py +386 -0
  1124. psychopy/app/pavlovia_ui/__init__.py +22 -0
  1125. psychopy/app/pavlovia_ui/_base.py +268 -0
  1126. psychopy/app/pavlovia_ui/functions.py +176 -0
  1127. psychopy/app/pavlovia_ui/menu.py +140 -0
  1128. psychopy/app/pavlovia_ui/project.py +943 -0
  1129. psychopy/app/pavlovia_ui/search.py +444 -0
  1130. psychopy/app/pavlovia_ui/sync.py +137 -0
  1131. psychopy/app/pavlovia_ui/user.py +264 -0
  1132. psychopy/app/plugin_manager/__init__.py +5 -0
  1133. psychopy/app/plugin_manager/dialog.py +402 -0
  1134. psychopy/app/plugin_manager/output.py +132 -0
  1135. psychopy/app/plugin_manager/packageIndex.py +303 -0
  1136. psychopy/app/plugin_manager/packages.py +643 -0
  1137. psychopy/app/plugin_manager/plugins.py +1363 -0
  1138. psychopy/app/plugin_manager/utils.py +115 -0
  1139. psychopy/app/preferencesDlg.py +771 -0
  1140. psychopy/app/psychopyApp.py +207 -0
  1141. psychopy/app/ribbon.py +1019 -0
  1142. psychopy/app/runner/__init__.py +1 -0
  1143. psychopy/app/runner/runner.py +1299 -0
  1144. psychopy/app/runner/scriptProcess.py +363 -0
  1145. psychopy/app/stdout/__init__.py +1 -0
  1146. psychopy/app/stdout/stdOutRich.py +410 -0
  1147. psychopy/app/sysInfoDlg.py +242 -0
  1148. psychopy/app/themes/__init__.py +78 -0
  1149. psychopy/app/themes/colors.py +201 -0
  1150. psychopy/app/themes/css/contrast_black.css +112 -0
  1151. psychopy/app/themes/css/contrast_white.css +115 -0
  1152. psychopy/app/themes/css/dark.css +112 -0
  1153. psychopy/app/themes/css/light.css +115 -0
  1154. psychopy/app/themes/fonts.py +629 -0
  1155. psychopy/app/themes/handlers.py +321 -0
  1156. psychopy/app/themes/icons.py +267 -0
  1157. psychopy/app/themes/spec/Classic.json +154 -0
  1158. psychopy/app/themes/spec/ClassicDark.json +154 -0
  1159. psychopy/app/themes/spec/GitHub.json +154 -0
  1160. psychopy/app/themes/spec/HiVisDark.json +153 -0
  1161. psychopy/app/themes/spec/HiVisLight.json +154 -0
  1162. psychopy/app/themes/spec/MinimalDark.json +154 -0
  1163. psychopy/app/themes/spec/MinimalLight.json +153 -0
  1164. psychopy/app/themes/spec/PsychopyDark.json +231 -0
  1165. psychopy/app/themes/spec/PsychopyLight.json +236 -0
  1166. psychopy/app/themes/ui.py +74 -0
  1167. psychopy/app/ui/__init__.py +184 -0
  1168. psychopy/app/urls.py +31 -0
  1169. psychopy/app/utils.py +1741 -0
  1170. psychopy/app/viewer/__init__.py +0 -0
  1171. psychopy/assets/Psychopy Window Favicon@16w.png +0 -0
  1172. psychopy/assets/Psychopy Window Favicon@32w.png +0 -0
  1173. psychopy/assets/USB-C.png +0 -0
  1174. psychopy/assets/USB.png +0 -0
  1175. psychopy/assets/__init__.py +0 -0
  1176. psychopy/assets/click.png +0 -0
  1177. psychopy/assets/clicknext.png +0 -0
  1178. psychopy/assets/creditCard.png +0 -0
  1179. psychopy/assets/default.mp3 +0 -0
  1180. psychopy/assets/default.mp4 +0 -0
  1181. psychopy/assets/default.png +0 -0
  1182. psychopy/assets/fonts/Arvo-Bold.ttf +0 -0
  1183. psychopy/assets/fonts/Arvo-BoldItalic.ttf +0 -0
  1184. psychopy/assets/fonts/Arvo-Italic.ttf +0 -0
  1185. psychopy/assets/fonts/Arvo-Regular.ttf +0 -0
  1186. psychopy/assets/fonts/DejaVuSerif.ttf +0 -0
  1187. psychopy/assets/fonts/IndieFlower-Regular.ttf +0 -0
  1188. psychopy/assets/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  1189. psychopy/assets/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
  1190. psychopy/assets/fonts/NotoSans-Bold.ttf +0 -0
  1191. psychopy/assets/fonts/NotoSans-BoldItalic.ttf +0 -0
  1192. psychopy/assets/fonts/NotoSans-Italic.ttf +0 -0
  1193. psychopy/assets/fonts/NotoSans-Regular.ttf +0 -0
  1194. psychopy/assets/next.png +0 -0
  1195. psychopy/assets/psychopy.ico +0 -0
  1196. psychopy/assets/psychopy.png +0 -0
  1197. psychopy/assets/templates/__init__.py +0 -0
  1198. psychopy/assets/templates/instruct1.png +0 -0
  1199. psychopy/assets/templates/instruct2.png +0 -0
  1200. psychopy/assets/touch.png +0 -0
  1201. psychopy/assets/touchnext.png +0 -0
  1202. psychopy/assets/voicekeyThresholdStim.wav +0 -0
  1203. psychopy/assets/window.ico +0 -0
  1204. psychopy/changes/2023.1.0.md +9 -0
  1205. psychopy/changes/2024.1.0.md +16 -0
  1206. psychopy/changes/__init__.py +0 -0
  1207. psychopy/clock.py +619 -0
  1208. psychopy/colors.py +1058 -0
  1209. psychopy/compatibility.py +147 -0
  1210. psychopy/constants.py +87 -0
  1211. psychopy/contrib/__init__.py +0 -0
  1212. psychopy/contrib/configobj/LICENSE +39 -0
  1213. psychopy/contrib/configobj/__init__.py +2455 -0
  1214. psychopy/contrib/configobj/_version.py +2 -0
  1215. psychopy/contrib/configobj/validate.py +1458 -0
  1216. psychopy/contrib/lazy_import.py +402 -0
  1217. psychopy/contrib/mseq.py +273 -0
  1218. psychopy/contrib/mseqSearch.py +183 -0
  1219. psychopy/contrib/psi.py +100 -0
  1220. psychopy/contrib/quest.py +484 -0
  1221. psychopy/contrib/tesselate.py +188 -0
  1222. psychopy/core.py +167 -0
  1223. psychopy/data/__init__.py +45 -0
  1224. psychopy/data/base.py +566 -0
  1225. psychopy/data/counterbalance.py +210 -0
  1226. psychopy/data/experiment.py +1049 -0
  1227. psychopy/data/fit.py +248 -0
  1228. psychopy/data/routine.py +94 -0
  1229. psychopy/data/shelf.py +237 -0
  1230. psychopy/data/staircase.py +2270 -0
  1231. psychopy/data/trial.py +2505 -0
  1232. psychopy/data/utils.py +858 -0
  1233. psychopy/demos/__init__.py +0 -0
  1234. psychopy/demos/builder/Design Templates/branchedExperiment/README.md +5 -0
  1235. psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +354 -0
  1236. psychopy/demos/builder/Design Templates/branchedExperiment/trialTypes.xlsx +0 -0
  1237. psychopy/demos/builder/Design Templates/psychophysicsStaircase/README.md +20 -0
  1238. psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +286 -0
  1239. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/README.md +14 -0
  1240. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +340 -0
  1241. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/stairDefinitions.xlsx +0 -0
  1242. psychopy/demos/builder/Design Templates/randomisedBlocks/README.md +18 -0
  1243. psychopy/demos/builder/Design Templates/randomisedBlocks/chooseBlock.xlsx +0 -0
  1244. psychopy/demos/builder/Design Templates/randomisedBlocks/facesBlock.xlsx +0 -0
  1245. psychopy/demos/builder/Design Templates/randomisedBlocks/housesBlock.xlsx +0 -0
  1246. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +219 -0
  1247. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face01.jpg +0 -0
  1248. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face02.jpg +0 -0
  1249. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face03.jpg +0 -0
  1250. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house01.jpg +0 -0
  1251. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house02.jpg +0 -0
  1252. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house03.jpg +0 -0
  1253. psychopy/demos/builder/Experiments/BART/README.md +30 -0
  1254. psychopy/demos/builder/Experiments/BART/assets/background.jpg +0 -0
  1255. psychopy/demos/builder/Experiments/BART/assets/background.png +0 -0
  1256. psychopy/demos/builder/Experiments/BART/assets/bang.mp3 +0 -0
  1257. psychopy/demos/builder/Experiments/BART/assets/bang.wav +0 -0
  1258. psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
  1259. psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
  1260. psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
  1261. psychopy/demos/builder/Experiments/BART/bart.psyexp +844 -0
  1262. psychopy/demos/builder/Experiments/BART/spreadsheets/conditions.xlsx +0 -0
  1263. psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +255 -0
  1264. psychopy/demos/builder/Experiments/BigFiveInventory/README.md +23 -0
  1265. psychopy/demos/builder/Experiments/BigFiveInventory/TIPI.xlsx +0 -0
  1266. psychopy/demos/builder/Experiments/BigFiveInventory/bigFiveItems.xlsx +0 -0
  1267. psychopy/demos/builder/Experiments/BigFiveInventory/demographics.xlsx +0 -0
  1268. psychopy/demos/builder/Experiments/BigFiveInventory/mini_IPIP.xlsx +0 -0
  1269. psychopy/demos/builder/Experiments/GoNoGo/conditions.xlsx +0 -0
  1270. psychopy/demos/builder/Experiments/GoNoGo/gng.psyexp +462 -0
  1271. psychopy/demos/builder/Experiments/GoNoGo/go.png +0 -0
  1272. psychopy/demos/builder/Experiments/GoNoGo/nogo.png +0 -0
  1273. psychopy/demos/builder/Experiments/dragAndDrop/README.md +5 -0
  1274. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +494 -0
  1275. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/blank_grid.png +0 -0
  1276. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/make_shapes.psyexp +221 -0
  1277. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/readme.md +4 -0
  1278. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_1.png +0 -0
  1279. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_10.png +0 -0
  1280. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_2.png +0 -0
  1281. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_3.png +0 -0
  1282. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_4.png +0 -0
  1283. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_5.png +0 -0
  1284. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_6.png +0 -0
  1285. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_7.png +0 -0
  1286. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_8.png +0 -0
  1287. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_9.png +0 -0
  1288. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  1289. psychopy/demos/builder/Experiments/goNoGo/readme.md +14 -0
  1290. psychopy/demos/builder/Experiments/mentalRotation/F.png +0 -0
  1291. psychopy/demos/builder/Experiments/mentalRotation/FR.png +0 -0
  1292. psychopy/demos/builder/Experiments/mentalRotation/MentalRot.csv +33 -0
  1293. psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +646 -0
  1294. psychopy/demos/builder/Experiments/mentalRotation/README.md +23 -0
  1295. psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +509 -0
  1296. psychopy/demos/builder/Experiments/navon/README.md +26 -0
  1297. psychopy/demos/builder/Experiments/navon/bigHsmallH.png +0 -0
  1298. psychopy/demos/builder/Experiments/navon/bigHsmallS.png +0 -0
  1299. psychopy/demos/builder/Experiments/navon/bigSsmallH.png +0 -0
  1300. psychopy/demos/builder/Experiments/navon/bigSsmallS.png +0 -0
  1301. psychopy/demos/builder/Experiments/navon/mask.png +0 -0
  1302. psychopy/demos/builder/Experiments/navon/stimuli.pptx +0 -0
  1303. psychopy/demos/builder/Experiments/navon/trialTypes.xlsx +0 -0
  1304. psychopy/demos/builder/Experiments/sternberg/README.md +14 -0
  1305. psychopy/demos/builder/Experiments/sternberg/mainTrials.xlsx +0 -0
  1306. psychopy/demos/builder/Experiments/sternberg/pracTrials.xlsx +0 -0
  1307. psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +649 -0
  1308. psychopy/demos/builder/Experiments/stroop/README.md +15 -0
  1309. psychopy/demos/builder/Experiments/stroop/stroop.psyexp +321 -0
  1310. psychopy/demos/builder/Experiments/stroop/trialTypes.csv +7 -0
  1311. psychopy/demos/builder/Experiments/stroopExtended/README.md +14 -0
  1312. psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +418 -0
  1313. psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +418 -0
  1314. psychopy/demos/builder/Experiments/stroopExtended/trialTypes.xlsx +0 -0
  1315. psychopy/demos/builder/Experiments/stroopExtended/trialTypesReverse.xlsx +0 -0
  1316. psychopy/demos/builder/Experiments/stroopVoice/README.md +27 -0
  1317. psychopy/demos/builder/Experiments/stroopVoice/conditions.xlsx +0 -0
  1318. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +382 -0
  1319. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +371 -0
  1320. psychopy/demos/builder/Feature Demos/buttonBox/readme.md +5 -0
  1321. psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +308 -0
  1322. psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +391 -0
  1323. psychopy/demos/builder/Feature Demos/gratings/readme.md +8 -0
  1324. psychopy/demos/builder/Feature Demos/movies/movie.psyexp +220 -0
  1325. psychopy/demos/builder/Feature Demos/movies/readme.md +3 -0
  1326. psychopy/demos/builder/Feature Demos/noise/face.jpg +0 -0
  1327. psychopy/demos/builder/Feature Demos/noise/noise.psyexp +477 -0
  1328. psychopy/demos/builder/Feature Demos/noise/readme.md +3 -0
  1329. psychopy/demos/builder/Feature Demos/panorama/panImg.jpg +0 -0
  1330. psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +177 -0
  1331. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +434 -0
  1332. psychopy/demos/builder/Feature Demos/pilotMode/readme.md +7 -0
  1333. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +451 -0
  1334. psychopy/demos/builder/Feature Demos/progress/readme.md +1 -0
  1335. psychopy/demos/builder/Feature Demos/sliders/README.md +8 -0
  1336. psychopy/demos/builder/Feature Demos/sliders/fruitConditions.xlsx +0 -0
  1337. psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +984 -0
  1338. psychopy/demos/builder/Feature Demos/visualValidator/readme.md +7 -0
  1339. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +199 -0
  1340. psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +639 -0
  1341. psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +415 -0
  1342. psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +659 -0
  1343. psychopy/demos/builder/Hardware/EGI_netstation/README.md +17 -0
  1344. psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +339 -0
  1345. psychopy/demos/builder/Hardware/EGI_netstation/trialTypesEEG.csv +7 -0
  1346. psychopy/demos/builder/Hardware/Eyetracking_visual_search/readme.md +19 -0
  1347. psychopy/demos/builder/Hardware/Eyetracking_visual_search/trials_params.xlsx +0 -0
  1348. psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +830 -0
  1349. psychopy/demos/builder/Hardware/camera/camera.psyexp +330 -0
  1350. psychopy/demos/builder/Hardware/camera/readme.md +6 -0
  1351. psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +459 -0
  1352. psychopy/demos/builder/Hardware/eyetracking/readme.md +7 -0
  1353. psychopy/demos/builder/Hardware/eyetracking_custom_cal/Bullseye_grey.mov +0 -0
  1354. psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +468 -0
  1355. psychopy/demos/builder/Hardware/eyetracking_custom_cal/readme.md +7 -0
  1356. psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +211 -0
  1357. psychopy/demos/builder/Hardware/fMRI/readme.md +7 -0
  1358. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +360 -0
  1359. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy.psyexp +360 -0
  1360. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy_legacy.psyexp +312 -0
  1361. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +360 -0
  1362. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy.psyexp +360 -0
  1363. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy_legacy.psyexp +312 -0
  1364. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +484 -0
  1365. psychopy/demos/builder/Hardware/microphone/phrases.xlsx +0 -0
  1366. psychopy/demos/builder/Hardware/microphone/readme.md +9 -0
  1367. psychopy/demos/builder/Hardware/pump/README.md +19 -0
  1368. psychopy/demos/builder/Hardware/pump/pump.psyexp +646 -0
  1369. psychopy/demos/builder/Helper Tools/achorVSalignment/FlowCircular-Regular.ttf +0 -0
  1370. psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +350 -0
  1371. psychopy/demos/builder/Helper Tools/achorVSalignment/readme.md +5 -0
  1372. psychopy/demos/builder/Helper Tools/clockFace/README.md +3 -0
  1373. psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +211 -0
  1374. psychopy/demos/builder/Helper Tools/colors/README.md +5 -0
  1375. psychopy/demos/builder/Helper Tools/colors/colors.psyexp +321 -0
  1376. psychopy/demos/builder/Helper Tools/drawPolygon/README.md +5 -0
  1377. psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +704 -0
  1378. psychopy/demos/builder/Helper Tools/keyNameFinder/README.md +14 -0
  1379. psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +225 -0
  1380. psychopy/demos/builder/Helper Tools/spatialUnits/README.md +6 -0
  1381. psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +206 -0
  1382. psychopy/demos/builder/README.txt +43 -0
  1383. psychopy/demos/builder/__init__.py +0 -0
  1384. psychopy/demos/coder/__init__.py +0 -0
  1385. psychopy/demos/coder/basic/hello_world.py +37 -0
  1386. psychopy/demos/coder/csvFromPsydat.py +29 -0
  1387. psychopy/demos/coder/experiment control/JND_staircase_analysis.py +85 -0
  1388. psychopy/demos/coder/experiment control/JND_staircase_exp.py +110 -0
  1389. psychopy/demos/coder/experiment control/TrialHandler.py +64 -0
  1390. psychopy/demos/coder/experiment control/TrialHandler2.py +42 -0
  1391. psychopy/demos/coder/experiment control/__init__.py +0 -0
  1392. psychopy/demos/coder/experiment control/autoDraw_autoLog.py +50 -0
  1393. psychopy/demos/coder/experiment control/experimentHandler.py +57 -0
  1394. psychopy/demos/coder/experiment control/fMRI_launchScan.py +74 -0
  1395. psychopy/demos/coder/experiment control/gammaMotionAnalysis.py +68 -0
  1396. psychopy/demos/coder/experiment control/gammaMotionNull.py +169 -0
  1397. psychopy/demos/coder/experiment control/logFiles.py +55 -0
  1398. psychopy/demos/coder/experiment control/piloting.py +65 -0
  1399. psychopy/demos/coder/experiment control/runtimeInfo.py +79 -0
  1400. psychopy/demos/coder/hardware/CRS_BitsBox.py +75 -0
  1401. psychopy/demos/coder/hardware/CRS_BitsPlusPlus.py +73 -0
  1402. psychopy/demos/coder/hardware/RiftHeadTrackingExample.py +95 -0
  1403. psychopy/demos/coder/hardware/RiftMinimal.py +44 -0
  1404. psychopy/demos/coder/hardware/VSHD_Distortion.py +49 -0
  1405. psychopy/demos/coder/hardware/__init__.py +0 -0
  1406. psychopy/demos/coder/hardware/camera.py +90 -0
  1407. psychopy/demos/coder/hardware/cedrusRB730.py +38 -0
  1408. psychopy/demos/coder/hardware/crsBitsAdvancedDemo.py +780 -0
  1409. psychopy/demos/coder/hardware/egi_netstation.py +45 -0
  1410. psychopy/demos/coder/hardware/hdf5_extract.py +133 -0
  1411. psychopy/demos/coder/hardware/ioLab_bbox.py +62 -0
  1412. psychopy/demos/coder/hardware/labjack_u3.py +61 -0
  1413. psychopy/demos/coder/hardware/monitorDemo.py +28 -0
  1414. psychopy/demos/coder/hardware/parallelPortOutput.py +49 -0
  1415. psychopy/demos/coder/hardware/qmixPump.py +79 -0
  1416. psychopy/demos/coder/hardware/testSoundLatency.py +127 -0
  1417. psychopy/demos/coder/input/GUI.py +52 -0
  1418. psychopy/demos/coder/input/__init__.py +0 -0
  1419. psychopy/demos/coder/input/customMouse.py +51 -0
  1420. psychopy/demos/coder/input/joystick_universal.py +84 -0
  1421. psychopy/demos/coder/input/keyNameFinder.py +39 -0
  1422. psychopy/demos/coder/input/mic.png +0 -0
  1423. psychopy/demos/coder/input/mouse.py +66 -0
  1424. psychopy/demos/coder/iohub/delaytest.py +231 -0
  1425. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/canal.jpg +0 -0
  1426. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/fall.jpg +0 -0
  1427. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/lake.jpg +0 -0
  1428. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/party.jpg +0 -0
  1429. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/swimming.jpg +0 -0
  1430. psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByConditionVariables.py +67 -0
  1431. psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByMessages.py +34 -0
  1432. psychopy/demos/coder/iohub/eyetracking/gcCursor/run.py +323 -0
  1433. psychopy/demos/coder/iohub/eyetracking/gcCursor/trial_conditions.xlsx +0 -0
  1434. psychopy/demos/coder/iohub/eyetracking/simple.py +147 -0
  1435. psychopy/demos/coder/iohub/eyetracking/validation.py +251 -0
  1436. psychopy/demos/coder/iohub/iodatastore/saveEventReport.py +56 -0
  1437. psychopy/demos/coder/iohub/keyboard.py +189 -0
  1438. psychopy/demos/coder/iohub/keyboardreactiontime.py +88 -0
  1439. psychopy/demos/coder/iohub/launchHub.py +160 -0
  1440. psychopy/demos/coder/iohub/mouse.py +112 -0
  1441. psychopy/demos/coder/iohub/mouse_multi_window.py +124 -0
  1442. psychopy/demos/coder/iohub/serial/_parseserial.py +53 -0
  1443. psychopy/demos/coder/iohub/serial/customparser.py +84 -0
  1444. psychopy/demos/coder/iohub/serial/pstbox.py +166 -0
  1445. psychopy/demos/coder/iohub/wintab/_wintabgraphics.py +281 -0
  1446. psychopy/demos/coder/iohub/wintab/pen_demo.py +260 -0
  1447. psychopy/demos/coder/misc/encrypt_data.py +48 -0
  1448. psychopy/demos/coder/misc/hdf5_2_csv +33 -0
  1449. psychopy/demos/coder/misc/makeMovie.py +28 -0
  1450. psychopy/demos/coder/misc/rigidBodyTransform.py +76 -0
  1451. psychopy/demos/coder/stimuli/Campaign.ttf +0 -0
  1452. psychopy/demos/coder/stimuli/MovieStim.py +65 -0
  1453. psychopy/demos/coder/stimuli/__init__.py +0 -0
  1454. psychopy/demos/coder/stimuli/aperture.py +37 -0
  1455. psychopy/demos/coder/stimuli/beach.jpg +0 -0
  1456. psychopy/demos/coder/stimuli/bufferImageStim.py +78 -0
  1457. psychopy/demos/coder/stimuli/clockface.py +43 -0
  1458. psychopy/demos/coder/stimuli/colorPalette.py +134 -0
  1459. psychopy/demos/coder/stimuli/compare_text_timing.py +193 -0
  1460. psychopy/demos/coder/stimuli/counterphase.py +43 -0
  1461. psychopy/demos/coder/stimuli/customTextures.py +57 -0
  1462. psychopy/demos/coder/stimuli/dot_gabors.py +35 -0
  1463. psychopy/demos/coder/stimuli/dots.py +35 -0
  1464. psychopy/demos/coder/stimuli/elementArrays.py +92 -0
  1465. psychopy/demos/coder/stimuli/embeddedOpenGL.py +35 -0
  1466. psychopy/demos/coder/stimuli/face.jpg +0 -0
  1467. psychopy/demos/coder/stimuli/face_jpg.py +48 -0
  1468. psychopy/demos/coder/stimuli/gabor.py +30 -0
  1469. psychopy/demos/coder/stimuli/imagesAndPatches.py +51 -0
  1470. psychopy/demos/coder/stimuli/jwpIntro.mp4 +0 -0
  1471. psychopy/demos/coder/stimuli/kanizsa.py +41 -0
  1472. psychopy/demos/coder/stimuli/maskReveal.py +63 -0
  1473. psychopy/demos/coder/stimuli/plaid.py +43 -0
  1474. psychopy/demos/coder/stimuli/ratingScale.py +183 -0
  1475. psychopy/demos/coder/stimuli/rotatingFlashingWedge.py +37 -0
  1476. psychopy/demos/coder/stimuli/screensAndWindows.py +58 -0
  1477. psychopy/demos/coder/stimuli/secondOrderGratings.py +66 -0
  1478. psychopy/demos/coder/stimuli/shapeContains.py +50 -0
  1479. psychopy/demos/coder/stimuli/shapes.py +63 -0
  1480. psychopy/demos/coder/stimuli/soundStimuli.py +49 -0
  1481. psychopy/demos/coder/stimuli/starField.py +48 -0
  1482. psychopy/demos/coder/stimuli/stim3d.py +107 -0
  1483. psychopy/demos/coder/stimuli/textBoxStim/textbox_glyph_placement.py +93 -0
  1484. psychopy/demos/coder/stimuli/textBoxStim/textbox_simple.py +71 -0
  1485. psychopy/demos/coder/stimuli/textStimuli.py +106 -0
  1486. psychopy/demos/coder/stimuli/textbox_editable.py +64 -0
  1487. psychopy/demos/coder/stimuli/variousVisualStims.py +47 -0
  1488. psychopy/demos/coder/stimuli/visual_noise.py +31 -0
  1489. psychopy/demos/coder/sysInfo.py +56 -0
  1490. psychopy/demos/coder/timing/__init__.py +0 -0
  1491. psychopy/demos/coder/timing/callOnFlip.py +39 -0
  1492. psychopy/demos/coder/timing/clocksAndTimers.py +46 -0
  1493. psychopy/demos/coder/timing/millikeyKeyboardTimingTest.py +323 -0
  1494. psychopy/demos/coder/timing/timeByFrames.py +80 -0
  1495. psychopy/demos/coder/timing/timeByFramesEx.py +107 -0
  1496. psychopy/demos/coder/understanding psychopy/colors.py +70 -0
  1497. psychopy/demos/coder/understanding psychopy/fontLayout.py +69 -0
  1498. psychopy/demos/demo_migration.py +214 -0
  1499. psychopy/demos/modernizeDemos.py +16 -0
  1500. psychopy/demos/test_demo_migration.py +155 -0
  1501. psychopy/devices/__init__.py +8 -0
  1502. psychopy/event.py +1339 -0
  1503. psychopy/exceptions.py +59 -0
  1504. psychopy/experiment/__init__.py +51 -0
  1505. psychopy/experiment/_experiment.py +1514 -0
  1506. psychopy/experiment/blankTemplate.xltx +0 -0
  1507. psychopy/experiment/components/__init__.py +402 -0
  1508. psychopy/experiment/components/_base.py +1689 -0
  1509. psychopy/experiment/components/aperture/__init__.py +153 -0
  1510. psychopy/experiment/components/aperture/classic/aperture.png +0 -0
  1511. psychopy/experiment/components/aperture/classic/aperture@2x.png +0 -0
  1512. psychopy/experiment/components/aperture/dark/aperture.png +0 -0
  1513. psychopy/experiment/components/aperture/dark/aperture@2x.png +0 -0
  1514. psychopy/experiment/components/aperture/light/aperture.png +0 -0
  1515. psychopy/experiment/components/aperture/light/aperture@2x.png +0 -0
  1516. psychopy/experiment/components/brush/__init__.py +165 -0
  1517. psychopy/experiment/components/brush/classic/brush.png +0 -0
  1518. psychopy/experiment/components/brush/classic/brush@2x.png +0 -0
  1519. psychopy/experiment/components/brush/dark/brush.png +0 -0
  1520. psychopy/experiment/components/brush/dark/brush@2x.png +0 -0
  1521. psychopy/experiment/components/brush/light/brush.png +0 -0
  1522. psychopy/experiment/components/brush/light/brush@2x.png +0 -0
  1523. psychopy/experiment/components/button/__init__.py +493 -0
  1524. psychopy/experiment/components/button/classic/button.png +0 -0
  1525. psychopy/experiment/components/button/classic/button@2x.png +0 -0
  1526. psychopy/experiment/components/button/dark/button.png +0 -0
  1527. psychopy/experiment/components/button/dark/button@2x.png +0 -0
  1528. psychopy/experiment/components/button/light/button.png +0 -0
  1529. psychopy/experiment/components/button/light/button@2x.png +0 -0
  1530. psychopy/experiment/components/buttonBox/__init__.py +322 -0
  1531. psychopy/experiment/components/buttonBox/classic/buttonBox.png +0 -0
  1532. psychopy/experiment/components/buttonBox/classic/buttonBox@2x.png +0 -0
  1533. psychopy/experiment/components/buttonBox/dark/buttonBox.png +0 -0
  1534. psychopy/experiment/components/buttonBox/dark/buttonBox@2x.png +0 -0
  1535. psychopy/experiment/components/buttonBox/light/buttonBox.png +0 -0
  1536. psychopy/experiment/components/buttonBox/light/buttonBox@2x.png +0 -0
  1537. psychopy/experiment/components/camera/__init__.py +381 -0
  1538. psychopy/experiment/components/camera/classic/webcam.png +0 -0
  1539. psychopy/experiment/components/camera/classic/webcam@2x.png +0 -0
  1540. psychopy/experiment/components/camera/dark/webcam.png +0 -0
  1541. psychopy/experiment/components/camera/dark/webcam@2x.png +0 -0
  1542. psychopy/experiment/components/camera/light/webcam.png +0 -0
  1543. psychopy/experiment/components/camera/light/webcam@2x.png +0 -0
  1544. psychopy/experiment/components/code/__init__.py +299 -0
  1545. psychopy/experiment/components/code/classic/code.png +0 -0
  1546. psychopy/experiment/components/code/classic/code@2x.png +0 -0
  1547. psychopy/experiment/components/code/dark/code.png +0 -0
  1548. psychopy/experiment/components/code/dark/code@2x.png +0 -0
  1549. psychopy/experiment/components/code/light/code.png +0 -0
  1550. psychopy/experiment/components/code/light/code@2x.png +0 -0
  1551. psychopy/experiment/components/dots/__init__.py +199 -0
  1552. psychopy/experiment/components/dots/classic/dots.png +0 -0
  1553. psychopy/experiment/components/dots/classic/dots@2x.png +0 -0
  1554. psychopy/experiment/components/dots/dark/dots.png +0 -0
  1555. psychopy/experiment/components/dots/dark/dots@2x.png +0 -0
  1556. psychopy/experiment/components/dots/dots.xcf +0 -0
  1557. psychopy/experiment/components/dots/light/dots.png +0 -0
  1558. psychopy/experiment/components/dots/light/dots@2x.png +0 -0
  1559. psychopy/experiment/components/eyetracker_record/__init__.py +194 -0
  1560. psychopy/experiment/components/eyetracker_record/classic/eyetracker_record.png +0 -0
  1561. psychopy/experiment/components/eyetracker_record/classic/eyetracker_record@2x.png +0 -0
  1562. psychopy/experiment/components/eyetracker_record/dark/eyetracker_record.png +0 -0
  1563. psychopy/experiment/components/eyetracker_record/dark/eyetracker_record@2x.png +0 -0
  1564. psychopy/experiment/components/eyetracker_record/light/eyetracker_record.png +0 -0
  1565. psychopy/experiment/components/eyetracker_record/light/eyetracker_record@2x.png +0 -0
  1566. psychopy/experiment/components/form/__init__.py +227 -0
  1567. psychopy/experiment/components/form/classic/form.png +0 -0
  1568. psychopy/experiment/components/form/classic/form@2x.png +0 -0
  1569. psychopy/experiment/components/form/dark/form.png +0 -0
  1570. psychopy/experiment/components/form/dark/form@2x.png +0 -0
  1571. psychopy/experiment/components/form/formItems.xltx +0 -0
  1572. psychopy/experiment/components/form/light/form.png +0 -0
  1573. psychopy/experiment/components/form/light/form@2x.png +0 -0
  1574. psychopy/experiment/components/grating/__init__.py +203 -0
  1575. psychopy/experiment/components/grating/classic/grating.png +0 -0
  1576. psychopy/experiment/components/grating/classic/grating@2x.png +0 -0
  1577. psychopy/experiment/components/grating/dark/grating.png +0 -0
  1578. psychopy/experiment/components/grating/dark/grating@2x.png +0 -0
  1579. psychopy/experiment/components/grating/light/grating.png +0 -0
  1580. psychopy/experiment/components/grating/light/grating@2x.png +0 -0
  1581. psychopy/experiment/components/image/__init__.py +195 -0
  1582. psychopy/experiment/components/image/classic/image.png +0 -0
  1583. psychopy/experiment/components/image/classic/image@2x.png +0 -0
  1584. psychopy/experiment/components/image/dark/image.png +0 -0
  1585. psychopy/experiment/components/image/dark/image@2x.png +0 -0
  1586. psychopy/experiment/components/image/light/image.png +0 -0
  1587. psychopy/experiment/components/image/light/image@2x.png +0 -0
  1588. psychopy/experiment/components/joyButtons/__init__.py +453 -0
  1589. psychopy/experiment/components/joyButtons/classic/joyButtons.png +0 -0
  1590. psychopy/experiment/components/joyButtons/classic/joybuttons@2x.png +0 -0
  1591. psychopy/experiment/components/joyButtons/dark/joyButtons.png +0 -0
  1592. psychopy/experiment/components/joyButtons/dark/joyButtons@2x.png +0 -0
  1593. psychopy/experiment/components/joyButtons/light/joyButtons.png +0 -0
  1594. psychopy/experiment/components/joyButtons/light/joyButtons@2x.png +0 -0
  1595. psychopy/experiment/components/joyButtons/virtualJoyButtons.py +34 -0
  1596. psychopy/experiment/components/joystick/__init__.py +572 -0
  1597. psychopy/experiment/components/joystick/classic/joystick.png +0 -0
  1598. psychopy/experiment/components/joystick/classic/joystick@2x.png +0 -0
  1599. psychopy/experiment/components/joystick/dark/joystick.png +0 -0
  1600. psychopy/experiment/components/joystick/dark/joystick@2x.png +0 -0
  1601. psychopy/experiment/components/joystick/light/joystick.png +0 -0
  1602. psychopy/experiment/components/joystick/light/joystick@2x.png +0 -0
  1603. psychopy/experiment/components/joystick/virtualJoystick.py +40 -0
  1604. psychopy/experiment/components/keyboard/__init__.py +568 -0
  1605. psychopy/experiment/components/keyboard/classic/keyboard.png +0 -0
  1606. psychopy/experiment/components/keyboard/classic/keyboard@2x.png +0 -0
  1607. psychopy/experiment/components/keyboard/dark/keyboard.png +0 -0
  1608. psychopy/experiment/components/keyboard/dark/keyboard@2x.png +0 -0
  1609. psychopy/experiment/components/keyboard/light/keyboard.png +0 -0
  1610. psychopy/experiment/components/keyboard/light/keyboard@2x.png +0 -0
  1611. psychopy/experiment/components/keyboard.xcf +0 -0
  1612. psychopy/experiment/components/microphone/__init__.py +600 -0
  1613. psychopy/experiment/components/microphone/classic/microphone.png +0 -0
  1614. psychopy/experiment/components/microphone/classic/microphone@2x.png +0 -0
  1615. psychopy/experiment/components/microphone/dark/microphone.png +0 -0
  1616. psychopy/experiment/components/microphone/dark/microphone@2x.png +0 -0
  1617. psychopy/experiment/components/microphone/light/microphone.png +0 -0
  1618. psychopy/experiment/components/microphone/light/microphone@2x.png +0 -0
  1619. psychopy/experiment/components/mouse/__init__.py +773 -0
  1620. psychopy/experiment/components/mouse/classic/mouse.png +0 -0
  1621. psychopy/experiment/components/mouse/classic/mouse@2x.png +0 -0
  1622. psychopy/experiment/components/mouse/dark/mouse.png +0 -0
  1623. psychopy/experiment/components/mouse/dark/mouse@2x.png +0 -0
  1624. psychopy/experiment/components/mouse/light/mouse.png +0 -0
  1625. psychopy/experiment/components/mouse/light/mouse@2x.png +0 -0
  1626. psychopy/experiment/components/movie/__init__.py +348 -0
  1627. psychopy/experiment/components/movie/classic/movie.png +0 -0
  1628. psychopy/experiment/components/movie/classic/movie@2x.png +0 -0
  1629. psychopy/experiment/components/movie/dark/movie.png +0 -0
  1630. psychopy/experiment/components/movie/dark/movie@2x.png +0 -0
  1631. psychopy/experiment/components/movie/light/movie.png +0 -0
  1632. psychopy/experiment/components/movie/light/movie@2x.png +0 -0
  1633. psychopy/experiment/components/panorama/__init__.py +456 -0
  1634. psychopy/experiment/components/panorama/classic/panorama.png +0 -0
  1635. psychopy/experiment/components/panorama/classic/panorama@2x.png +0 -0
  1636. psychopy/experiment/components/panorama/dark/panorama.png +0 -0
  1637. psychopy/experiment/components/panorama/dark/panorama@2x.png +0 -0
  1638. psychopy/experiment/components/panorama/light/panorama.png +0 -0
  1639. psychopy/experiment/components/panorama/light/panorama@2x.png +0 -0
  1640. psychopy/experiment/components/parallelOut/__init__.py +178 -0
  1641. psychopy/experiment/components/parallelOut/classic/parallel.png +0 -0
  1642. psychopy/experiment/components/parallelOut/classic/parallel@2x.png +0 -0
  1643. psychopy/experiment/components/parallelOut/dark/parallel.png +0 -0
  1644. psychopy/experiment/components/parallelOut/dark/parallel@2x.png +0 -0
  1645. psychopy/experiment/components/parallelOut/light/parallel.png +0 -0
  1646. psychopy/experiment/components/parallelOut/light/parallel@2x.png +0 -0
  1647. psychopy/experiment/components/polygon/__init__.py +332 -0
  1648. psychopy/experiment/components/polygon/classic/polygon.png +0 -0
  1649. psychopy/experiment/components/polygon/classic/polygon@2x.png +0 -0
  1650. psychopy/experiment/components/polygon/dark/polygon.png +0 -0
  1651. psychopy/experiment/components/polygon/dark/polygon@2x.png +0 -0
  1652. psychopy/experiment/components/polygon/light/polygon.png +0 -0
  1653. psychopy/experiment/components/polygon/light/polygon@2x.png +0 -0
  1654. psychopy/experiment/components/progress/__init__.py +130 -0
  1655. psychopy/experiment/components/progress/classic/progress.png +0 -0
  1656. psychopy/experiment/components/progress/classic/progress@2x.png +0 -0
  1657. psychopy/experiment/components/progress/dark/progress.png +0 -0
  1658. psychopy/experiment/components/progress/dark/progress@2x.png +0 -0
  1659. psychopy/experiment/components/progress/light/progress.png +0 -0
  1660. psychopy/experiment/components/progress/light/progress@2x.png +0 -0
  1661. psychopy/experiment/components/resourceManager/__init__.py +176 -0
  1662. psychopy/experiment/components/resourceManager/classic/resource_manager.png +0 -0
  1663. psychopy/experiment/components/resourceManager/classic/resource_manager@2x.png +0 -0
  1664. psychopy/experiment/components/resourceManager/dark/resource_manager.png +0 -0
  1665. psychopy/experiment/components/resourceManager/dark/resource_manager@2x.png +0 -0
  1666. psychopy/experiment/components/resourceManager/light/resource_manager.png +0 -0
  1667. psychopy/experiment/components/resourceManager/light/resource_manager@2x.png +0 -0
  1668. psychopy/experiment/components/roi/__init__.py +316 -0
  1669. psychopy/experiment/components/roi/classic/eyetracker_roi.png +0 -0
  1670. psychopy/experiment/components/roi/classic/eyetracker_roi@2x.png +0 -0
  1671. psychopy/experiment/components/roi/dark/eyetracker_roi.png +0 -0
  1672. psychopy/experiment/components/roi/dark/eyetracker_roi@2X.png +0 -0
  1673. psychopy/experiment/components/roi/light/eyetracker_roi.png +0 -0
  1674. psychopy/experiment/components/roi/light/eyetracker_roi@2X.png +0 -0
  1675. psychopy/experiment/components/routineSettings/__init__.py +387 -0
  1676. psychopy/experiment/components/routineSettings/classic/routineSettings.png +0 -0
  1677. psychopy/experiment/components/routineSettings/classic/routineSettings@2x.png +0 -0
  1678. psychopy/experiment/components/routineSettings/dark/routineSettings.png +0 -0
  1679. psychopy/experiment/components/routineSettings/dark/routineSettings@2x.png +0 -0
  1680. psychopy/experiment/components/routineSettings/light/routineSettings.png +0 -0
  1681. psychopy/experiment/components/routineSettings/light/routineSettings@2x.png +0 -0
  1682. psychopy/experiment/components/serialOut/__init__.py +304 -0
  1683. psychopy/experiment/components/serialOut/classic/serial.png +0 -0
  1684. psychopy/experiment/components/serialOut/classic/serial@2x.png +0 -0
  1685. psychopy/experiment/components/serialOut/dark/serial.png +0 -0
  1686. psychopy/experiment/components/serialOut/dark/serial@2x.png +0 -0
  1687. psychopy/experiment/components/serialOut/light/serial.png +0 -0
  1688. psychopy/experiment/components/serialOut/light/serial@2x.png +0 -0
  1689. psychopy/experiment/components/settings/JS_htmlHeader.tmpl +23 -0
  1690. psychopy/experiment/components/settings/JS_setupExp.tmpl +28 -0
  1691. psychopy/experiment/components/settings/__init__.py +2254 -0
  1692. psychopy/experiment/components/settings/classic/settings.png +0 -0
  1693. psychopy/experiment/components/settings/classic/settings@2x.png +0 -0
  1694. psychopy/experiment/components/settings/dark/settings.png +0 -0
  1695. psychopy/experiment/components/settings/dark/settings@2x.png +0 -0
  1696. psychopy/experiment/components/settings/eyetracking.py +108 -0
  1697. psychopy/experiment/components/settings/light/settings.png +0 -0
  1698. psychopy/experiment/components/settings/light/settings@2x.png +0 -0
  1699. psychopy/experiment/components/slider/__init__.py +410 -0
  1700. psychopy/experiment/components/slider/classic/slider.png +0 -0
  1701. psychopy/experiment/components/slider/classic/slider@2x.png +0 -0
  1702. psychopy/experiment/components/slider/dark/slider.png +0 -0
  1703. psychopy/experiment/components/slider/dark/slider@2x.png +0 -0
  1704. psychopy/experiment/components/slider/light/slider.png +0 -0
  1705. psychopy/experiment/components/slider/light/slider@2x.png +0 -0
  1706. psychopy/experiment/components/sound/__init__.py +441 -0
  1707. psychopy/experiment/components/sound/classic/sound.png +0 -0
  1708. psychopy/experiment/components/sound/classic/sound@2x.png +0 -0
  1709. psychopy/experiment/components/sound/dark/sound.png +0 -0
  1710. psychopy/experiment/components/sound/dark/sound@2x.png +0 -0
  1711. psychopy/experiment/components/sound/light/sound.png +0 -0
  1712. psychopy/experiment/components/sound/light/sound@2x.png +0 -0
  1713. psychopy/experiment/components/soundsensor/__init__.py +324 -0
  1714. psychopy/experiment/components/soundsensor/classic/soundsensor.png +0 -0
  1715. psychopy/experiment/components/soundsensor/classic/soundsensor@2x.png +0 -0
  1716. psychopy/experiment/components/soundsensor/dark/soundsensor.png +0 -0
  1717. psychopy/experiment/components/soundsensor/dark/soundsensor@2x.png +0 -0
  1718. psychopy/experiment/components/soundsensor/light/soundsensor.png +0 -0
  1719. psychopy/experiment/components/soundsensor/light/soundsensor@2x.png +0 -0
  1720. psychopy/experiment/components/static/__init__.py +293 -0
  1721. psychopy/experiment/components/static/classic/static.png +0 -0
  1722. psychopy/experiment/components/static/classic/static@2x.png +0 -0
  1723. psychopy/experiment/components/static/dark/static.png +0 -0
  1724. psychopy/experiment/components/static/dark/static@2x.png +0 -0
  1725. psychopy/experiment/components/static/light/static.png +0 -0
  1726. psychopy/experiment/components/static/light/static@2x.png +0 -0
  1727. psychopy/experiment/components/text/__init__.py +190 -0
  1728. psychopy/experiment/components/text/classic/text.png +0 -0
  1729. psychopy/experiment/components/text/classic/text@2x.png +0 -0
  1730. psychopy/experiment/components/text/dark/text.png +0 -0
  1731. psychopy/experiment/components/text/dark/text@2x.png +0 -0
  1732. psychopy/experiment/components/text/light/text.png +0 -0
  1733. psychopy/experiment/components/text/light/text@2x.png +0 -0
  1734. psychopy/experiment/components/textbox/__init__.py +336 -0
  1735. psychopy/experiment/components/textbox/classic/textbox.png +0 -0
  1736. psychopy/experiment/components/textbox/classic/textbox@2x.png +0 -0
  1737. psychopy/experiment/components/textbox/dark/textbox.png +0 -0
  1738. psychopy/experiment/components/textbox/dark/textbox@2x.png +0 -0
  1739. psychopy/experiment/components/textbox/light/textbox.png +0 -0
  1740. psychopy/experiment/components/textbox/light/textbox@2x.png +0 -0
  1741. psychopy/experiment/components/unknown/__init__.py +87 -0
  1742. psychopy/experiment/components/unknown/classic/unknown.png +0 -0
  1743. psychopy/experiment/components/unknown/classic/unknown@2x.png +0 -0
  1744. psychopy/experiment/components/unknown/dark/unknown.png +0 -0
  1745. psychopy/experiment/components/unknown/dark/unknown@2x.png +0 -0
  1746. psychopy/experiment/components/unknown/light/unknown.png +0 -0
  1747. psychopy/experiment/components/unknown/light/unknown@2x.png +0 -0
  1748. psychopy/experiment/components/unknownPlugin/__init__.py +86 -0
  1749. psychopy/experiment/components/unknownPlugin/classic/unknownPlugin.png +0 -0
  1750. psychopy/experiment/components/unknownPlugin/classic/unknownPlugin@2x.png +0 -0
  1751. psychopy/experiment/components/unknownPlugin/dark/unknownPlugin.png +0 -0
  1752. psychopy/experiment/components/unknownPlugin/dark/unknownPlugin@2x.png +0 -0
  1753. psychopy/experiment/components/unknownPlugin/light/unknownPlugin.png +0 -0
  1754. psychopy/experiment/components/unknownPlugin/light/unknownPlugin@2x.png +0 -0
  1755. psychopy/experiment/components/utils.py +0 -0
  1756. psychopy/experiment/components/variable/__init__.py +184 -0
  1757. psychopy/experiment/components/variable/classic/variable.png +0 -0
  1758. psychopy/experiment/components/variable/classic/variable@2x.png +0 -0
  1759. psychopy/experiment/components/variable/dark/variable.png +0 -0
  1760. psychopy/experiment/components/variable/dark/variable@2x.png +0 -0
  1761. psychopy/experiment/components/variable/light/variable.png +0 -0
  1762. psychopy/experiment/components/variable/light/variable@2x.png +0 -0
  1763. psychopy/experiment/devices.py +303 -0
  1764. psychopy/experiment/experiment.xsd +223 -0
  1765. psychopy/experiment/exports.py +390 -0
  1766. psychopy/experiment/flow.py +547 -0
  1767. psychopy/experiment/localization.py +11 -0
  1768. psychopy/experiment/loopTemplate.xltx +0 -0
  1769. psychopy/experiment/loops.py +1051 -0
  1770. psychopy/experiment/monitor.py +74 -0
  1771. psychopy/experiment/params.py +541 -0
  1772. psychopy/experiment/plugins.py +42 -0
  1773. psychopy/experiment/py2js.py +183 -0
  1774. psychopy/experiment/py2js_transpiler.py +613 -0
  1775. psychopy/experiment/questPlusTemplate.xltx +0 -0
  1776. psychopy/experiment/questTemplate.xltx +0 -0
  1777. psychopy/experiment/routines/__init__.py +108 -0
  1778. psychopy/experiment/routines/_base.py +1172 -0
  1779. psychopy/experiment/routines/audioValidator/__init__.py +229 -0
  1780. psychopy/experiment/routines/audioValidator/classic/audio_validator.png +0 -0
  1781. psychopy/experiment/routines/audioValidator/classic/audio_validator@2x.png +0 -0
  1782. psychopy/experiment/routines/audioValidator/dark/audio_validator.png +0 -0
  1783. psychopy/experiment/routines/audioValidator/dark/audio_validator@2x.png +0 -0
  1784. psychopy/experiment/routines/audioValidator/light/audio_validator.png +0 -0
  1785. psychopy/experiment/routines/audioValidator/light/audio_validator@2x.png +0 -0
  1786. psychopy/experiment/routines/counterbalance/__init__.py +320 -0
  1787. psychopy/experiment/routines/counterbalance/classic/counterbalance.png +0 -0
  1788. psychopy/experiment/routines/counterbalance/classic/counterbalance@2x.png +0 -0
  1789. psychopy/experiment/routines/counterbalance/counterbalanceItems.xltx +0 -0
  1790. psychopy/experiment/routines/counterbalance/dark/counterbalance.png +0 -0
  1791. psychopy/experiment/routines/counterbalance/dark/counterbalance@2x.png +0 -0
  1792. psychopy/experiment/routines/counterbalance/light/counterbalance.png +0 -0
  1793. psychopy/experiment/routines/counterbalance/light/counterbalance@2x.png +0 -0
  1794. psychopy/experiment/routines/eyetracker_calibrate/__init__.py +334 -0
  1795. psychopy/experiment/routines/eyetracker_calibrate/classic/eyetracker_calib.png +0 -0
  1796. psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib.png +0 -0
  1797. psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib@2x.png +0 -0
  1798. psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib.png +0 -0
  1799. psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib@2x.png +0 -0
  1800. psychopy/experiment/routines/eyetracker_validate/__init__.py +354 -0
  1801. psychopy/experiment/routines/eyetracker_validate/classic/eyetracker_valid.png +0 -0
  1802. psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid.png +0 -0
  1803. psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid@2x.png +0 -0
  1804. psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid.png +0 -0
  1805. psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid@2x.png +0 -0
  1806. psychopy/experiment/routines/pavlovia_survey/__init__.py +236 -0
  1807. psychopy/experiment/routines/pavlovia_survey/classic/survey.png +0 -0
  1808. psychopy/experiment/routines/pavlovia_survey/classic/survey@2x.png +0 -0
  1809. psychopy/experiment/routines/pavlovia_survey/dark/survey.png +0 -0
  1810. psychopy/experiment/routines/pavlovia_survey/dark/survey@2x.png +0 -0
  1811. psychopy/experiment/routines/pavlovia_survey/light/survey.png +0 -0
  1812. psychopy/experiment/routines/pavlovia_survey/light/survey@2x.png +0 -0
  1813. psychopy/experiment/routines/unknown/__init__.py +30 -0
  1814. psychopy/experiment/routines/unknown/classic/eyetracker_record.png +0 -0
  1815. psychopy/experiment/routines/unknown/classic/unknown.png +0 -0
  1816. psychopy/experiment/routines/unknown/dark/eyetracker_record.png +0 -0
  1817. psychopy/experiment/routines/unknown/dark/eyetracker_record@2x.png +0 -0
  1818. psychopy/experiment/routines/unknown/dark/unknown.png +0 -0
  1819. psychopy/experiment/routines/unknown/dark/unknown@2x.png +0 -0
  1820. psychopy/experiment/routines/unknown/light/eyetracker_record.png +0 -0
  1821. psychopy/experiment/routines/unknown/light/eyetracker_record@2x.png +0 -0
  1822. psychopy/experiment/routines/unknown/light/unknown.png +0 -0
  1823. psychopy/experiment/routines/unknown/light/unknown@2x.png +0 -0
  1824. psychopy/experiment/routines/utils.py +0 -0
  1825. psychopy/experiment/routines/visualValidator/__init__.py +395 -0
  1826. psychopy/experiment/routines/visualValidator/classic/visual_validator.png +0 -0
  1827. psychopy/experiment/routines/visualValidator/classic/visual_validator@2x.png +0 -0
  1828. psychopy/experiment/routines/visualValidator/dark/visual_validator.png +0 -0
  1829. psychopy/experiment/routines/visualValidator/dark/visual_validator@2x.png +0 -0
  1830. psychopy/experiment/routines/visualValidator/light/visual_validator.png +0 -0
  1831. psychopy/experiment/routines/visualValidator/light/visual_validator@2x.png +0 -0
  1832. psychopy/experiment/staircaseTemplate.xltx +0 -0
  1833. psychopy/experiment/utils.py +44 -0
  1834. psychopy/filters.py +11 -0
  1835. psychopy/gamma.py +9 -0
  1836. psychopy/gui/__init__.py +47 -0
  1837. psychopy/gui/qtgui.py +882 -0
  1838. psychopy/gui/util.py +74 -0
  1839. psychopy/gui/wxgui.py +420 -0
  1840. psychopy/hardware/__init__.py +178 -0
  1841. psychopy/hardware/base.py +466 -0
  1842. psychopy/hardware/bbtk/__init__.py +24 -0
  1843. psychopy/hardware/brainproducts.py +25 -0
  1844. psychopy/hardware/button.py +238 -0
  1845. psychopy/hardware/buttonbox/__init__.py +122 -0
  1846. psychopy/hardware/camera/__init__.py +3657 -0
  1847. psychopy/hardware/cedrus.py +24 -0
  1848. psychopy/hardware/crs/__init__.py +46 -0
  1849. psychopy/hardware/crs/bits.py +37 -0
  1850. psychopy/hardware/crs/colorcal.py +18 -0
  1851. psychopy/hardware/crs/optical.py +33 -0
  1852. psychopy/hardware/crs/shaders.py +34 -0
  1853. psychopy/hardware/emotiv.py +33 -0
  1854. psychopy/hardware/emulator.py +46 -0
  1855. psychopy/hardware/exceptions.py +86 -0
  1856. psychopy/hardware/eyetracker.py +132 -0
  1857. psychopy/hardware/forp.py +37 -0
  1858. psychopy/hardware/gammasci.py +26 -0
  1859. psychopy/hardware/iolab.py +24 -0
  1860. psychopy/hardware/joystick/__init__.py +1196 -0
  1861. psychopy/hardware/joystick/_base.py +251 -0
  1862. psychopy/hardware/joystick/backend_glfw.py +306 -0
  1863. psychopy/hardware/joystick/backend_pyglet.py +309 -0
  1864. psychopy/hardware/joystick/mappings.py +287 -0
  1865. psychopy/hardware/keyboard.py +975 -0
  1866. psychopy/hardware/knownDevices.json +52 -0
  1867. psychopy/hardware/labhackers.py +27 -0
  1868. psychopy/hardware/labjacks.py +26 -0
  1869. psychopy/hardware/lightsensor.py +789 -0
  1870. psychopy/hardware/listener.py +330 -0
  1871. psychopy/hardware/manager.py +992 -0
  1872. psychopy/hardware/microphone.py +1630 -0
  1873. psychopy/hardware/minolta.py +33 -0
  1874. psychopy/hardware/monitor.py +144 -0
  1875. psychopy/hardware/mouse/__init__.py +885 -0
  1876. psychopy/hardware/photometer/__init__.py +207 -0
  1877. psychopy/hardware/pr.py +33 -0
  1878. psychopy/hardware/qmix.py +32 -0
  1879. psychopy/hardware/serialdevice.py +387 -0
  1880. psychopy/hardware/soundsensor.py +476 -0
  1881. psychopy/hardware/spatial/__init__.py +231 -0
  1882. psychopy/hardware/speaker.py +351 -0
  1883. psychopy/hardware/triggerbox/__init__.py +56 -0
  1884. psychopy/hardware/triggerbox/base.py +170 -0
  1885. psychopy/hardware/triggerbox/parallel.py +362 -0
  1886. psychopy/info.py +719 -0
  1887. psychopy/iohub/__init__.py +50 -0
  1888. psychopy/iohub/client/__init__.py +1481 -0
  1889. psychopy/iohub/client/connect.py +260 -0
  1890. psychopy/iohub/client/eyetracker/__init__.py +4 -0
  1891. psychopy/iohub/client/eyetracker/validation/__init__.py +8 -0
  1892. psychopy/iohub/client/eyetracker/validation/posgrid.py +274 -0
  1893. psychopy/iohub/client/eyetracker/validation/procedure.py +1292 -0
  1894. psychopy/iohub/client/eyetracker/validation/trigger.py +240 -0
  1895. psychopy/iohub/client/keyboard.py +513 -0
  1896. psychopy/iohub/client/wintab.py +378 -0
  1897. psychopy/iohub/constants.py +1176 -0
  1898. psychopy/iohub/datastore/__init__.py +439 -0
  1899. psychopy/iohub/datastore/default_datastore.yaml +8 -0
  1900. psychopy/iohub/datastore/util.py +868 -0
  1901. psychopy/iohub/default_config.yaml +17 -0
  1902. psychopy/iohub/devices/__init__.py +1047 -0
  1903. psychopy/iohub/devices/computer.py +607 -0
  1904. psychopy/iohub/devices/default_device.yaml +15 -0
  1905. psychopy/iohub/devices/deviceConfigValidation.py +519 -0
  1906. psychopy/iohub/devices/display/__init__.py +755 -0
  1907. psychopy/iohub/devices/display/default_display.yaml +118 -0
  1908. psychopy/iohub/devices/display/supported_config_settings.yaml +69 -0
  1909. psychopy/iohub/devices/eventfilters.py +3572 -0
  1910. psychopy/iohub/devices/experiment/__init__.py +316 -0
  1911. psychopy/iohub/devices/experiment/default_experiment.yaml +74 -0
  1912. psychopy/iohub/devices/experiment/supported_config_settings.yaml +26 -0
  1913. psychopy/iohub/devices/eyetracker/__init__.py +474 -0
  1914. psychopy/iohub/devices/eyetracker/calibration/__init__.py +1 -0
  1915. psychopy/iohub/devices/eyetracker/calibration/procedure.py +391 -0
  1916. psychopy/iohub/devices/eyetracker/default_eyetracker.yaml +18 -0
  1917. psychopy/iohub/devices/eyetracker/eye_events.py +1751 -0
  1918. psychopy/iohub/devices/eyetracker/filters/__init__.py +4 -0
  1919. psychopy/iohub/devices/eyetracker/filters/parser.py +913 -0
  1920. psychopy/iohub/devices/eyetracker/hw/__init__.py +4 -0
  1921. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +29 -0
  1922. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +29 -0
  1923. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/calibration.py +18 -0
  1924. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +31 -0
  1925. psychopy/iohub/devices/eyetracker/hw/mouse/__init__.py +8 -0
  1926. psychopy/iohub/devices/eyetracker/hw/mouse/calibration.py +10 -0
  1927. psychopy/iohub/devices/eyetracker/hw/mouse/default_eyetracker.yaml +102 -0
  1928. psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +443 -0
  1929. psychopy/iohub/devices/eyetracker/hw/mouse/supported_config_settings.yaml +110 -0
  1930. psychopy/iohub/devices/eyetracker/hw/pupil_labs/__init__.py +4 -0
  1931. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +27 -0
  1932. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/eyetracker.py +18 -0
  1933. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/__init__.py +29 -0
  1934. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/bisector.py +20 -0
  1935. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/constants.py +19 -0
  1936. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/data_parse.py +22 -0
  1937. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/eyetracker.py +18 -0
  1938. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/pupil_remote.py +18 -0
  1939. psychopy/iohub/devices/eyetracker/hw/sr_research/__init__.py +4 -0
  1940. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/__init__.py +28 -0
  1941. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/calibration.py +22 -0
  1942. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/eyetracker.py +38 -0
  1943. psychopy/iohub/devices/eyetracker/hw/tobii/__init__.py +29 -0
  1944. psychopy/iohub/devices/eyetracker/hw/tobii/calibration.py +19 -0
  1945. psychopy/iohub/devices/eyetracker/hw/tobii/eyetracker.py +17 -0
  1946. psychopy/iohub/devices/eyetracker/hw/tobii/tobiiwrapper.py +19 -0
  1947. psychopy/iohub/devices/eyetracker/supported_config_settings.yaml +44 -0
  1948. psychopy/iohub/devices/keyboard/__init__.py +309 -0
  1949. psychopy/iohub/devices/keyboard/darwin.py +431 -0
  1950. psychopy/iohub/devices/keyboard/darwinkey.py +94 -0
  1951. psychopy/iohub/devices/keyboard/default_keyboard.yaml +94 -0
  1952. psychopy/iohub/devices/keyboard/linux2.py +102 -0
  1953. psychopy/iohub/devices/keyboard/supported_config_settings.yaml +39 -0
  1954. psychopy/iohub/devices/keyboard/win32.py +356 -0
  1955. psychopy/iohub/devices/mouse/__init__.py +709 -0
  1956. psychopy/iohub/devices/mouse/darwin.py +414 -0
  1957. psychopy/iohub/devices/mouse/default_mouse.yaml +92 -0
  1958. psychopy/iohub/devices/mouse/linux2.py +172 -0
  1959. psychopy/iohub/devices/mouse/supported_config_settings.yaml +39 -0
  1960. psychopy/iohub/devices/mouse/win32.py +284 -0
  1961. psychopy/iohub/devices/pyXHook.py +619 -0
  1962. psychopy/iohub/devices/serial/__init__.py +779 -0
  1963. psychopy/iohub/devices/serial/default_pstbox.yaml +108 -0
  1964. psychopy/iohub/devices/serial/default_serial.yaml +108 -0
  1965. psychopy/iohub/devices/serial/supported_config_settings.yaml +85 -0
  1966. psychopy/iohub/devices/serial/supported_config_settings_pstbox.yaml +85 -0
  1967. psychopy/iohub/devices/supported_config_settings.yaml +36 -0
  1968. psychopy/iohub/devices/wintab/__init__.py +580 -0
  1969. psychopy/iohub/devices/wintab/default_wintab.yaml +89 -0
  1970. psychopy/iohub/devices/wintab/supported_config_settings.yaml +42 -0
  1971. psychopy/iohub/devices/wintab/win32.py +722 -0
  1972. psychopy/iohub/devices/xlib.py +6795 -0
  1973. psychopy/iohub/errors.py +61 -0
  1974. psychopy/iohub/lazy_import.py +582 -0
  1975. psychopy/iohub/net.py +349 -0
  1976. psychopy/iohub/server.py +1075 -0
  1977. psychopy/iohub/start_iohub_process.py +121 -0
  1978. psychopy/iohub/util/__init__.py +832 -0
  1979. psychopy/iohub/util/visualangle.py +126 -0
  1980. psychopy/layout.py +916 -0
  1981. psychopy/liaison.py +471 -0
  1982. psychopy/locale_setup.py +41 -0
  1983. psychopy/localization/__init__.py +22 -0
  1984. psychopy/localization/_localization.py +155 -0
  1985. psychopy/localization/generateTranslationTemplate.py +257 -0
  1986. psychopy/localization/mappings.txt +170 -0
  1987. psychopy/localization/messages.pot +9950 -0
  1988. psychopy/localization/readme.txt +68 -0
  1989. psychopy/logging.py +415 -0
  1990. psychopy/microphone.py +28 -0
  1991. psychopy/misc.py +40 -0
  1992. psychopy/monitors/MonitorCenter.py +1354 -0
  1993. psychopy/monitors/__init__.py +30 -0
  1994. psychopy/monitors/calibData.py +78 -0
  1995. psychopy/monitors/calibTools.py +1301 -0
  1996. psychopy/monitors/getLumSeries.py +50 -0
  1997. psychopy/monitors/psychopy-icon.svg +395 -0
  1998. psychopy/monitors/psychopy.ico +0 -0
  1999. psychopy/parallel/__init__.py +206 -0
  2000. psychopy/parallel/_dlportio.py +150 -0
  2001. psychopy/parallel/_inpout.py +120 -0
  2002. psychopy/parallel/_linux.py +105 -0
  2003. psychopy/piloting.py +61 -0
  2004. psychopy/platform_specific/__init__.py +46 -0
  2005. psychopy/platform_specific/darwin.py +326 -0
  2006. psychopy/platform_specific/linux.py +77 -0
  2007. psychopy/platform_specific/win32.py +124 -0
  2008. psychopy/plugins/__init__.py +1397 -0
  2009. psychopy/plugins/util.py +104 -0
  2010. psychopy/preferences/Darwin.spec +294 -0
  2011. psychopy/preferences/FreeBSD.spec +294 -0
  2012. psychopy/preferences/Linux.spec +294 -0
  2013. psychopy/preferences/Windows.spec +294 -0
  2014. psychopy/preferences/__init__.py +20 -0
  2015. psychopy/preferences/baseNoArch.spec +290 -0
  2016. psychopy/preferences/devices.py +80 -0
  2017. psychopy/preferences/generateHints.py +93 -0
  2018. psychopy/preferences/generateSpec.py +80 -0
  2019. psychopy/preferences/hints.py +367 -0
  2020. psychopy/preferences/preferences.py +448 -0
  2021. psychopy/projects/__init__.py +11 -0
  2022. psychopy/projects/gitignore.py +45 -0
  2023. psychopy/projects/pavlovia.py +1674 -0
  2024. psychopy/projects/sshkeys.py +107 -0
  2025. psychopy/psychojs.zip +0 -0
  2026. psychopy/pylintrc +69 -0
  2027. psychopy/scripts/__init__.py +0 -0
  2028. psychopy/scripts/psychopy-pkgutil.py +969 -0
  2029. psychopy/scripts/psyexpCompile.py +229 -0
  2030. psychopy/session.py +1651 -0
  2031. psychopy/sound/__init__.py +50 -0
  2032. psychopy/sound/_base.py +399 -0
  2033. psychopy/sound/audioclip.py +1325 -0
  2034. psychopy/sound/audiodevice.py +857 -0
  2035. psychopy/sound/backend_ptb.py +406 -0
  2036. psychopy/sound/backend_pygame.py +323 -0
  2037. psychopy/sound/backend_pyo.py +64 -0
  2038. psychopy/sound/backend_pysound.py +324 -0
  2039. psychopy/sound/backend_sounddevice.py +44 -0
  2040. psychopy/sound/backends/__init__.py +0 -0
  2041. psychopy/sound/exceptions.py +115 -0
  2042. psychopy/sound/microphone.py +357 -0
  2043. psychopy/sound/sound.py +58 -0
  2044. psychopy/sound/transcribe.py +1321 -0
  2045. psychopy/tests/README.md +344 -0
  2046. psychopy/tests/__init__.py +47 -0
  2047. psychopy/tests/data/Electronic_Chime-KevanGC-495939803.wav +0 -0
  2048. psychopy/tests/data/TestCircle_w128h128_bottom_left.png +0 -0
  2049. psychopy/tests/data/TestCircle_w128h128_bottom_right.png +0 -0
  2050. psychopy/tests/data/TestCircle_w128h128_center_center.png +0 -0
  2051. psychopy/tests/data/TestCircle_w128h128_top_left.png +0 -0
  2052. psychopy/tests/data/TestCircle_w128h128_top_right.png +0 -0
  2053. psychopy/tests/data/TestCircle_w128h64_bottom_left.png +0 -0
  2054. psychopy/tests/data/TestCircle_w128h64_bottom_right.png +0 -0
  2055. psychopy/tests/data/TestCircle_w128h64_center_center.png +0 -0
  2056. psychopy/tests/data/TestCircle_w128h64_top_left.png +0 -0
  2057. psychopy/tests/data/TestCircle_w128h64_top_right.png +0 -0
  2058. psychopy/tests/data/TestCircle_w64h128_bottom_left.png +0 -0
  2059. psychopy/tests/data/TestCircle_w64h128_bottom_right.png +0 -0
  2060. psychopy/tests/data/TestCircle_w64h128_center_center.png +0 -0
  2061. psychopy/tests/data/TestCircle_w64h128_top_left.png +0 -0
  2062. psychopy/tests/data/TestCircle_w64h128_top_right.png +0 -0
  2063. psychopy/tests/data/TestCircle_w64h64_bottom_left.png +0 -0
  2064. psychopy/tests/data/TestCircle_w64h64_bottom_right.png +0 -0
  2065. psychopy/tests/data/TestCircle_w64h64_center_center.png +0 -0
  2066. psychopy/tests/data/TestCircle_w64h64_top_left.png +0 -0
  2067. psychopy/tests/data/TestCircle_w64h64_top_right.png +0 -0
  2068. psychopy/tests/data/TestForm_scrolling_nq10_s0.5.png +0 -0
  2069. psychopy/tests/data/TestForm_scrolling_nq10_s0.png +0 -0
  2070. psychopy/tests/data/TestForm_scrolling_nq10_s1.png +0 -0
  2071. psychopy/tests/data/TestForm_scrolling_nq1_s0.5.png +0 -0
  2072. psychopy/tests/data/TestForm_scrolling_nq1_s0.png +0 -0
  2073. psychopy/tests/data/TestForm_scrolling_nq1_s1.png +0 -0
  2074. psychopy/tests/data/TestForm_scrolling_nq3_s0.5.png +0 -0
  2075. psychopy/tests/data/TestForm_scrolling_nq3_s0.png +0 -0
  2076. psychopy/tests/data/TestForm_scrolling_nq3_s1.png +0 -0
  2077. psychopy/tests/data/TestImage_w128h128_bottom_left.png +0 -0
  2078. psychopy/tests/data/TestImage_w128h128_bottom_right.png +0 -0
  2079. psychopy/tests/data/TestImage_w128h128_center_center.png +0 -0
  2080. psychopy/tests/data/TestImage_w128h128_top_left.png +0 -0
  2081. psychopy/tests/data/TestImage_w128h128_top_right.png +0 -0
  2082. psychopy/tests/data/TestImage_w128h64_bottom_left.png +0 -0
  2083. psychopy/tests/data/TestImage_w128h64_bottom_right.png +0 -0
  2084. psychopy/tests/data/TestImage_w128h64_center_center.png +0 -0
  2085. psychopy/tests/data/TestImage_w128h64_top_left.png +0 -0
  2086. psychopy/tests/data/TestImage_w128h64_top_right.png +0 -0
  2087. psychopy/tests/data/TestImage_w64h128_bottom_left.png +0 -0
  2088. psychopy/tests/data/TestImage_w64h128_bottom_right.png +0 -0
  2089. psychopy/tests/data/TestImage_w64h128_center_center.png +0 -0
  2090. psychopy/tests/data/TestImage_w64h128_top_left.png +0 -0
  2091. psychopy/tests/data/TestImage_w64h128_top_right.png +0 -0
  2092. psychopy/tests/data/TestImage_w64h64_bottom_left.png +0 -0
  2093. psychopy/tests/data/TestImage_w64h64_bottom_right.png +0 -0
  2094. psychopy/tests/data/TestImage_w64h64_center_center.png +0 -0
  2095. psychopy/tests/data/TestImage_w64h64_top_left.png +0 -0
  2096. psychopy/tests/data/TestImage_w64h64_top_right.png +0 -0
  2097. psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.3.png +0 -0
  2098. psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.6.png +0 -0
  2099. psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.3.png +0 -0
  2100. psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.6.png +0 -0
  2101. psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.3.png +0 -0
  2102. psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.6.png +0 -0
  2103. psychopy/tests/data/TestProgress_testValue_minmax_0.png +0 -0
  2104. psychopy/tests/data/TestProgress_testValue_minmax_1.png +0 -0
  2105. psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.3.png +0 -0
  2106. psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.6.png +0 -0
  2107. psychopy/tests/data/TestProgress_testValue_vertical_center center_0.3.png +0 -0
  2108. psychopy/tests/data/TestProgress_testValue_vertical_center center_0.6.png +0 -0
  2109. psychopy/tests/data/TestProgress_testValue_vertical_top center_0.3.png +0 -0
  2110. psychopy/tests/data/TestProgress_testValue_vertical_top center_0.6.png +0 -0
  2111. psychopy/tests/data/TestProgress_w128h128_bottom_left.png +0 -0
  2112. psychopy/tests/data/TestProgress_w128h128_bottom_right.png +0 -0
  2113. psychopy/tests/data/TestProgress_w128h128_center_center.png +0 -0
  2114. psychopy/tests/data/TestProgress_w128h128_top_left.png +0 -0
  2115. psychopy/tests/data/TestProgress_w128h128_top_right.png +0 -0
  2116. psychopy/tests/data/TestProgress_w128h64_bottom_left.png +0 -0
  2117. psychopy/tests/data/TestProgress_w128h64_bottom_right.png +0 -0
  2118. psychopy/tests/data/TestProgress_w128h64_center_center.png +0 -0
  2119. psychopy/tests/data/TestProgress_w128h64_top_left.png +0 -0
  2120. psychopy/tests/data/TestProgress_w128h64_top_right.png +0 -0
  2121. psychopy/tests/data/TestProgress_w64h128_bottom_left.png +0 -0
  2122. psychopy/tests/data/TestProgress_w64h128_bottom_right.png +0 -0
  2123. psychopy/tests/data/TestProgress_w64h128_center_center.png +0 -0
  2124. psychopy/tests/data/TestProgress_w64h128_top_left.png +0 -0
  2125. psychopy/tests/data/TestProgress_w64h128_top_right.png +0 -0
  2126. psychopy/tests/data/TestProgress_w64h64_bottom_left.png +0 -0
  2127. psychopy/tests/data/TestProgress_w64h64_bottom_right.png +0 -0
  2128. psychopy/tests/data/TestProgress_w64h64_center_center.png +0 -0
  2129. psychopy/tests/data/TestProgress_w64h64_top_left.png +0 -0
  2130. psychopy/tests/data/TestProgress_w64h64_top_right.png +0 -0
  2131. psychopy/tests/data/TestROI_w128h128_bottom_left.png +0 -0
  2132. psychopy/tests/data/TestROI_w128h128_bottom_right.png +0 -0
  2133. psychopy/tests/data/TestROI_w128h128_center_center.png +0 -0
  2134. psychopy/tests/data/TestROI_w128h128_top_left.png +0 -0
  2135. psychopy/tests/data/TestROI_w128h128_top_right.png +0 -0
  2136. psychopy/tests/data/TestROI_w128h64_bottom_left.png +0 -0
  2137. psychopy/tests/data/TestROI_w128h64_bottom_right.png +0 -0
  2138. psychopy/tests/data/TestROI_w128h64_center_center.png +0 -0
  2139. psychopy/tests/data/TestROI_w128h64_top_left.png +0 -0
  2140. psychopy/tests/data/TestROI_w128h64_top_right.png +0 -0
  2141. psychopy/tests/data/TestROI_w64h128_bottom_left.png +0 -0
  2142. psychopy/tests/data/TestROI_w64h128_bottom_right.png +0 -0
  2143. psychopy/tests/data/TestROI_w64h128_center_center.png +0 -0
  2144. psychopy/tests/data/TestROI_w64h128_top_left.png +0 -0
  2145. psychopy/tests/data/TestROI_w64h128_top_right.png +0 -0
  2146. psychopy/tests/data/TestROI_w64h64_bottom_left.png +0 -0
  2147. psychopy/tests/data/TestROI_w64h64_bottom_right.png +0 -0
  2148. psychopy/tests/data/TestROI_w64h64_center_center.png +0 -0
  2149. psychopy/tests/data/TestROI_w64h64_top_left.png +0 -0
  2150. psychopy/tests/data/TestROI_w64h64_top_right.png +0 -0
  2151. psychopy/tests/data/TestSession_exp1.png +0 -0
  2152. psychopy/tests/data/TestSession_exp2.png +0 -0
  2153. psychopy/tests/data/TestShape_w128h128_bottom_left.png +0 -0
  2154. psychopy/tests/data/TestShape_w128h128_bottom_right.png +0 -0
  2155. psychopy/tests/data/TestShape_w128h128_center_center.png +0 -0
  2156. psychopy/tests/data/TestShape_w128h128_top_left.png +0 -0
  2157. psychopy/tests/data/TestShape_w128h128_top_right.png +0 -0
  2158. psychopy/tests/data/TestShape_w128h64_bottom_left.png +0 -0
  2159. psychopy/tests/data/TestShape_w128h64_bottom_right.png +0 -0
  2160. psychopy/tests/data/TestShape_w128h64_center_center.png +0 -0
  2161. psychopy/tests/data/TestShape_w128h64_top_left.png +0 -0
  2162. psychopy/tests/data/TestShape_w128h64_top_right.png +0 -0
  2163. psychopy/tests/data/TestShape_w64h128_bottom_left.png +0 -0
  2164. psychopy/tests/data/TestShape_w64h128_bottom_right.png +0 -0
  2165. psychopy/tests/data/TestShape_w64h128_center_center.png +0 -0
  2166. psychopy/tests/data/TestShape_w64h128_top_left.png +0 -0
  2167. psychopy/tests/data/TestShape_w64h128_top_right.png +0 -0
  2168. psychopy/tests/data/TestShape_w64h64_bottom_left.png +0 -0
  2169. psychopy/tests/data/TestShape_w64h64_bottom_right.png +0 -0
  2170. psychopy/tests/data/TestShape_w64h64_center_center.png +0 -0
  2171. psychopy/tests/data/TestShape_w64h64_top_left.png +0 -0
  2172. psychopy/tests/data/TestShape_w64h64_top_right.png +0 -0
  2173. psychopy/tests/data/TestTarget_w128h128_bottom_left.png +0 -0
  2174. psychopy/tests/data/TestTarget_w128h128_bottom_right.png +0 -0
  2175. psychopy/tests/data/TestTarget_w128h128_center_center.png +0 -0
  2176. psychopy/tests/data/TestTarget_w128h128_top_left.png +0 -0
  2177. psychopy/tests/data/TestTarget_w128h128_top_right.png +0 -0
  2178. psychopy/tests/data/TestTarget_w128h64_bottom_left.png +0 -0
  2179. psychopy/tests/data/TestTarget_w128h64_bottom_right.png +0 -0
  2180. psychopy/tests/data/TestTarget_w128h64_center_center.png +0 -0
  2181. psychopy/tests/data/TestTarget_w128h64_top_left.png +0 -0
  2182. psychopy/tests/data/TestTarget_w128h64_top_right.png +0 -0
  2183. psychopy/tests/data/TestTarget_w64h128_bottom_left.png +0 -0
  2184. psychopy/tests/data/TestTarget_w64h128_bottom_right.png +0 -0
  2185. psychopy/tests/data/TestTarget_w64h128_center_center.png +0 -0
  2186. psychopy/tests/data/TestTarget_w64h128_top_left.png +0 -0
  2187. psychopy/tests/data/TestTarget_w64h128_top_right.png +0 -0
  2188. psychopy/tests/data/TestTarget_w64h64_bottom_left.png +0 -0
  2189. psychopy/tests/data/TestTarget_w64h64_bottom_right.png +0 -0
  2190. psychopy/tests/data/TestTarget_w64h64_center_center.png +0 -0
  2191. psychopy/tests/data/TestTarget_w64h64_top_left.png +0 -0
  2192. psychopy/tests/data/TestTarget_w64h64_top_right.png +0 -0
  2193. psychopy/tests/data/TestTextbox_testLetterSpacing_0p6.png +0 -0
  2194. psychopy/tests/data/TestTextbox_testLetterSpacing_0p8.png +0 -0
  2195. psychopy/tests/data/TestTextbox_testLetterSpacing_1.png +0 -0
  2196. psychopy/tests/data/TestTextbox_testLetterSpacing_1p2.png +0 -0
  2197. psychopy/tests/data/TestTextbox_testLetterSpacing_1p4.png +0 -0
  2198. psychopy/tests/data/TestTextbox_testLetterSpacing_1p6.png +0 -0
  2199. psychopy/tests/data/TestTextbox_testLetterSpacing_1p8.png +0 -0
  2200. psychopy/tests/data/TestTextbox_testLetterSpacing_2p0.png +0 -0
  2201. psychopy/tests/data/TestTextbox_testLetterSpacing_None.png +0 -0
  2202. psychopy/tests/data/TestUnknownPluginComponent_load_resave.psyexp +135 -0
  2203. psychopy/tests/data/Test_textbox/test_ori_0_bottom right.png +0 -0
  2204. psychopy/tests/data/Test_textbox/test_ori_0_center.png +0 -0
  2205. psychopy/tests/data/Test_textbox/test_ori_0_top left.png +0 -0
  2206. psychopy/tests/data/Test_textbox/test_ori_120_bottom right.png +0 -0
  2207. psychopy/tests/data/Test_textbox/test_ori_120_center.png +0 -0
  2208. psychopy/tests/data/Test_textbox/test_ori_120_top left.png +0 -0
  2209. psychopy/tests/data/Test_textbox/test_ori_180_bottom right.png +0 -0
  2210. psychopy/tests/data/Test_textbox/test_ori_180_center.png +0 -0
  2211. psychopy/tests/data/Test_textbox/test_ori_180_top left.png +0 -0
  2212. psychopy/tests/data/Test_textbox/test_ori_240_bottom right.png +0 -0
  2213. psychopy/tests/data/Test_textbox/test_ori_240_center.png +0 -0
  2214. psychopy/tests/data/Test_textbox/test_ori_240_top left.png +0 -0
  2215. psychopy/tests/data/Test_textbox_testSpeechpoint_-3ovr8_0ovr8.png +0 -0
  2216. psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_-3ovr8.png +0 -0
  2217. psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_3ovr8.png +0 -0
  2218. psychopy/tests/data/Test_textbox_testSpeechpoint_3ovr8_0ovr8.png +0 -0
  2219. psychopy/tests/data/Test_textbox_w128h128_bottom_left.png +0 -0
  2220. psychopy/tests/data/Test_textbox_w128h128_bottom_right.png +0 -0
  2221. psychopy/tests/data/Test_textbox_w128h128_center_center.png +0 -0
  2222. psychopy/tests/data/Test_textbox_w128h128_top_left.png +0 -0
  2223. psychopy/tests/data/Test_textbox_w128h128_top_right.png +0 -0
  2224. psychopy/tests/data/Test_textbox_w128h64_bottom_left.png +0 -0
  2225. psychopy/tests/data/Test_textbox_w128h64_bottom_right.png +0 -0
  2226. psychopy/tests/data/Test_textbox_w128h64_center_center.png +0 -0
  2227. psychopy/tests/data/Test_textbox_w128h64_top_left.png +0 -0
  2228. psychopy/tests/data/Test_textbox_w128h64_top_right.png +0 -0
  2229. psychopy/tests/data/Test_textbox_w64h128_bottom_left.png +0 -0
  2230. psychopy/tests/data/Test_textbox_w64h128_bottom_right.png +0 -0
  2231. psychopy/tests/data/Test_textbox_w64h128_center_center.png +0 -0
  2232. psychopy/tests/data/Test_textbox_w64h128_top_left.png +0 -0
  2233. psychopy/tests/data/Test_textbox_w64h128_top_right.png +0 -0
  2234. psychopy/tests/data/Test_textbox_w64h64_bottom_left.png +0 -0
  2235. psychopy/tests/data/Test_textbox_w64h64_bottom_right.png +0 -0
  2236. psychopy/tests/data/Test_textbox_w64h64_center_center.png +0 -0
  2237. psychopy/tests/data/Test_textbox_w64h64_top_left.png +0 -0
  2238. psychopy/tests/data/Test_textbox_w64h64_top_right.png +0 -0
  2239. psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_left.png +0 -0
  2240. psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_right.png +0 -0
  2241. psychopy/tests/data/Test_uax14_textbox_w128h128_center_center.png +0 -0
  2242. psychopy/tests/data/Test_uax14_textbox_w128h128_top_left.png +0 -0
  2243. psychopy/tests/data/Test_uax14_textbox_w128h128_top_right.png +0 -0
  2244. psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_left.png +0 -0
  2245. psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_right.png +0 -0
  2246. psychopy/tests/data/Test_uax14_textbox_w128h64_center_center.png +0 -0
  2247. psychopy/tests/data/Test_uax14_textbox_w128h64_top_left.png +0 -0
  2248. psychopy/tests/data/Test_uax14_textbox_w128h64_top_right.png +0 -0
  2249. psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_left.png +0 -0
  2250. psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_right.png +0 -0
  2251. psychopy/tests/data/Test_uax14_textbox_w64h128_center_center.png +0 -0
  2252. psychopy/tests/data/Test_uax14_textbox_w64h128_top_left.png +0 -0
  2253. psychopy/tests/data/Test_uax14_textbox_w64h128_top_right.png +0 -0
  2254. psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_left.png +0 -0
  2255. psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_right.png +0 -0
  2256. psychopy/tests/data/Test_uax14_textbox_w64h64_center_center.png +0 -0
  2257. psychopy/tests/data/Test_uax14_textbox_w64h64_top_left.png +0 -0
  2258. psychopy/tests/data/Test_uax14_textbox_w64h64_top_right.png +0 -0
  2259. psychopy/tests/data/TextComponent_disabled.psyexp +63 -0
  2260. psychopy/tests/data/TextComponent_not_disabled.psyexp +63 -0
  2261. psychopy/tests/data/aperture1_deg.png +0 -0
  2262. psychopy/tests/data/aperture1_degFlat.png +0 -0
  2263. psychopy/tests/data/aperture1_degFlatPos.png +0 -0
  2264. psychopy/tests/data/aperture1_norm.png +0 -0
  2265. psychopy/tests/data/aperture1_normHexbackground.png +0 -0
  2266. psychopy/tests/data/aperture1_normNoShade.png +0 -0
  2267. psychopy/tests/data/aperture1_pix.png +0 -0
  2268. psychopy/tests/data/aperture1_stencil.png +0 -0
  2269. psychopy/tests/data/aperture2_deg.png +0 -0
  2270. psychopy/tests/data/aperture2_degFlat.png +0 -0
  2271. psychopy/tests/data/aperture2_degFlatPos.png +0 -0
  2272. psychopy/tests/data/aperture2_norm.png +0 -0
  2273. psychopy/tests/data/aperture2_normHexbackground.png +0 -0
  2274. psychopy/tests/data/aperture2_normNoShade.png +0 -0
  2275. psychopy/tests/data/aperture2_pix.png +0 -0
  2276. psychopy/tests/data/aperture2_stencil.png +0 -0
  2277. psychopy/tests/data/beatandrcos_cm.png +0 -0
  2278. psychopy/tests/data/beatandrcos_deg.png +0 -0
  2279. psychopy/tests/data/beatandrcos_degFlat.png +0 -0
  2280. psychopy/tests/data/beatandrcos_degFlatPos.png +0 -0
  2281. psychopy/tests/data/beatandrcos_height.png +0 -0
  2282. psychopy/tests/data/beatandrcos_norm.png +0 -0
  2283. psychopy/tests/data/beatandrcos_normAddBlend.png +0 -0
  2284. psychopy/tests/data/beatandrcos_normHexbackground.png +0 -0
  2285. psychopy/tests/data/beatandrcos_pix.png +0 -0
  2286. psychopy/tests/data/beatandrcos_stencil.png +0 -0
  2287. psychopy/tests/data/blend_add_cm.png +0 -0
  2288. psychopy/tests/data/blend_add_deg.png +0 -0
  2289. psychopy/tests/data/blend_add_degFlat.png +0 -0
  2290. psychopy/tests/data/blend_add_degFlatPos.png +0 -0
  2291. psychopy/tests/data/blend_add_height.png +0 -0
  2292. psychopy/tests/data/blend_add_norm.png +0 -0
  2293. psychopy/tests/data/blend_add_normAddBlend.png +0 -0
  2294. psychopy/tests/data/blend_add_normHexbackground.png +0 -0
  2295. psychopy/tests/data/blend_add_normNoShade.png +0 -0
  2296. psychopy/tests/data/blend_add_pix.png +0 -0
  2297. psychopy/tests/data/blend_add_stencil.png +0 -0
  2298. psychopy/tests/data/broken2020_2_5_resources/broken_resources.psyexp +84 -0
  2299. psychopy/tests/data/broken2020_2_5_resources/psychopy72.png +0 -0
  2300. psychopy/tests/data/bufferimg_gabor_cm.png +0 -0
  2301. psychopy/tests/data/bufferimg_gabor_deg.png +0 -0
  2302. psychopy/tests/data/bufferimg_gabor_degFlat.png +0 -0
  2303. psychopy/tests/data/bufferimg_gabor_degFlatPos.png +0 -0
  2304. psychopy/tests/data/bufferimg_gabor_height.png +0 -0
  2305. psychopy/tests/data/bufferimg_gabor_norm.png +0 -0
  2306. psychopy/tests/data/bufferimg_gabor_normAddBlend.png +0 -0
  2307. psychopy/tests/data/bufferimg_gabor_normHexbackground.png +0 -0
  2308. psychopy/tests/data/bufferimg_gabor_normNoShade.png +0 -0
  2309. psychopy/tests/data/bufferimg_gabor_pix.png +0 -0
  2310. psychopy/tests/data/bufferimg_gabor_stencil.png +0 -0
  2311. psychopy/tests/data/circleHex_cm.png +0 -0
  2312. psychopy/tests/data/circleHex_deg.png +0 -0
  2313. psychopy/tests/data/circleHex_degFlat.png +0 -0
  2314. psychopy/tests/data/circleHex_degFlatPos.png +0 -0
  2315. psychopy/tests/data/circleHex_height.png +0 -0
  2316. psychopy/tests/data/circleHex_norm.png +0 -0
  2317. psychopy/tests/data/circleHex_normAddBlend.png +0 -0
  2318. psychopy/tests/data/circleHex_normHexbackground.png +0 -0
  2319. psychopy/tests/data/circleHex_normNoShade.png +0 -0
  2320. psychopy/tests/data/circleHex_pix.png +0 -0
  2321. psychopy/tests/data/circleHex_stencil.png +0 -0
  2322. psychopy/tests/data/corrFullRandom.csv +16 -0
  2323. psychopy/tests/data/corrFullRandom.tsv +6 -0
  2324. psychopy/tests/data/corrFullRandomTH2.csv +16 -0
  2325. psychopy/tests/data/corrMultiKeyExpWide.csv +3 -0
  2326. psychopy/tests/data/corrMultiKeyTrials.csv +8 -0
  2327. psychopy/tests/data/corrMultiKeyTrials.xlsx +0 -0
  2328. psychopy/tests/data/corrRandom.csv +16 -0
  2329. psychopy/tests/data/corrRandom.tsv +6 -0
  2330. psychopy/tests/data/corrRandomTH2.csv +16 -0
  2331. psychopy/tests/data/corrXlsx.xlsx +0 -0
  2332. psychopy/tests/data/correctScript/js/correctCodeComponent.js +181 -0
  2333. psychopy/tests/data/correctScript/js/correctImageComponent.js +204 -0
  2334. psychopy/tests/data/correctScript/js/correctKeyboardComponent.js +225 -0
  2335. psychopy/tests/data/correctScript/js/correctMouseComponent.js +221 -0
  2336. psychopy/tests/data/correctScript/js/correctMovieComponent.js +210 -0
  2337. psychopy/tests/data/correctScript/js/correctPolygonComponent.js +205 -0
  2338. psychopy/tests/data/correctScript/js/correctSliderComponent.js +209 -0
  2339. psychopy/tests/data/correctScript/js/correctSoundComponent.js +205 -0
  2340. psychopy/tests/data/correctScript/js/correctTextComponent.js +207 -0
  2341. psychopy/tests/data/correctScript/python/correctApertureComponent.py +157 -0
  2342. psychopy/tests/data/correctScript/python/correctCodeComponent.py +135 -0
  2343. psychopy/tests/data/correctScript/python/correctDotsComponent.py +161 -0
  2344. psychopy/tests/data/correctScript/python/correctEnvGratingComponent.py +167 -0
  2345. psychopy/tests/data/correctScript/python/correctFormComponent.py +150 -0
  2346. psychopy/tests/data/correctScript/python/correctGratingComponent.py +158 -0
  2347. psychopy/tests/data/correctScript/python/correctImageComponent.py +160 -0
  2348. psychopy/tests/data/correctScript/python/correctJoyButtonsComponent.py +204 -0
  2349. psychopy/tests/data/correctScript/python/correctJoystickComponent.py +214 -0
  2350. psychopy/tests/data/correctScript/python/correctKeyboardComponent.py +169 -0
  2351. psychopy/tests/data/correctScript/python/correctMicrophoneComponent.py +163 -0
  2352. psychopy/tests/data/correctScript/python/correctMouseComponent.py +174 -0
  2353. psychopy/tests/data/correctScript/python/correctMovieComponent.py +160 -0
  2354. psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +169 -0
  2355. psychopy/tests/data/correctScript/python/correctParallelOutComponent.py +156 -0
  2356. psychopy/tests/data/correctScript/python/correctPatchComponent.py +159 -0
  2357. psychopy/tests/data/correctScript/python/correctPolygonComponent.py +159 -0
  2358. psychopy/tests/data/correctScript/python/correctQmixPumpComponent.py +174 -0
  2359. psychopy/tests/data/correctScript/python/correctRatingScaleComponent.py +152 -0
  2360. psychopy/tests/data/correctScript/python/correctSliderComponent.py +159 -0
  2361. psychopy/tests/data/correctScript/python/correctSoundComponent.py +157 -0
  2362. psychopy/tests/data/correctScript/python/correctStaticComponent.py +176 -0
  2363. psychopy/tests/data/correctScript/python/correctTextComponent.py +159 -0
  2364. psychopy/tests/data/correctScript/python/correctVariableComponent.py +139 -0
  2365. psychopy/tests/data/correctScript/python/correctcedrusButtonBoxComponent.py +199 -0
  2366. psychopy/tests/data/correctScript/python/correctioLabsButtonBoxComponent.py +187 -0
  2367. psychopy/tests/data/dataTest.xlsx +0 -0
  2368. psychopy/tests/data/dots_cm.png +0 -0
  2369. psychopy/tests/data/dots_deg.png +0 -0
  2370. psychopy/tests/data/dots_degFlat.png +0 -0
  2371. psychopy/tests/data/dots_degFlatPos.png +0 -0
  2372. psychopy/tests/data/dots_height.png +0 -0
  2373. psychopy/tests/data/dots_norm.png +0 -0
  2374. psychopy/tests/data/dots_normAddBlend.png +0 -0
  2375. psychopy/tests/data/dots_normHexbackground.png +0 -0
  2376. psychopy/tests/data/dots_normNoShade.png +0 -0
  2377. psychopy/tests/data/dots_pix.png +0 -0
  2378. psychopy/tests/data/dots_stencil.png +0 -0
  2379. psychopy/tests/data/duplicateHeaders.csv +2 -0
  2380. psychopy/tests/data/elarray1_cm.png +0 -0
  2381. psychopy/tests/data/elarray1_deg.png +0 -0
  2382. psychopy/tests/data/elarray1_degFlat.png +0 -0
  2383. psychopy/tests/data/elarray1_degFlatPos.png +0 -0
  2384. psychopy/tests/data/elarray1_height.png +0 -0
  2385. psychopy/tests/data/elarray1_norm.png +0 -0
  2386. psychopy/tests/data/elarray1_normAddBlend.png +0 -0
  2387. psychopy/tests/data/elarray1_normHexbackground.png +0 -0
  2388. psychopy/tests/data/elarray1_pix.png +0 -0
  2389. psychopy/tests/data/elarray1_stencil.png +0 -0
  2390. psychopy/tests/data/envelopeandrcos_cm.png +0 -0
  2391. psychopy/tests/data/envelopeandrcos_deg.png +0 -0
  2392. psychopy/tests/data/envelopeandrcos_degFlat.png +0 -0
  2393. psychopy/tests/data/envelopeandrcos_degFlatPos.png +0 -0
  2394. psychopy/tests/data/envelopeandrcos_height.png +0 -0
  2395. psychopy/tests/data/envelopeandrcos_norm.png +0 -0
  2396. psychopy/tests/data/envelopeandrcos_normAddBlend.png +0 -0
  2397. psychopy/tests/data/envelopeandrcos_normHexbackground.png +0 -0
  2398. psychopy/tests/data/envelopeandrcos_pix.png +0 -0
  2399. psychopy/tests/data/envelopeandrcos_stencil.png +0 -0
  2400. psychopy/tests/data/envelopepowerandrcos_cm.png +0 -0
  2401. psychopy/tests/data/envelopepowerandrcos_deg.png +0 -0
  2402. psychopy/tests/data/envelopepowerandrcos_degFlat.png +0 -0
  2403. psychopy/tests/data/envelopepowerandrcos_degFlatPos.png +0 -0
  2404. psychopy/tests/data/envelopepowerandrcos_height.png +0 -0
  2405. psychopy/tests/data/envelopepowerandrcos_norm.png +0 -0
  2406. psychopy/tests/data/envelopepowerandrcos_normAddBlend.png +0 -0
  2407. psychopy/tests/data/envelopepowerandrcos_normHexbackground.png +0 -0
  2408. psychopy/tests/data/envelopepowerandrcos_pix.png +0 -0
  2409. psychopy/tests/data/envelopepowerandrcos_stencil.png +0 -0
  2410. psychopy/tests/data/filltext.png +0 -0
  2411. psychopy/tests/data/form_font_demographics.png +0 -0
  2412. psychopy/tests/data/form_font_demographics.xlsx +0 -0
  2413. psychopy/tests/data/form_font_languages.png +0 -0
  2414. psychopy/tests/data/form_font_languages.xlsx +0 -0
  2415. psychopy/tests/data/form_font_nonstandard.png +0 -0
  2416. psychopy/tests/data/futureParams.psyexp +150 -0
  2417. psychopy/tests/data/gabor1_cm.png +0 -0
  2418. psychopy/tests/data/gabor1_deg.png +0 -0
  2419. psychopy/tests/data/gabor1_degFlat.png +0 -0
  2420. psychopy/tests/data/gabor1_degFlatPos.png +0 -0
  2421. psychopy/tests/data/gabor1_height.png +0 -0
  2422. psychopy/tests/data/gabor1_norm.png +0 -0
  2423. psychopy/tests/data/gabor1_normAddBlend.png +0 -0
  2424. psychopy/tests/data/gabor1_normHexbackground.png +0 -0
  2425. psychopy/tests/data/gabor1_normNoShade.png +0 -0
  2426. psychopy/tests/data/gabor1_pix.png +0 -0
  2427. psychopy/tests/data/gabor1_stencil.png +0 -0
  2428. psychopy/tests/data/gabor2_cm.png +0 -0
  2429. psychopy/tests/data/gabor2_deg.png +0 -0
  2430. psychopy/tests/data/gabor2_degFlat.png +0 -0
  2431. psychopy/tests/data/gabor2_degFlatPos.png +0 -0
  2432. psychopy/tests/data/gabor2_height.png +0 -0
  2433. psychopy/tests/data/gabor2_norm.png +0 -0
  2434. psychopy/tests/data/gabor2_normAddBlend.png +0 -0
  2435. psychopy/tests/data/gabor2_normHexbackground.png +0 -0
  2436. psychopy/tests/data/gabor2_normNoShade.png +0 -0
  2437. psychopy/tests/data/gabor2_pix.png +0 -0
  2438. psychopy/tests/data/gabor2_stencil.png +0 -0
  2439. psychopy/tests/data/ghost_stroop.psyexp +131 -0
  2440. psychopy/tests/data/ghost_trialTypes.xlsx +0 -0
  2441. psychopy/tests/data/green_48000.flac.dist +0 -0
  2442. psychopy/tests/data/greyscale.jpg +0 -0
  2443. psychopy/tests/data/greyscale2.png +0 -0
  2444. psychopy/tests/data/greyscale2_cm.png +0 -0
  2445. psychopy/tests/data/greyscale2_deg.png +0 -0
  2446. psychopy/tests/data/greyscale2_degFlat.png +0 -0
  2447. psychopy/tests/data/greyscale2_degFlatPos.png +0 -0
  2448. psychopy/tests/data/greyscale2_height.png +0 -0
  2449. psychopy/tests/data/greyscale2_norm.png +0 -0
  2450. psychopy/tests/data/greyscale2_normAddBlend.png +0 -0
  2451. psychopy/tests/data/greyscale2_normHexbackground.png +0 -0
  2452. psychopy/tests/data/greyscale2_normNoShade.png +0 -0
  2453. psychopy/tests/data/greyscale2_pix.png +0 -0
  2454. psychopy/tests/data/greyscale2_stencil.png +0 -0
  2455. psychopy/tests/data/greyscaleLowContr_cm.png +0 -0
  2456. psychopy/tests/data/greyscaleLowContr_deg.png +0 -0
  2457. psychopy/tests/data/greyscaleLowContr_degFlat.png +0 -0
  2458. psychopy/tests/data/greyscaleLowContr_degFlatPos.png +0 -0
  2459. psychopy/tests/data/greyscaleLowContr_height.png +0 -0
  2460. psychopy/tests/data/greyscaleLowContr_norm.png +0 -0
  2461. psychopy/tests/data/greyscaleLowContr_normAddBlend.png +0 -0
  2462. psychopy/tests/data/greyscaleLowContr_normHexbackground.png +0 -0
  2463. psychopy/tests/data/greyscaleLowContr_normNoShade.png +0 -0
  2464. psychopy/tests/data/greyscaleLowContr_pix.png +0 -0
  2465. psychopy/tests/data/greyscaleLowContr_stencil.png +0 -0
  2466. psychopy/tests/data/greyscale_cm.png +0 -0
  2467. psychopy/tests/data/greyscale_deg.png +0 -0
  2468. psychopy/tests/data/greyscale_degFlat.png +0 -0
  2469. psychopy/tests/data/greyscale_degFlatPos.png +0 -0
  2470. psychopy/tests/data/greyscale_height.png +0 -0
  2471. psychopy/tests/data/greyscale_norm.png +0 -0
  2472. psychopy/tests/data/greyscale_normAddBlend.png +0 -0
  2473. psychopy/tests/data/greyscale_normHexbackground.png +0 -0
  2474. psychopy/tests/data/greyscale_normNoShade.png +0 -0
  2475. psychopy/tests/data/greyscale_pix.png +0 -0
  2476. psychopy/tests/data/greyscale_stencil.png +0 -0
  2477. psychopy/tests/data/imageAndGauss_cm.png +0 -0
  2478. psychopy/tests/data/imageAndGauss_deg.png +0 -0
  2479. psychopy/tests/data/imageAndGauss_degFlat.png +0 -0
  2480. psychopy/tests/data/imageAndGauss_degFlatPos.png +0 -0
  2481. psychopy/tests/data/imageAndGauss_height.png +0 -0
  2482. psychopy/tests/data/imageAndGauss_norm.png +0 -0
  2483. psychopy/tests/data/imageAndGauss_normAddBlend.png +0 -0
  2484. psychopy/tests/data/imageAndGauss_normHexbackground.png +0 -0
  2485. psychopy/tests/data/imageAndGauss_normNoShade.png +0 -0
  2486. psychopy/tests/data/imageAndGauss_pix.png +0 -0
  2487. psychopy/tests/data/imageAndGauss_stencil.png +0 -0
  2488. psychopy/tests/data/movFrame1_cm.png +0 -0
  2489. psychopy/tests/data/movFrame1_deg.png +0 -0
  2490. psychopy/tests/data/movFrame1_degFlat.png +0 -0
  2491. psychopy/tests/data/movFrame1_degFlatPos.png +0 -0
  2492. psychopy/tests/data/movFrame1_height.png +0 -0
  2493. psychopy/tests/data/movFrame1_norm.png +0 -0
  2494. psychopy/tests/data/movFrame1_normAddBlend.png +0 -0
  2495. psychopy/tests/data/movFrame1_normHexbackground.png +0 -0
  2496. psychopy/tests/data/movFrame1_normNoShade.png +0 -0
  2497. psychopy/tests/data/movFrame1_pix.png +0 -0
  2498. psychopy/tests/data/movFrame1_stencil.png +0 -0
  2499. psychopy/tests/data/multiKeypressExperiment.psydat +357 -0
  2500. psychopy/tests/data/multiKeypressTrialhandler.psydat +252 -0
  2501. psychopy/tests/data/multiStairConds.xlsx +0 -0
  2502. psychopy/tests/data/multiStairQuestPlus.xlsx +0 -0
  2503. psychopy/tests/data/newstyle.psydat +6155 -0
  2504. psychopy/tests/data/noiseAndRcos_cm.png +0 -0
  2505. psychopy/tests/data/noiseAndRcos_deg.png +0 -0
  2506. psychopy/tests/data/noiseAndRcos_degFlat.png +0 -0
  2507. psychopy/tests/data/noiseAndRcos_degFlatPos.png +0 -0
  2508. psychopy/tests/data/noiseAndRcos_height.png +0 -0
  2509. psychopy/tests/data/noiseAndRcos_norm.png +0 -0
  2510. psychopy/tests/data/noiseAndRcos_normAddBlend.png +0 -0
  2511. psychopy/tests/data/noiseAndRcos_normHexbackground.png +0 -0
  2512. psychopy/tests/data/noiseAndRcos_normNoShade.png +0 -0
  2513. psychopy/tests/data/noiseAndRcos_pix.png +0 -0
  2514. psychopy/tests/data/noiseAndRcos_stencil.png +0 -0
  2515. psychopy/tests/data/noiseFiltersAndRcos_cm.png +0 -0
  2516. psychopy/tests/data/noiseFiltersAndRcos_deg.png +0 -0
  2517. psychopy/tests/data/noiseFiltersAndRcos_degFlat.png +0 -0
  2518. psychopy/tests/data/noiseFiltersAndRcos_degFlatPos.png +0 -0
  2519. psychopy/tests/data/noiseFiltersAndRcos_height.png +0 -0
  2520. psychopy/tests/data/noiseFiltersAndRcos_norm.png +0 -0
  2521. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend.png +0 -0
  2522. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground.png +0 -0
  2523. psychopy/tests/data/noiseFiltersAndRcos_normNoShade.png +0 -0
  2524. psychopy/tests/data/noiseFiltersAndRcos_pix.png +0 -0
  2525. psychopy/tests/data/noiseFiltersAndRcos_stencil.png +0 -0
  2526. psychopy/tests/data/numpyImage_cm.png +0 -0
  2527. psychopy/tests/data/numpyImage_deg.png +0 -0
  2528. psychopy/tests/data/numpyImage_degFlat.png +0 -0
  2529. psychopy/tests/data/numpyImage_degFlatPos.png +0 -0
  2530. psychopy/tests/data/numpyImage_height.png +0 -0
  2531. psychopy/tests/data/numpyImage_norm.png +0 -0
  2532. psychopy/tests/data/numpyImage_normAddBlend.png +0 -0
  2533. psychopy/tests/data/numpyImage_normHexbackground.png +0 -0
  2534. psychopy/tests/data/numpyImage_normNoShade.png +0 -0
  2535. psychopy/tests/data/numpyImage_pix.png +0 -0
  2536. psychopy/tests/data/numpyImage_stencil.png +0 -0
  2537. psychopy/tests/data/numpyLowContr_cm.png +0 -0
  2538. psychopy/tests/data/numpyLowContr_deg.png +0 -0
  2539. psychopy/tests/data/numpyLowContr_degFlat.png +0 -0
  2540. psychopy/tests/data/numpyLowContr_degFlatPos.png +0 -0
  2541. psychopy/tests/data/numpyLowContr_height.png +0 -0
  2542. psychopy/tests/data/numpyLowContr_norm.png +0 -0
  2543. psychopy/tests/data/numpyLowContr_normAddBlend.png +0 -0
  2544. psychopy/tests/data/numpyLowContr_normHexbackground.png +0 -0
  2545. psychopy/tests/data/numpyLowContr_normNoShade.png +0 -0
  2546. psychopy/tests/data/numpyLowContr_pix.png +0 -0
  2547. psychopy/tests/data/numpyLowContr_stencil.png +0 -0
  2548. psychopy/tests/data/oldstyle.psydat +1534 -0
  2549. psychopy/tests/data/oldstyle_stair.psydat +263 -0
  2550. psychopy/tests/data/ratingscale1_cm.png +0 -0
  2551. psychopy/tests/data/ratingscale1_deg.png +0 -0
  2552. psychopy/tests/data/ratingscale1_degFlat.png +0 -0
  2553. psychopy/tests/data/ratingscale1_degFlatPos.png +0 -0
  2554. psychopy/tests/data/ratingscale1_height.png +0 -0
  2555. psychopy/tests/data/ratingscale1_norm.png +0 -0
  2556. psychopy/tests/data/ratingscale1_normAddBlend.png +0 -0
  2557. psychopy/tests/data/ratingscale1_normHexbackground.png +0 -0
  2558. psychopy/tests/data/ratingscale1_normNoShade.png +0 -0
  2559. psychopy/tests/data/ratingscale1_pix.png +0 -0
  2560. psychopy/tests/data/ratingscale1_stencil.png +0 -0
  2561. psychopy/tests/data/red_16000.flac.dist +0 -0
  2562. psychopy/tests/data/retroListParam.psyexp +82 -0
  2563. psychopy/tests/data/right_to_left_unidcode.xlsx +0 -0
  2564. psychopy/tests/data/sample.meshwarp.data +11522 -0
  2565. psychopy/tests/data/shape2_1_cm.png +0 -0
  2566. psychopy/tests/data/shape2_1_deg.png +0 -0
  2567. psychopy/tests/data/shape2_1_degFlat.png +0 -0
  2568. psychopy/tests/data/shape2_1_degFlatPos.png +0 -0
  2569. psychopy/tests/data/shape2_1_height.png +0 -0
  2570. psychopy/tests/data/shape2_1_norm.png +0 -0
  2571. psychopy/tests/data/shape2_1_normAddBlend.png +0 -0
  2572. psychopy/tests/data/shape2_1_normHexbackground.png +0 -0
  2573. psychopy/tests/data/shape2_1_normNoShade.png +0 -0
  2574. psychopy/tests/data/shape2_1_pix.png +0 -0
  2575. psychopy/tests/data/shape2_1_stencil.png +0 -0
  2576. psychopy/tests/data/shape2_2_cm.png +0 -0
  2577. psychopy/tests/data/shape2_2_deg.png +0 -0
  2578. psychopy/tests/data/shape2_2_degFlat.png +0 -0
  2579. psychopy/tests/data/shape2_2_degFlatPos.png +0 -0
  2580. psychopy/tests/data/shape2_2_height.png +0 -0
  2581. psychopy/tests/data/shape2_2_norm.png +0 -0
  2582. psychopy/tests/data/shape2_2_normAddBlend.png +0 -0
  2583. psychopy/tests/data/shape2_2_normHexbackground.png +0 -0
  2584. psychopy/tests/data/shape2_2_normNoShade.png +0 -0
  2585. psychopy/tests/data/shape2_2_pix.png +0 -0
  2586. psychopy/tests/data/shape2_2_stencil.png +0 -0
  2587. psychopy/tests/data/simpleimage1_cm.png +0 -0
  2588. psychopy/tests/data/simpleimage1_deg.png +0 -0
  2589. psychopy/tests/data/simpleimage1_degFlat.png +0 -0
  2590. psychopy/tests/data/simpleimage1_degFlatPos.png +0 -0
  2591. psychopy/tests/data/simpleimage1_height.png +0 -0
  2592. psychopy/tests/data/simpleimage1_norm.png +0 -0
  2593. psychopy/tests/data/simpleimage1_normAddBlend.png +0 -0
  2594. psychopy/tests/data/simpleimage1_normHexbackground.png +0 -0
  2595. psychopy/tests/data/simpleimage1_normNoShade.png +0 -0
  2596. psychopy/tests/data/simpleimage1_pix.png +0 -0
  2597. psychopy/tests/data/simpleimage1_stencil.png +0 -0
  2598. psychopy/tests/data/test001EntryImporting.psyexp +66 -0
  2599. psychopy/tests/data/testMovie.mp4 +0 -0
  2600. psychopy/tests/data/test_basic_run.py +1 -0
  2601. psychopy/tests/data/test_circle_radius_.1height.png +0 -0
  2602. psychopy/tests/data/test_circle_radius_.2height.png +0 -0
  2603. psychopy/tests/data/test_circle_radius_10pix.png +0 -0
  2604. psychopy/tests/data/test_circle_radius_20pix.png +0 -0
  2605. psychopy/tests/data/test_components/testClearKeyboard/testClearKeyboard.psyexp +200 -0
  2606. psychopy/tests/data/test_form_combinations_choice_bigItem.png +0 -0
  2607. psychopy/tests/data/test_form_combinations_choice_bigItemOverflow.png +0 -0
  2608. psychopy/tests/data/test_form_combinations_choice_bigResp.png +0 -0
  2609. psychopy/tests/data/test_form_combinations_choice_bigRespOverflow.png +0 -0
  2610. psychopy/tests/data/test_form_combinations_description_bigItem.png +0 -0
  2611. psychopy/tests/data/test_form_combinations_description_bigItemOverflow.png +0 -0
  2612. psychopy/tests/data/test_form_combinations_description_bigResp.png +0 -0
  2613. psychopy/tests/data/test_form_combinations_description_bigRespOverflow.png +0 -0
  2614. psychopy/tests/data/test_form_combinations_free text_bigItem.png +0 -0
  2615. psychopy/tests/data/test_form_combinations_free text_bigItemOverflow.png +0 -0
  2616. psychopy/tests/data/test_form_combinations_free text_bigResp.png +0 -0
  2617. psychopy/tests/data/test_form_combinations_free text_bigRespOverflow.png +0 -0
  2618. psychopy/tests/data/test_form_combinations_heading_bigItem.png +0 -0
  2619. psychopy/tests/data/test_form_combinations_heading_bigItemOverflow.png +0 -0
  2620. psychopy/tests/data/test_form_combinations_heading_bigResp.png +0 -0
  2621. psychopy/tests/data/test_form_combinations_heading_bigRespOverflow.png +0 -0
  2622. psychopy/tests/data/test_form_combinations_radio_bigItem.png +0 -0
  2623. psychopy/tests/data/test_form_combinations_radio_bigItemOverflow.png +0 -0
  2624. psychopy/tests/data/test_form_combinations_radio_bigResp.png +0 -0
  2625. psychopy/tests/data/test_form_combinations_radio_bigRespOverflow.png +0 -0
  2626. psychopy/tests/data/test_form_combinations_rating_bigItem.png +0 -0
  2627. psychopy/tests/data/test_form_combinations_rating_bigItemOverflow.png +0 -0
  2628. psychopy/tests/data/test_form_combinations_rating_bigResp.png +0 -0
  2629. psychopy/tests/data/test_form_combinations_rating_bigRespOverflow.png +0 -0
  2630. psychopy/tests/data/test_form_combinations_slider_bigItem.png +0 -0
  2631. psychopy/tests/data/test_form_combinations_slider_bigItemOverflow.png +0 -0
  2632. psychopy/tests/data/test_form_combinations_slider_bigResp.png +0 -0
  2633. psychopy/tests/data/test_form_combinations_slider_bigRespOverflow.png +0 -0
  2634. psychopy/tests/data/test_get_resources/blue.png +0 -0
  2635. psychopy/tests/data/test_get_resources/groupA.csv +3 -0
  2636. psychopy/tests/data/test_get_resources/groupB.csv +3 -0
  2637. psychopy/tests/data/test_get_resources/groups.csv +3 -0
  2638. psychopy/tests/data/test_get_resources/handledbyrm_constrloop.psyexp +143 -0
  2639. psychopy/tests/data/test_get_resources/handledbyrm_noloop.psyexp +114 -0
  2640. psychopy/tests/data/test_get_resources/handledbyrm_recurloop.psyexp +138 -0
  2641. psychopy/tests/data/test_get_resources/handledbyrm_strloop.psyexp +126 -0
  2642. psychopy/tests/data/test_get_resources/handledbystatic_constrloop.psyexp +141 -0
  2643. psychopy/tests/data/test_get_resources/handledbystatic_noloop.psyexp +112 -0
  2644. psychopy/tests/data/test_get_resources/handledbystatic_recurloop.psyexp +136 -0
  2645. psychopy/tests/data/test_get_resources/handledbystatic_strloop.psyexp +124 -0
  2646. psychopy/tests/data/test_get_resources/unhandled_constrloop.psyexp +128 -0
  2647. psychopy/tests/data/test_get_resources/unhandled_noloop.psyexp +99 -0
  2648. psychopy/tests/data/test_get_resources/unhandled_recurloop.psyexp +123 -0
  2649. psychopy/tests/data/test_get_resources/unhandled_strloop.psyexp +111 -0
  2650. psychopy/tests/data/test_get_resources/white.png +0 -0
  2651. psychopy/tests/data/test_get_resources/yellow.png +0 -0
  2652. psychopy/tests/data/test_image_aspect_default_None.png +0 -0
  2653. psychopy/tests/data/test_image_aspect_default_xFull_yNone.png +0 -0
  2654. psychopy/tests/data/test_image_aspect_default_xNone_yFull.png +0 -0
  2655. psychopy/tests/data/test_image_aspect_default_xNone_yNone.png +0 -0
  2656. psychopy/tests/data/test_image_flip_anchor_horiz.png +0 -0
  2657. psychopy/tests/data/test_image_flip_anchor_vert.png +0 -0
  2658. psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
  2659. psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
  2660. psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
  2661. psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
  2662. psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
  2663. psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
  2664. psychopy/tests/data/test_loops/testLoopsBlocks.psyexp +161 -0
  2665. psychopy/tests/data/test_loops/testStaircase.psyexp +105 -0
  2666. psychopy/tests/data/test_loops/test_current_loop_attr.psyexp +185 -0
  2667. psychopy/tests/data/test_panorama/panoramaTestImage.png +0 -0
  2668. psychopy/tests/data/test_panorama/panoramaTestImage.svg +1 -0
  2669. psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_-0.3.png +0 -0
  2670. psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_0.3.png +0 -0
  2671. psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_-0.3.png +0 -0
  2672. psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_0.3.png +0 -0
  2673. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_-1.0.png +0 -0
  2674. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_1.0.png +0 -0
  2675. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_-0.3.png +0 -0
  2676. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_0.3.png +0 -0
  2677. psychopy/tests/data/test_resources.psyexp +491 -0
  2678. psychopy/tests/data/test_session/outside_root/externalExp.psyexp +116 -0
  2679. psychopy/tests/data/test_session/root/annotation/annotation.psyexp +161 -0
  2680. psychopy/tests/data/test_session/root/error/error.psyexp +93 -0
  2681. psychopy/tests/data/test_session/root/exp1/exp1.psyexp +133 -0
  2682. psychopy/tests/data/test_session/root/exp2/exp2.psyexp +133 -0
  2683. psychopy/tests/data/test_session/root/frameRate/frameRate.psyexp +114 -0
  2684. psychopy/tests/data/test_session/root/invUseVersion/invUseVersion.psyexp +133 -0
  2685. psychopy/tests/data/test_session/root/testClockFormat/testClockFormat.psyexp +122 -0
  2686. psychopy/tests/data/test_session/root/testCtrls/testCtrls.psyexp +115 -0
  2687. psychopy/tests/data/test_session/root/testEditExpInfo/testEditExpInfo.psyexp +135 -0
  2688. psychopy/tests/data/test_session/root/testFutureTrials/testFutureTrials.psyexp +155 -0
  2689. psychopy/tests/data/test_session/root/testNamedButtonBox/testNamedButtonBox.psyexp +145 -0
  2690. psychopy/tests/data/test_session/root/testTrialNav/trialNav.psyexp +158 -0
  2691. psychopy/tests/data/test_slider_horiz_accute_horiz.png +0 -0
  2692. psychopy/tests/data/test_slider_horiz_accute_vert.png +0 -0
  2693. psychopy/tests/data/test_slider_horiz_horiz.png +0 -0
  2694. psychopy/tests/data/test_slider_horiz_obtuse_horiz.png +0 -0
  2695. psychopy/tests/data/test_slider_horiz_obtuse_vert.png +0 -0
  2696. psychopy/tests/data/test_slider_horiz_vert.png +0 -0
  2697. psychopy/tests/data/test_slider_ticklabelloc_blanks.png +0 -0
  2698. psychopy/tests/data/test_slider_ticklabelloc_clustered.png +0 -0
  2699. psychopy/tests/data/test_slider_ticklabelloc_morelabels.png +0 -0
  2700. psychopy/tests/data/test_slider_ticklabelloc_morelabelsclustered.png +0 -0
  2701. psychopy/tests/data/test_slider_ticklabelloc_moreticks.png +0 -0
  2702. psychopy/tests/data/test_slider_ticklabelloc_moreticksclustered.png +0 -0
  2703. psychopy/tests/data/test_slider_ticklabelloc_nolabels.png +0 -0
  2704. psychopy/tests/data/test_slider_ticklabelloc_noticks.png +0 -0
  2705. psychopy/tests/data/test_slider_ticklabelloc_simple.png +0 -0
  2706. psychopy/tests/data/test_slider_triangle_horiz_False_flip_False.png +0 -0
  2707. psychopy/tests/data/test_slider_triangle_horiz_False_flip_True.png +0 -0
  2708. psychopy/tests/data/test_slider_triangle_horiz_True_flip_False.png +0 -0
  2709. psychopy/tests/data/test_slider_triangle_horiz_True_flip_True.png +0 -0
  2710. psychopy/tests/data/test_sounds/default_16000.wav +0 -0
  2711. psychopy/tests/data/test_sounds/default_192000.wav +0 -0
  2712. psychopy/tests/data/test_sounds/default_22050.wav +0 -0
  2713. psychopy/tests/data/test_sounds/default_32000.wav +0 -0
  2714. psychopy/tests/data/test_sounds/default_44100.wav +0 -0
  2715. psychopy/tests/data/test_sounds/default_48000.wav +0 -0
  2716. psychopy/tests/data/test_sounds/default_8000.wav +0 -0
  2717. psychopy/tests/data/test_sounds/default_96000.wav +0 -0
  2718. psychopy/tests/data/test_static_component_script.psyexp +76 -0
  2719. psychopy/tests/data/test_win_bg_large_256_default.png +0 -0
  2720. psychopy/tests/data/test_win_bg_large_500_default.png +0 -0
  2721. psychopy/tests/data/test_win_bg_small_200_default.png +0 -0
  2722. psychopy/tests/data/test_win_bg_small_256_default.png +0 -0
  2723. psychopy/tests/data/test_win_bg_tall_200_default.png +0 -0
  2724. psychopy/tests/data/test_win_bg_tall_256_default.png +0 -0
  2725. psychopy/tests/data/test_win_bg_tall_500_default.png +0 -0
  2726. psychopy/tests/data/test_win_bg_tall_fill_default.png +0 -0
  2727. psychopy/tests/data/test_win_bg_wide_200_default.png +0 -0
  2728. psychopy/tests/data/test_win_bg_wide_256_default.png +0 -0
  2729. psychopy/tests/data/test_win_bg_wide_500_default.png +0 -0
  2730. psychopy/tests/data/test_win_bg_wide_fill_default.png +0 -0
  2731. psychopy/tests/data/test_win_bgcolor_blue.png +0 -0
  2732. psychopy/tests/data/test_win_bgcolor_green.png +0 -0
  2733. psychopy/tests/data/test_win_bgcolor_red.png +0 -0
  2734. psychopy/tests/data/testimage.jpg +0 -0
  2735. psychopy/tests/data/testimagegray.jpg +0 -0
  2736. psychopy/tests/data/testpixels.png +0 -0
  2737. psychopy/tests/data/testwedges.png +0 -0
  2738. psychopy/tests/data/text1_cm.png +0 -0
  2739. psychopy/tests/data/text1_deg.png +0 -0
  2740. psychopy/tests/data/text1_degFlat.png +0 -0
  2741. psychopy/tests/data/text1_degFlatPos.png +0 -0
  2742. psychopy/tests/data/text1_height.png +0 -0
  2743. psychopy/tests/data/text1_norm.png +0 -0
  2744. psychopy/tests/data/text1_normAddBlend.png +0 -0
  2745. psychopy/tests/data/text1_normHexbackground.png +0 -0
  2746. psychopy/tests/data/text1_normNoShade.png +0 -0
  2747. psychopy/tests/data/text1_pix.png +0 -0
  2748. psychopy/tests/data/text1_stencil.png +0 -0
  2749. psychopy/tests/data/text2_cm.png +0 -0
  2750. psychopy/tests/data/text2_deg.png +0 -0
  2751. psychopy/tests/data/text2_degFlat.png +0 -0
  2752. psychopy/tests/data/text2_degFlatPos.png +0 -0
  2753. psychopy/tests/data/text2_norm.png +0 -0
  2754. psychopy/tests/data/text2_normNoShade.png +0 -0
  2755. psychopy/tests/data/text2_pix.png +0 -0
  2756. psychopy/tests/data/textbox_charcolors_default_roygbiv.png +0 -0
  2757. psychopy/tests/data/textbox_charcolors_default_white_hello_black_there.png +0 -0
  2758. psychopy/tests/data/textbox_default_align_bottom_center.png +0 -0
  2759. psychopy/tests/data/textbox_default_align_bottom_left.png +0 -0
  2760. psychopy/tests/data/textbox_default_align_bottom_right.png +0 -0
  2761. psychopy/tests/data/textbox_default_align_center.png +0 -0
  2762. psychopy/tests/data/textbox_default_align_center_center.png +0 -0
  2763. psychopy/tests/data/textbox_default_align_center_left.png +0 -0
  2764. psychopy/tests/data/textbox_default_align_center_right.png +0 -0
  2765. psychopy/tests/data/textbox_default_align_centre.png +0 -0
  2766. psychopy/tests/data/textbox_default_align_centre_centre.png +0 -0
  2767. psychopy/tests/data/textbox_default_align_more_than_two_words.png +0 -0
  2768. psychopy/tests/data/textbox_default_align_someword.png +0 -0
  2769. psychopy/tests/data/textbox_default_align_top_center.png +0 -0
  2770. psychopy/tests/data/textbox_default_align_top_left.png +0 -0
  2771. psychopy/tests/data/textbox_default_align_top_right.png +0 -0
  2772. psychopy/tests/data/textbox_default_colors_WOB.png +0 -0
  2773. psychopy/tests/data/textbox_default_colors_exemplar1.png +0 -0
  2774. psychopy/tests/data/textbox_default_colors_exemplar2.png +0 -0
  2775. psychopy/tests/data/textbox_default_colors_exemplar3.png +0 -0
  2776. psychopy/tests/data/textbox_default_colors_tyke1.png +0 -0
  2777. psychopy/tests/data/textbox_default_colors_tyke2.png +0 -0
  2778. psychopy/tests/data/textbox_default_colors_tyke3.png +0 -0
  2779. psychopy/tests/data/textbox_default_cutoff_top.png +0 -0
  2780. psychopy/tests/data/textbox_default_exemplar_1.png +0 -0
  2781. psychopy/tests/data/textbox_default_exemplar_2.png +0 -0
  2782. psychopy/tests/data/textbox_default_exemplar_3.png +0 -0
  2783. psychopy/tests/data/textbox_default_exemplar_4.png +0 -0
  2784. psychopy/tests/data/textbox_default_tyke_1.png +0 -0
  2785. psychopy/tests/data/textbox_default_tyke_2.png +0 -0
  2786. psychopy/tests/data/textbox_typing_blank.png +0 -0
  2787. psychopy/tests/data/textbox_typing_longKoeran.png +0 -0
  2788. psychopy/tests/data/textbox_typing_longWord.png +0 -0
  2789. psychopy/tests/data/textbox_typing_navDel.png +0 -0
  2790. psychopy/tests/data/textbox_typing_navLR.png +0 -0
  2791. psychopy/tests/data/textbox_typing_newline.png +0 -0
  2792. psychopy/tests/data/textbox_typing_pangram.png +0 -0
  2793. psychopy/tests/data/textbox_uax14_align_bottom_center.png +0 -0
  2794. psychopy/tests/data/textbox_uax14_align_bottom_left.png +0 -0
  2795. psychopy/tests/data/textbox_uax14_align_bottom_right.png +0 -0
  2796. psychopy/tests/data/textbox_uax14_align_center.png +0 -0
  2797. psychopy/tests/data/textbox_uax14_align_center_center.png +0 -0
  2798. psychopy/tests/data/textbox_uax14_align_center_left.png +0 -0
  2799. psychopy/tests/data/textbox_uax14_align_center_right.png +0 -0
  2800. psychopy/tests/data/textbox_uax14_align_centre.png +0 -0
  2801. psychopy/tests/data/textbox_uax14_align_centre_centre.png +0 -0
  2802. psychopy/tests/data/textbox_uax14_align_more_than_two_words.png +0 -0
  2803. psychopy/tests/data/textbox_uax14_align_someword.png +0 -0
  2804. psychopy/tests/data/textbox_uax14_align_top_center.png +0 -0
  2805. psychopy/tests/data/textbox_uax14_align_top_left.png +0 -0
  2806. psychopy/tests/data/textbox_uax14_align_top_right.png +0 -0
  2807. psychopy/tests/data/textbox_uax14_colors_WOB.png +0 -0
  2808. psychopy/tests/data/textbox_uax14_colors_exemplar1.png +0 -0
  2809. psychopy/tests/data/textbox_uax14_colors_exemplar2.png +0 -0
  2810. psychopy/tests/data/textbox_uax14_colors_exemplar3.png +0 -0
  2811. psychopy/tests/data/textbox_uax14_colors_tyke1.png +0 -0
  2812. psychopy/tests/data/textbox_uax14_colors_tyke2.png +0 -0
  2813. psychopy/tests/data/textbox_uax14_colors_tyke3.png +0 -0
  2814. psychopy/tests/data/textbox_uax14_cutoff_top.png +0 -0
  2815. psychopy/tests/data/textbox_uax14_exemplar_1.png +0 -0
  2816. psychopy/tests/data/textbox_uax14_exemplar_2.png +0 -0
  2817. psychopy/tests/data/textbox_uax14_exemplar_3.png +0 -0
  2818. psychopy/tests/data/textbox_uax14_exemplar_4.png +0 -0
  2819. psychopy/tests/data/textbox_uax14_tyke_1.png +0 -0
  2820. psychopy/tests/data/textbox_uax14_tyke_2.png +0 -0
  2821. psychopy/tests/data/trialTypes.csv +1 -0
  2822. psychopy/tests/data/trialTypes.docx +0 -0
  2823. psychopy/tests/data/trialTypes.pkl +0 -0
  2824. psychopy/tests/data/trialTypes.tsv +7 -0
  2825. psychopy/tests/data/trialTypes.xls +0 -0
  2826. psychopy/tests/data/trialTypes.xlsx +0 -0
  2827. psychopy/tests/data/trialTypes_eu.csv +7 -0
  2828. psychopy/tests/data/trialsBlankCols.xlsx +0 -0
  2829. psychopy/tests/data/wedge1_cm.png +0 -0
  2830. psychopy/tests/data/wedge1_deg.png +0 -0
  2831. psychopy/tests/data/wedge1_degFlat.png +0 -0
  2832. psychopy/tests/data/wedge1_degFlatPos.png +0 -0
  2833. psychopy/tests/data/wedge1_height.png +0 -0
  2834. psychopy/tests/data/wedge1_norm.png +0 -0
  2835. psychopy/tests/data/wedge1_normAddBlend.png +0 -0
  2836. psychopy/tests/data/wedge1_normHexbackground.png +0 -0
  2837. psychopy/tests/data/wedge1_normNoShade.png +0 -0
  2838. psychopy/tests/data/wedge1_pix.png +0 -0
  2839. psychopy/tests/data/wedge1_stencil.png +0 -0
  2840. psychopy/tests/data/wedge2_cm.png +0 -0
  2841. psychopy/tests/data/wedge2_deg.png +0 -0
  2842. psychopy/tests/data/wedge2_degFlat.png +0 -0
  2843. psychopy/tests/data/wedge2_degFlatPos.png +0 -0
  2844. psychopy/tests/data/wedge2_height.png +0 -0
  2845. psychopy/tests/data/wedge2_norm.png +0 -0
  2846. psychopy/tests/data/wedge2_normAddBlend.png +0 -0
  2847. psychopy/tests/data/wedge2_normHexbackground.png +0 -0
  2848. psychopy/tests/data/wedge2_normNoShade.png +0 -0
  2849. psychopy/tests/data/wedge2_pix.png +0 -0
  2850. psychopy/tests/data/wedge2_stencil.png +0 -0
  2851. psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos-0.4_0.png +0 -0
  2852. psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos0_0.png +0 -0
  2853. psychopy/tests/data/winScalePos_ori0_scale-1_1_pos-0.4_0.png +0 -0
  2854. psychopy/tests/data/winScalePos_ori0_scale-1_1_pos0_0.png +0 -0
  2855. psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos-0.4_0.png +0 -0
  2856. psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos0_0.png +0 -0
  2857. psychopy/tests/data/winScalePos_ori0_scale-2_2_pos-0.4_0.png +0 -0
  2858. psychopy/tests/data/winScalePos_ori0_scale-2_2_pos0_0.png +0 -0
  2859. psychopy/tests/data/winScalePos_ori0_scale1_-1_pos-0.4_0.png +0 -0
  2860. psychopy/tests/data/winScalePos_ori0_scale1_-1_pos0_0.png +0 -0
  2861. psychopy/tests/data/winScalePos_ori0_scale1_1_pos-0.4_0.png +0 -0
  2862. psychopy/tests/data/winScalePos_ori0_scale1_1_pos0_0.png +0 -0
  2863. psychopy/tests/data/winScalePos_ori0_scale2_-2_pos-0.4_0.png +0 -0
  2864. psychopy/tests/data/winScalePos_ori0_scale2_-2_pos0_0.png +0 -0
  2865. psychopy/tests/data/winScalePos_ori0_scale2_2_pos-0.4_0.png +0 -0
  2866. psychopy/tests/data/winScalePos_ori0_scale2_2_pos0_0.png +0 -0
  2867. psychopy/tests/data/winScalePos_ori45_scale-1_-1_pos0_0.png +0 -0
  2868. psychopy/tests/data/winScalePos_ori45_scale-1_1_pos0_0.png +0 -0
  2869. psychopy/tests/data/winScalePos_ori45_scale-2_-2_pos0_0.png +0 -0
  2870. psychopy/tests/data/winScalePos_ori45_scale-2_2_pos0_0.png +0 -0
  2871. psychopy/tests/data/winScalePos_ori45_scale1_-1_pos0_0.png +0 -0
  2872. psychopy/tests/data/winScalePos_ori45_scale1_1_pos0_0.png +0 -0
  2873. psychopy/tests/data/winScalePos_ori45_scale2_-2_pos0_0.png +0 -0
  2874. psychopy/tests/data/winScalePos_ori45_scale2_2_pos0_0.png +0 -0
  2875. psychopy/tests/doc/run_btn.png +0 -0
  2876. psychopy/tests/doc/test-suite-bugs.jpg +0 -0
  2877. psychopy/tests/doc/user-bugs.jpg +0 -0
  2878. psychopy/tests/dummy_xorg.conf +21 -0
  2879. psychopy/tests/run.py +50 -0
  2880. psychopy/tests/test_Installation.py +38 -0
  2881. psychopy/tests/test_alerts/test_alerts.py +37 -0
  2882. psychopy/tests/test_alerts/test_alerttools.py +132 -0
  2883. psychopy/tests/test_app/__init__.py +0 -0
  2884. psychopy/tests/test_app/conftest.py +40 -0
  2885. psychopy/tests/test_app/test_builder/__init__.py +0 -0
  2886. psychopy/tests/test_app/test_builder/test_BuilderFrame.py +166 -0
  2887. psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +133 -0
  2888. psychopy/tests/test_app/test_builder/test_ComponentDialogs.py +98 -0
  2889. psychopy/tests/test_app/test_command_line.py +65 -0
  2890. psychopy/tests/test_app/test_runner/__init__.py +0 -0
  2891. psychopy/tests/test_app/test_runner/test_RunnerFrame.py +166 -0
  2892. psychopy/tests/test_app/test_speed.py +147 -0
  2893. psychopy/tests/test_app/test_themes/test_icons.py +79 -0
  2894. psychopy/tests/test_codecov.py +29 -0
  2895. psychopy/tests/test_colors/test_Color.py +41 -0
  2896. psychopy/tests/test_data/dataPrev.xlsx +0 -0
  2897. psychopy/tests/test_data/test_ExperimentHandler.py +171 -0
  2898. psychopy/tests/test_data/test_MultiStairHandler.py +174 -0
  2899. psychopy/tests/test_data/test_StairHandlers.py +1241 -0
  2900. psychopy/tests/test_data/test_TrialHandler.py +335 -0
  2901. psychopy/tests/test_data/test_TrialHandler2.py +584 -0
  2902. psychopy/tests/test_data/test_TrialHandlerExt.py +359 -0
  2903. psychopy/tests/test_data/test_fitFunctions.py +101 -0
  2904. psychopy/tests/test_data/test_utils.py +173 -0
  2905. psychopy/tests/test_data/test_xlsx.py +92 -0
  2906. psychopy/tests/test_demos/test_builder_demos.py +52 -0
  2907. psychopy/tests/test_experiment/__init__.py +0 -0
  2908. psychopy/tests/test_experiment/known_py_diffs.txt +47 -0
  2909. psychopy/tests/test_experiment/needs_wx/__init__.py +0 -0
  2910. psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +14780 -0
  2911. psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +130 -0
  2912. psychopy/tests/test_experiment/needs_wx/test_Experiment.py +406 -0
  2913. psychopy/tests/test_experiment/needs_wx/test_components.py +170 -0
  2914. psychopy/tests/test_experiment/test_component_compile_js.py +59 -0
  2915. psychopy/tests/test_experiment/test_component_compile_python.py +165 -0
  2916. psychopy/tests/test_experiment/test_components/__init__.py +1 -0
  2917. psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py +218 -0
  2918. psychopy/tests/test_experiment/test_components/test_CodeComponent.py +70 -0
  2919. psychopy/tests/test_experiment/test_components/test_GratingComponent.py +7 -0
  2920. psychopy/tests/test_experiment/test_components/test_ImageComponent.py +8 -0
  2921. psychopy/tests/test_experiment/test_components/test_KeyboardComponent.py +28 -0
  2922. psychopy/tests/test_experiment/test_components/test_MouseComponent.py +136 -0
  2923. psychopy/tests/test_experiment/test_components/test_PolygonComponent.py +65 -0
  2924. psychopy/tests/test_experiment/test_components/test_ResourceManagerComponent.py +50 -0
  2925. psychopy/tests/test_experiment/test_components/test_RoutineSettingsComponent.py +16 -0
  2926. psychopy/tests/test_experiment/test_components/test_SettingsComponent.py +80 -0
  2927. psychopy/tests/test_experiment/test_components/test_StaticComponent.py +51 -0
  2928. psychopy/tests/test_experiment/test_components/test_UnknownPluginComponent.py +27 -0
  2929. psychopy/tests/test_experiment/test_components/test_all_components.py +104 -0
  2930. psychopy/tests/test_experiment/test_components/test_base_components.py +477 -0
  2931. psychopy/tests/test_experiment/test_experiment.py +199 -0
  2932. psychopy/tests/test_experiment/test_loops.py +103 -0
  2933. psychopy/tests/test_experiment/test_params.py +363 -0
  2934. psychopy/tests/test_experiment/test_py2js.py +230 -0
  2935. psychopy/tests/test_experiment/test_routines/__init__.py +1 -0
  2936. psychopy/tests/test_experiment/test_routines/test_EyetrackerCalibrationRoutine.py +9 -0
  2937. psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +9 -0
  2938. psychopy/tests/test_experiment/test_routines/test_all_routines.py +23 -0
  2939. psychopy/tests/test_experiment/test_routines/test_base_routine.py +154 -0
  2940. psychopy/tests/test_experiment/test_routines/test_standalone_routines.py +36 -0
  2941. psychopy/tests/test_gui/test_DlgFromDictQt.py +82 -0
  2942. psychopy/tests/test_gui/test_DlgFromDictWx.py +82 -0
  2943. psychopy/tests/test_hardware/__init__.py +0 -0
  2944. psychopy/tests/test_hardware/test_device_manager.py +36 -0
  2945. psychopy/tests/test_hardware/test_emulator.py +106 -0
  2946. psychopy/tests/test_hardware/test_gammasci.py +35 -0
  2947. psychopy/tests/test_hardware/test_keyboard.py +241 -0
  2948. psychopy/tests/test_hardware/test_keyboard_events.py +362 -0
  2949. psychopy/tests/test_hardware/test_photodiode.py +66 -0
  2950. psychopy/tests/test_hardware/test_ports.py +129 -0
  2951. psychopy/tests/test_iohub/__init__.py +1 -0
  2952. psychopy/tests/test_iohub/test_computer.py +124 -0
  2953. psychopy/tests/test_iohub/test_event_get_and_clear.py +94 -0
  2954. psychopy/tests/test_iohub/test_keyboard.py +91 -0
  2955. psychopy/tests/test_iohub/test_launch.py +19 -0
  2956. psychopy/tests/test_iohub/testutil.py +54 -0
  2957. psychopy/tests/test_liaison/test_Liaison.py +275 -0
  2958. psychopy/tests/test_misc/__init__.py +0 -0
  2959. psychopy/tests/test_misc/memory_usage.py +164 -0
  2960. psychopy/tests/test_misc/test_GammaFun.py +15 -0
  2961. psychopy/tests/test_misc/test_clock.py +63 -0
  2962. psychopy/tests/test_misc/test_color.py +205 -0
  2963. psychopy/tests/test_misc/test_core.py +451 -0
  2964. psychopy/tests/test_misc/test_event.py +308 -0
  2965. psychopy/tests/test_misc/test_info.py +18 -0
  2966. psychopy/tests/test_misc/test_layout.py +49 -0
  2967. psychopy/tests/test_misc/test_locale.py +38 -0
  2968. psychopy/tests/test_misc/test_web.py +22 -0
  2969. psychopy/tests/test_monitors/test_monitors.py +91 -0
  2970. psychopy/tests/test_plugins/__init__.py +0 -0
  2971. psychopy/tests/test_plugins/test_plugin_stubs.py +125 -0
  2972. psychopy/tests/test_preferences/__init__.py +0 -0
  2973. psychopy/tests/test_preferences/test_prefs.py +28 -0
  2974. psychopy/tests/test_session/test_Session.py +257 -0
  2975. psychopy/tests/test_sound/__init__.py +0 -0
  2976. psychopy/tests/test_sound/test_audioclip.py +331 -0
  2977. psychopy/tests/test_sound/test_hamming.py +45 -0
  2978. psychopy/tests/test_sound/test_sound.py +138 -0
  2979. psychopy/tests/test_sound/test_sound_pygame.py +67 -0
  2980. psychopy/tests/test_tools/test_animationtools.py +51 -0
  2981. psychopy/tests/test_tools/test_arraytools.py +242 -0
  2982. psychopy/tests/test_tools/test_attributetools.py +257 -0
  2983. psychopy/tests/test_tools/test_colorspacetools.py +150 -0
  2984. psychopy/tests/test_tools/test_environmenttools.py +52 -0
  2985. psychopy/tests/test_tools/test_fileerrortools.py +97 -0
  2986. psychopy/tests/test_tools/test_filetools.py +104 -0
  2987. psychopy/tests/test_tools/test_imagetools.py +57 -0
  2988. psychopy/tests/test_tools/test_mathtools.py +688 -0
  2989. psychopy/tests/test_tools/test_stringtools.py +143 -0
  2990. psychopy/tests/test_tools/test_versionchooser.py +209 -0
  2991. psychopy/tests/test_tools/test_viewtools.py +168 -0
  2992. psychopy/tests/test_validators/__init__.py +0 -0
  2993. psychopy/tests/test_validators/test_voicekeyValidator.py +95 -0
  2994. psychopy/tests/test_visual/__init__.py +0 -0
  2995. psychopy/tests/test_visual/measure_parity.py +243 -0
  2996. psychopy/tests/test_visual/test_all_stimuli.py +655 -0
  2997. psychopy/tests/test_visual/test_basevisual.py +564 -0
  2998. psychopy/tests/test_visual/test_brush.py +68 -0
  2999. psychopy/tests/test_visual/test_button.py +15 -0
  3000. psychopy/tests/test_visual/test_circle.py +74 -0
  3001. psychopy/tests/test_visual/test_contains_overlaps.py +242 -0
  3002. psychopy/tests/test_visual/test_custommouse.py +47 -0
  3003. psychopy/tests/test_visual/test_dots.py +155 -0
  3004. psychopy/tests/test_visual/test_form.py +414 -0
  3005. psychopy/tests/test_visual/test_framepacking.py +41 -0
  3006. psychopy/tests/test_visual/test_gamma.py +152 -0
  3007. psychopy/tests/test_visual/test_glfw_backend.py +14 -0
  3008. psychopy/tests/test_visual/test_image.py +219 -0
  3009. psychopy/tests/test_visual/test_panorama.py +41 -0
  3010. psychopy/tests/test_visual/test_progress.py +96 -0
  3011. psychopy/tests/test_visual/test_projections.py +215 -0
  3012. psychopy/tests/test_visual/test_projections_interactive.py +163 -0
  3013. psychopy/tests/test_visual/test_roi.py +105 -0
  3014. psychopy/tests/test_visual/test_shape.py +23 -0
  3015. psychopy/tests/test_visual/test_slider.py +328 -0
  3016. psychopy/tests/test_visual/test_target.py +35 -0
  3017. psychopy/tests/test_visual/test_textbox.py +508 -0
  3018. psychopy/tests/test_visual/test_winFlipTiming.py +95 -0
  3019. psychopy/tests/test_visual/test_winScalePos.py +78 -0
  3020. psychopy/tests/test_visual/test_window.py +124 -0
  3021. psychopy/tests/utils.py +372 -0
  3022. psychopy/tools/LineBreak.txt +3597 -0
  3023. psychopy/tools/__init__.py +9 -0
  3024. psychopy/tools/animationtools.py +49 -0
  3025. psychopy/tools/apptools.py +32 -0
  3026. psychopy/tools/arraytools.py +561 -0
  3027. psychopy/tools/attributetools.py +263 -0
  3028. psychopy/tools/audiotools.py +361 -0
  3029. psychopy/tools/colorspacetools.py +717 -0
  3030. psychopy/tools/coordinatetools.py +104 -0
  3031. psychopy/tools/environmenttools.py +62 -0
  3032. psychopy/tools/fileerrortools.py +66 -0
  3033. psychopy/tools/filetools.py +406 -0
  3034. psychopy/tools/fontmanager.py +1065 -0
  3035. psychopy/tools/gltools.py +7367 -0
  3036. psychopy/tools/imagetools.py +65 -0
  3037. psychopy/tools/linebreak.py +322 -0
  3038. psychopy/tools/mathtools.py +4910 -0
  3039. psychopy/tools/monitorunittools.py +276 -0
  3040. psychopy/tools/movietools.py +1111 -0
  3041. psychopy/tools/pkgtools.py +654 -0
  3042. psychopy/tools/plottools.py +25 -0
  3043. psychopy/tools/rifttools.py +76 -0
  3044. psychopy/tools/stereotools.py +9 -0
  3045. psychopy/tools/stimulustools.py +198 -0
  3046. psychopy/tools/stringtools.py +466 -0
  3047. psychopy/tools/systemtools.py +1331 -0
  3048. psychopy/tools/typetools.py +52 -0
  3049. psychopy/tools/unittools.py +16 -0
  3050. psychopy/tools/versionchooser.py +604 -0
  3051. psychopy/tools/viewtools.py +1068 -0
  3052. psychopy/tools/wizard.py +804 -0
  3053. psychopy/validation/__init__.py +6 -0
  3054. psychopy/validation/audio.py +78 -0
  3055. psychopy/validation/visual.py +119 -0
  3056. psychopy/visual/__init__.py +121 -0
  3057. psychopy/visual/aperture.py +333 -0
  3058. psychopy/visual/backends/__init__.py +77 -0
  3059. psychopy/visual/backends/_base.py +481 -0
  3060. psychopy/visual/backends/gamma.py +349 -0
  3061. psychopy/visual/backends/glfwbackend.py +24 -0
  3062. psychopy/visual/backends/pygamebackend.py +343 -0
  3063. psychopy/visual/backends/pygletbackend.py +970 -0
  3064. psychopy/visual/basevisual.py +2027 -0
  3065. psychopy/visual/brush.py +204 -0
  3066. psychopy/visual/bufferimage.py +311 -0
  3067. psychopy/visual/button.py +194 -0
  3068. psychopy/visual/circle.py +165 -0
  3069. psychopy/visual/custommouse.py +263 -0
  3070. psychopy/visual/dot.py +734 -0
  3071. psychopy/visual/dropdown.py +165 -0
  3072. psychopy/visual/elementarray.py +802 -0
  3073. psychopy/visual/filters.py +419 -0
  3074. psychopy/visual/form.py +1195 -0
  3075. psychopy/visual/globalVars.py +24 -0
  3076. psychopy/visual/grating.py +581 -0
  3077. psychopy/visual/helpers.py +336 -0
  3078. psychopy/visual/image.py +438 -0
  3079. psychopy/visual/line.py +227 -0
  3080. psychopy/visual/movie.py +12 -0
  3081. psychopy/visual/movie2.py +21 -0
  3082. psychopy/visual/movie3.py +18 -0
  3083. psychopy/visual/movies/__init__.py +2334 -0
  3084. psychopy/visual/movies/frame.py +258 -0
  3085. psychopy/visual/movies/metadata.py +242 -0
  3086. psychopy/visual/movies/players/_base.py +364 -0
  3087. psychopy/visual/nnlvs.py +830 -0
  3088. psychopy/visual/noise.py +32 -0
  3089. psychopy/visual/panorama.py +313 -0
  3090. psychopy/visual/patch.py +21 -0
  3091. psychopy/visual/pie.py +237 -0
  3092. psychopy/visual/polygon.py +226 -0
  3093. psychopy/visual/progress.py +313 -0
  3094. psychopy/visual/radial.py +29 -0
  3095. psychopy/visual/ratingscale.py +18 -0
  3096. psychopy/visual/rect.py +195 -0
  3097. psychopy/visual/rift.py +2660 -0
  3098. psychopy/visual/roi.py +141 -0
  3099. psychopy/visual/secondorder.py +27 -0
  3100. psychopy/visual/shaders.py +787 -0
  3101. psychopy/visual/shape.py +897 -0
  3102. psychopy/visual/simpleimage.py +303 -0
  3103. psychopy/visual/slider.py +1222 -0
  3104. psychopy/visual/stim3d.py +2109 -0
  3105. psychopy/visual/target.py +278 -0
  3106. psychopy/visual/text.py +769 -0
  3107. psychopy/visual/textbox/__init__.py +1280 -0
  3108. psychopy/visual/textbox/fontmanager.py +574 -0
  3109. psychopy/visual/textbox/parsedtext.py +317 -0
  3110. psychopy/visual/textbox/textgrid.py +278 -0
  3111. psychopy/visual/textbox/textureatlas.py +248 -0
  3112. psychopy/visual/textbox2/__init__.py +4 -0
  3113. psychopy/visual/textbox2/textbox2.py +1970 -0
  3114. psychopy/visual/vlcmoviestim.py +1294 -0
  3115. psychopy/visual/window.py +3910 -0
  3116. psychopy/visual/windowframepack.py +86 -0
  3117. psychopy/visual/windowwarp.py +457 -0
  3118. psychopy/voicekey/__init__.py +56 -0
  3119. psychopy/voicekey/labjack_vks.py +9 -0
  3120. psychopy/voicekey/parallel_vks.py +9 -0
  3121. psychopy/voicekey/vk_tools.py +131 -0
  3122. psychopy/web.py +289 -0
  3123. psychopy-2025.2.4.dist-info/METADATA +160 -0
  3124. psychopy-2025.2.4.dist-info/RECORD +3127 -0
  3125. psychopy-2025.2.4.dist-info/WHEEL +4 -0
  3126. psychopy-2025.2.4.dist-info/entry_points.txt +5 -0
  3127. psychopy-2025.2.4.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,3657 @@
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-2025 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
+ import ctypes
64
+ import collections
65
+
66
+ from psychopy import core
67
+ from psychopy.constants import NOT_STARTED
68
+ from psychopy.hardware import DeviceManager
69
+ from psychopy.hardware.base import BaseDevice
70
+ from psychopy.visual.movies.frame import MovieFrame, NULL_MOVIE_FRAME_INFO
71
+ from psychopy.sound.microphone import Microphone
72
+ from psychopy.hardware.microphone import MicrophoneDevice
73
+ from psychopy.tools import systemtools as st
74
+ import psychopy.tools.movietools as movietools
75
+ import psychopy.logging as logging
76
+ from psychopy.localization import _translate
77
+
78
+ # ------------------------------------------------------------------------------
79
+ # Constants
80
+ #
81
+
82
+ VIDEO_DEVICE_ROOT_LINUX = '/dev'
83
+ CAMERA_UNKNOWN_VALUE = u'Unknown' # fields where we couldn't get a value
84
+ CAMERA_NULL_VALUE = u'Null' # fields where we couldn't get a value
85
+
86
+ # camera operating modes
87
+ CAMERA_MODE_VIDEO = u'video'
88
+ CAMERA_MODE_CV = u'cv'
89
+ # CAMERA_MODE_PHOTO = u'photo' # planned
90
+
91
+ # camera status
92
+ CAMERA_STATUS_OK = 'ok'
93
+ CAMERA_STATUS_PAUSED = 'paused'
94
+ CAMERA_STATUS_EOF = 'eof'
95
+
96
+ # camera API flags, these specify which API camera settings were queried with
97
+ CAMERA_API_AVFOUNDATION = u'AVFoundation' # mac
98
+ CAMERA_API_DIRECTSHOW = u'DirectShow' # windows
99
+ CAMERA_API_VIDEO4LINUX2 = u'Video4Linux2' # linux
100
+ CAMERA_API_ANY = u'Any' # any API (OpenCV only)
101
+ CAMERA_API_UNKNOWN = u'Unknown' # unknown API
102
+ CAMERA_API_NULL = u'Null' # empty field
103
+
104
+ # camera libraries for playback nad recording
105
+ CAMERA_LIB_FFPYPLAYER = u'ffpyplayer'
106
+ CAMERA_LIB_OPENCV = u'opencv'
107
+ CAMERA_LIB_UNKNOWN = u'unknown'
108
+ CAMERA_LIB_NULL = u'null'
109
+
110
+ # special values
111
+ CAMERA_FRAMERATE_NOMINAL_NTSC = '30.000030'
112
+ CAMERA_FRAMERATE_NTSC = 30.000030
113
+
114
+ # FourCC and pixel format mappings, mostly used with AVFoundation to determine
115
+ # the FFMPEG decoder which is most suitable for it. Please expand this if you
116
+ # know any more!
117
+ pixelFormatTbl = {
118
+ 'yuvs': 'yuyv422', # 4:2:2
119
+ '420v': 'nv12', # 4:2:0
120
+ '2vuy': 'uyvy422' # QuickTime 4:2:2
121
+ }
122
+
123
+ # Camera standards to help with selection. Some standalone cameras sometimes
124
+ # support an insane number of formats, this will help narrow them down.
125
+ standardResolutions = {
126
+ 'vga': (640, 480),
127
+ 'svga': (800, 600),
128
+ 'xga': (1024, 768),
129
+ 'wxga': (1280, 768),
130
+ 'wxga+': (1440, 900),
131
+ 'sxga': (1280, 1024),
132
+ 'wsxga+': (1680, 1050),
133
+ 'uxga': (1600, 1200),
134
+ 'wuxga': (1920, 1200),
135
+ 'wqxga': (2560, 1600),
136
+ 'wquxga': (3840, 2400),
137
+ '720p': (1280, 720), # also known as HD
138
+ '1080p': (1920, 1080),
139
+ '2160p': (3840, 2160),
140
+ 'uhd': (3840, 2160),
141
+ 'dci': (4096, 2160)
142
+ }
143
+
144
+ # ------------------------------------------------------------------------------
145
+ # Keep track of open capture interfaces so we can close them at shutdown in the
146
+ # event that the user forrgets or the program crashes.
147
+ #
148
+
149
+ _openCaptureInterfaces = set()
150
+
151
+
152
+ # ------------------------------------------------------------------------------
153
+ # Exceptions
154
+ #
155
+
156
+ class CameraError(Exception):
157
+ """Base class for errors around the camera."""
158
+
159
+
160
+ class CameraNotReadyError(CameraError):
161
+ """Camera is not ready."""
162
+
163
+
164
+ class CameraNotFoundError(CameraError):
165
+ """Raised when a camera cannot be found on the system."""
166
+
167
+
168
+ class CameraFormatNotSupportedError(CameraError):
169
+ """Raised when a camera cannot use the settings requested by the user."""
170
+
171
+ class CameraFrameRateNotSupportedError(CameraFormatNotSupportedError):
172
+ """Raised when a camera cannot use the frame rate settings requested by the
173
+ user."""
174
+
175
+ class CameraFrameSizeNotSupportedError(CameraFormatNotSupportedError):
176
+ """Raised when a camera cannot use the frame size settings requested by the
177
+ user."""
178
+
179
+ class FormatNotFoundError(CameraError):
180
+ """Cannot find a suitable pixel format for the camera."""
181
+
182
+
183
+ class PlayerNotAvailableError(Exception):
184
+ """Raised when a player object is not available but is required."""
185
+
186
+
187
+ # ------------------------------------------------------------------------------
188
+ # Classes
189
+ #
190
+
191
+ class CameraInfo:
192
+ """Information about a specific operating mode for a camera attached to the
193
+ system.
194
+
195
+ Parameters
196
+ ----------
197
+ index : int
198
+ Index of the camera. This is the enumeration for the camera which is
199
+ used to identify and select it by the `cameraLib`. This value may differ
200
+ between operating systems and the `cameraLib` being used.
201
+ name : str
202
+ Camera name retrieved by the OS. This may be a human-readable name
203
+ (i.e. DirectShow on Windows), an index on MacOS or a path (e.g.,
204
+ `/dev/video0` on Linux). If the `cameraLib` does not support this
205
+ feature, then this value will be generated.
206
+ frameSize : ArrayLike
207
+ Resolution of the frame `(w, h)` in pixels.
208
+ frameRate : ArrayLike
209
+ Allowable framerate for this camera mode.
210
+ pixelFormat : str
211
+ Pixel format for the stream. If `u'Null'`, then `codecFormat` is being
212
+ used to configure the camera.
213
+ codecFormat : str
214
+ Codec format for the stream. If `u'Null'`, then `pixelFormat` is being
215
+ used to configure the camera. Usually this value is used for high-def
216
+ stream formats.
217
+ cameraLib : str
218
+ Library used to access the camera. This can be either, 'ffpyplayer',
219
+ 'opencv'.
220
+ cameraAPI : str
221
+ API used to access the camera. This relates to the external interface
222
+ being used by `cameraLib` to access the camera. This value can be:
223
+ 'AVFoundation', 'DirectShow' or 'Video4Linux2'.
224
+
225
+ """
226
+ __slots__ = [
227
+ '_index',
228
+ '_name',
229
+ '_frameSize',
230
+ '_frameRate',
231
+ '_pixelFormat',
232
+ '_codecFormat',
233
+ '_cameraLib',
234
+ '_cameraAPI' # API in use, e.g. DirectShow on Windows
235
+ ]
236
+
237
+ def __init__(self,
238
+ index=-1,
239
+ name=CAMERA_NULL_VALUE,
240
+ frameSize=(-1, -1),
241
+ frameRate=-1.0,
242
+ pixelFormat=CAMERA_UNKNOWN_VALUE,
243
+ codecFormat=CAMERA_UNKNOWN_VALUE,
244
+ cameraLib=CAMERA_NULL_VALUE,
245
+ cameraAPI=CAMERA_API_NULL):
246
+
247
+ self.index = index
248
+ self.name = name
249
+ self.frameSize = frameSize
250
+ self.frameRate = frameRate
251
+ self.pixelFormat = pixelFormat
252
+ self.codecFormat = codecFormat
253
+ self.cameraLib = cameraLib
254
+ self.cameraAPI = cameraAPI
255
+
256
+ def __repr__(self):
257
+ return (f"CameraInfo(index={repr(self.index)}, "
258
+ f"name={repr(self.name)}, "
259
+ f"frameSize={repr(self.frameSize)}, "
260
+ f"frameRate={self.frameRate}, "
261
+ f"pixelFormat={repr(self.pixelFormat)}, "
262
+ f"codecFormat={repr(self.codecFormat)}, "
263
+ f"cameraLib={repr(self.cameraLib)}, "
264
+ f"cameraAPI={repr(self.cameraAPI)})")
265
+
266
+ def __str__(self):
267
+ return self.description()
268
+
269
+ @property
270
+ def index(self):
271
+ """Camera index (`int`). This is the enumerated index of this camera.
272
+ """
273
+ return self._index
274
+
275
+ @index.setter
276
+ def index(self, value):
277
+ self._index = int(value)
278
+
279
+ @property
280
+ def name(self):
281
+ """Camera name (`str`). This is the camera name retrieved by the OS.
282
+ """
283
+ return self._name
284
+
285
+ @name.setter
286
+ def name(self, value):
287
+ self._name = str(value)
288
+
289
+ @property
290
+ def frameSize(self):
291
+ """Resolution (w, h) in pixels (`ArrayLike` or `None`).
292
+ """
293
+ return self._frameSize
294
+
295
+ @frameSize.setter
296
+ def frameSize(self, value):
297
+ if value is None:
298
+ self._frameSize = None
299
+ return
300
+
301
+ assert len(value) == 2, "Value for `frameSize` must have length 2."
302
+ assert all([isinstance(i, int) for i in value]), (
303
+ "Values for `frameSize` must be integers.")
304
+
305
+ self._frameSize = value
306
+
307
+ @property
308
+ def frameRate(self):
309
+ """Frame rate (`float`) or range (`ArrayLike`).
310
+
311
+ Depends on the backend being used. If a range is provided, then the
312
+ first value is the maximum and the second value is the minimum frame
313
+ rate.
314
+ """
315
+ return self._frameRate
316
+
317
+ @frameRate.setter
318
+ def frameRate(self, value):
319
+ # assert len(value) == 2, "Value for `frameRateRange` must have length 2."
320
+ # assert all([isinstance(i, int) for i in value]), (
321
+ # "Values for `frameRateRange` must be integers.")
322
+ # assert value[0] <= value[1], (
323
+ # "Value for `frameRateRange` must be `min` <= `max`.")
324
+
325
+ self._frameRate = value
326
+
327
+ @property
328
+ def pixelFormat(self):
329
+ """Video pixel format (`str`). An empty string indicates this field is
330
+ not initialized.
331
+ """
332
+ return self._pixelFormat
333
+
334
+ @pixelFormat.setter
335
+ def pixelFormat(self, value):
336
+ self._pixelFormat = str(value)
337
+
338
+ @property
339
+ def codecFormat(self):
340
+ """Codec format, may be used instead of `pixelFormat` for some
341
+ configurations. Default is `''`.
342
+ """
343
+ return self._codecFormat
344
+
345
+ @codecFormat.setter
346
+ def codecFormat(self, value):
347
+ self._codecFormat = str(value)
348
+
349
+ @property
350
+ def cameraLib(self):
351
+ """Camera library these settings are targeted towards (`str`).
352
+ """
353
+ return self._cameraLib
354
+
355
+ @cameraLib.setter
356
+ def cameraLib(self, value):
357
+ self._cameraLib = str(value)
358
+
359
+ @property
360
+ def cameraAPI(self):
361
+ """Camera API in use to obtain this information (`str`).
362
+ """
363
+ return self._cameraAPI
364
+
365
+ @cameraAPI.setter
366
+ def cameraAPI(self, value):
367
+ self._cameraAPI = str(value)
368
+
369
+ def frameSizeAsFormattedString(self):
370
+ """Get image size as as formatted string.
371
+
372
+ Returns
373
+ -------
374
+ str
375
+ Size formatted as `'WxH'` (e.g. `'480x320'`).
376
+
377
+ """
378
+ return '{width}x{height}'.format(
379
+ width=self.frameSize[0],
380
+ height=self.frameSize[1])
381
+
382
+ def description(self):
383
+ """Get a description as a string.
384
+
385
+ For all backends, this value is guaranteed to be valid after the camera
386
+ has been opened. Some backends may be able to provide this information
387
+ before the camera is opened.
388
+
389
+ Returns
390
+ -------
391
+ str
392
+ Description of the camera format as a human readable string.
393
+
394
+ """
395
+ codecFormat = self._codecFormat
396
+ pixelFormat = self._pixelFormat
397
+ codec = codecFormat if not pixelFormat else pixelFormat
398
+
399
+ if self.frameSize is None:
400
+ frameSize = (-1, -1)
401
+ else:
402
+ frameSize = self.frameSize
403
+
404
+ return "[{name}] {width}x{height}@{frameRate}fps, {codec}".format(
405
+ #index=self.index,
406
+ name=self.name,
407
+ width=str(frameSize[0]),
408
+ height=str(frameSize[1]),
409
+ frameRate=str(self.frameRate),
410
+ codec=codec
411
+ )
412
+
413
+
414
+ class CameraDevice(BaseDevice):
415
+ """Class providing an interface with a camera attached to the system.
416
+
417
+ This interface handles the opening, closing, and reading of camera streams.
418
+
419
+ Parameters
420
+ ----------
421
+ device : Any
422
+ Camera device to open a stream with. The type of this value is dependent
423
+ on the platform and the camera library being used. This can be an integer
424
+ index, a string representing the camera device name.
425
+ captureLib : str
426
+ Camera library to use for opening the camera stream. This can be either
427
+ 'ffpyplayer' or 'opencv'. If `None`, the default recommend library is
428
+ used.
429
+ frameSize : tuple
430
+ Frame size of the camera stream. This is a tuple of the form
431
+ `(width, height)`.
432
+ frameRate : float
433
+ Frame rate of the camera stream. This is the number of frames per
434
+ second that the camera will capture. If `None`, the default frame rate
435
+ is used. The default value is 30.0.
436
+ pixelFormat : str or None
437
+ Pixel format of the camera stream. This is the format in which the
438
+ camera will capture frames. If `None`, the default pixel format is used.
439
+ The default value is `None`.
440
+ codecFormat : str or None
441
+ Codec format of the camera stream. This is the codec that will be used
442
+ to encode the camera stream. If `None`, the default codec format is
443
+ used. The default value is `None`.
444
+ captureAPI: str
445
+ Camera API to use for opening the camera stream. This can be either
446
+ 'AVFoundation', 'DirectShow', or 'Video4Linux2'. If `None`, the default
447
+ camera API is used based on the platform. The default value is `None`.
448
+ decoderOpts : dict or None
449
+ Decoder options for the camera stream. This is a dictionary of options
450
+ that will be passed to the decoder when opening the camera stream. If
451
+ `None`, the default decoder options are used. The default value is an
452
+ empty dictionary.
453
+ bufferSecs : float
454
+ Number of seconds to buffer frames from the capture stream. This allows
455
+ frames to be buffered in memory until they are needed. This allows
456
+ the camera stream to be read asynchronously and prevents frames from
457
+ being dropped if the main thread is busy. The default value is 5.0
458
+ seconds.
459
+
460
+ """
461
+ def __init__(self, device, captureLib='ffpyplayer', frameSize=(640, 480),
462
+ frameRate=30.0, pixelFormat=None, codecFormat=None,
463
+ captureAPI=None, decoderOpts=None, bufferSecs=5.0):
464
+
465
+ BaseDevice.__init__(self)
466
+
467
+ # transform some of the params
468
+ pixelFormat = pixelFormat if pixelFormat is not None else ''
469
+ codecFormat = codecFormat if codecFormat is not None else ''
470
+
471
+ # if device is an integer, get name from index
472
+ foundProfile = None
473
+ if isinstance(device, int):
474
+ for profile in self.getAvailableDevices(False):
475
+ if profile['device'] == device:
476
+ foundProfile = profile
477
+ device = profile['deviceName']
478
+ break
479
+ elif isinstance(device, str):
480
+ # if device is a string, use it as the device name
481
+ for profile in self.getAvailableDevices(False):
482
+ # find a device which best matches the settings
483
+ if profile['deviceName'] != device:
484
+ continue
485
+
486
+ # check if all the other params match
487
+ paramsMatch = all([
488
+ profile['deviceName'] == device,
489
+ profile['captureLib'] == captureLib if captureLib else True,
490
+ profile['frameSize'] == frameSize if frameSize else True,
491
+ profile['frameRate'] == frameRate if frameRate else True,
492
+ profile['pixelFormat'] == pixelFormat if pixelFormat else True,
493
+ profile['codecFormat'] == codecFormat if codecFormat else True,
494
+ profile['captureAPI'] == captureAPI if captureAPI else True
495
+ ])
496
+
497
+ if not paramsMatch:
498
+ continue
499
+
500
+ foundProfile = profile
501
+ device = profile['device']
502
+
503
+ break
504
+
505
+ if foundProfile is None:
506
+ raise CameraNotFoundError(
507
+ "Cannot find camera with index or name '{}'.".format(device))
508
+
509
+ self._device = device
510
+
511
+ # camera settings from profile
512
+ self._frameSize = foundProfile['frameSize']
513
+ self._frameRate = foundProfile['frameRate']
514
+ self._pixelFormat = foundProfile['pixelFormat']
515
+ self._codecFormat = foundProfile['codecFormat']
516
+ self._captureLib = foundProfile['captureLib']
517
+ self._captureAPI = foundProfile['captureAPI']
518
+
519
+ # capture interface
520
+ self._capture = None # camera stream capture object
521
+ self._decoderOpts = decoderOpts if decoderOpts is not None else {}
522
+ self._bufferSecs = bufferSecs # number of seconds to buffer frames
523
+ self._absRecStreamStartTime = -1.0 # absolute recording start time
524
+ self._absRecExpStartTime = -1.0
525
+
526
+ # stream properties
527
+ self._metadata = {} # metadata about the camera stream
528
+
529
+ # recording properties
530
+ self._frameStore = [] # store frames read from the camera stream
531
+ self._isRecording = False # `True` if the camera is recording and frames will be captured
532
+
533
+ # camera API to use with FFMPEG
534
+ if captureAPI is None:
535
+ if platform.system() == 'Windows':
536
+ self._cameraAPI = CAMERA_API_DIRECTSHOW
537
+ elif platform.system() == 'Darwin':
538
+ self._cameraAPI = CAMERA_API_AVFOUNDATION
539
+ elif platform.system() == 'Linux':
540
+ self._cameraAPI = CAMERA_API_VIDEO4LINUX2
541
+ else:
542
+ raise RuntimeError(
543
+ "Unsupported platform: {}. Supported platforms are: {}".format(
544
+ platform.system(), ', '.join(self._supportedPlatforms)))
545
+ else:
546
+ self._cameraAPI = captureAPI
547
+
548
+ # store device info
549
+ profile = self.getDeviceProfile()
550
+ if profile:
551
+ self.info = CameraInfo(
552
+ name=profile['deviceName'],
553
+ frameSize=profile['frameSize'],
554
+ frameRate=profile['frameRate'],
555
+ pixelFormat=profile['pixelFormat'],
556
+ codecFormat=profile['codecFormat'],
557
+ cameraLib=profile['captureLib'],
558
+ cameraAPI=profile['captureAPI']
559
+ )
560
+ else:
561
+ self.info = CameraInfo()
562
+
563
+ def isSameDevice(self, other):
564
+ """
565
+ Determine whether this object represents the same physical device as a given other object.
566
+
567
+ Parameters
568
+ ----------
569
+ other : BaseDevice, dict
570
+ Other device object to compare against, or a dict of params.
571
+
572
+ Returns
573
+ -------
574
+ bool
575
+ True if the two objects represent the same physical device
576
+ """
577
+ if isinstance(other, CameraDevice):
578
+ return other._device == self._device
579
+ elif isinstance(other, Camera):
580
+ return getattr(other, "_capture", None) == self
581
+ elif isinstance(other, dict) and "device" in other:
582
+ return other['deviceName'] == self._device
583
+ else:
584
+ return False
585
+
586
+ @staticmethod
587
+ def getAvailableDevices(best=True):
588
+ """
589
+ Get all available devices of this type.
590
+
591
+ Parameters
592
+ ----------
593
+ best : bool
594
+ If True, return only the best available frame rate/resolution for each device, rather
595
+ than returning all. Best available spec is chosen as the highest resolution with a
596
+ frame rate above 30fps (or just highest resolution, if none are over 30fps).
597
+
598
+ Returns
599
+ -------
600
+ list[dict]
601
+ List of dictionaries containing the parameters needed to initialise each device.
602
+ """
603
+ profiles = []
604
+ # iterate through cameras
605
+ for cams in CameraDevice.getCameras().values():
606
+ # if requested, filter for best spec for each device
607
+ if best:
608
+ allCams = cams.copy()
609
+ lastBest = {
610
+ 'pixels': 0,
611
+ 'frameRate': 0
612
+ }
613
+ bestResolution = None
614
+ minFrameRate = max(28, min([cam.frameRate for cam in allCams]))
615
+ for cam in allCams:
616
+ # summarise spec of this cam
617
+ current = {
618
+ 'pixels': cam.frameSize[0] * cam.frameSize[1],
619
+ 'frameRate': cam.frameRate
620
+ }
621
+ # store best frame rate as a fallback
622
+ if bestResolution is None or current['pixels'] > lastBest['pixels']:
623
+ bestResolution = cam
624
+ # if it's better than the last, set it as the only cam
625
+ if current['pixels'] > lastBest['pixels'] and current['frameRate'] >= minFrameRate:
626
+ cams = [cam]
627
+ # if no cameras meet frame rate requirement, use one with best resolution
628
+ cams = [bestResolution]
629
+ # iterate through all (possibly filtered) cameras
630
+ for cam in cams:
631
+ # construct a dict profile from the CameraInfo object
632
+ profiles.append({
633
+ 'deviceName': cam.name,
634
+ 'deviceClass': "psychopy.hardware.camera.CameraDevice",
635
+ 'device': cam.index,
636
+ 'captureLib': cam.cameraLib,
637
+ 'frameSize': cam.frameSize,
638
+ 'frameRate': cam.frameRate,
639
+ 'pixelFormat': cam.pixelFormat,
640
+ 'codecFormat': cam.codecFormat,
641
+ 'captureAPI': cam.cameraAPI
642
+ })
643
+
644
+ return profiles
645
+
646
+ @staticmethod
647
+ def getCameras(cameraLib=None):
648
+ """Get a list of devices this interface can open.
649
+
650
+ Parameters
651
+ ----------
652
+ cameraLib : str or None
653
+ Camera library to use for opening the camera stream. This can be
654
+ either 'ffpyplayer' or 'opencv'. If `None`, the default recommend
655
+ library is used.
656
+
657
+ Returns
658
+ -------
659
+ dict
660
+ List of objects which represent cameras that can be opened by this
661
+ interface. Pass any of these values to `device` to open a stream.
662
+
663
+ """
664
+ if cameraLib is None:
665
+ cameraLib = CAMERA_LIB_FFPYPLAYER
666
+
667
+ if cameraLib == CAMERA_LIB_FFPYPLAYER:
668
+ global _cameraGetterFuncTbl
669
+ systemName = platform.system() # get the system name
670
+
671
+ # lookup the function for the given platform
672
+ getCamerasFunc = _cameraGetterFuncTbl.get(systemName, None)
673
+ if getCamerasFunc is None: # if unsupported
674
+ raise OSError(
675
+ "Cannot get cameras, unsupported platform '{}'.".format(
676
+ systemName))
677
+
678
+ return getCamerasFunc()
679
+
680
+ def _clearFrameStore(self):
681
+ """Clear the frame store.
682
+ """
683
+ self._frameStore.clear()
684
+
685
+ @property
686
+ def device(self):
687
+ """Camera device this interface is using (`Any`).
688
+
689
+ This is the camera device that was passed to the constructor. It may be
690
+ a `CameraInfo` object or a string representing the camera device.
691
+
692
+ """
693
+ return self._device
694
+
695
+ @property
696
+ def cameraLib(self):
697
+ """Camera library this interface is using (`str`).
698
+
699
+ This is the camera library that was passed to the constructor. It may be
700
+ 'ffpyplayer' or 'opencv'. If `None`, the default recommend library is
701
+ used.
702
+
703
+ """
704
+ return self.info.captureLib if self.info else None
705
+
706
+ @property
707
+ def frameSize(self):
708
+ """Frame size of the camera stream (`tuple`).
709
+
710
+ This is the frame size of the camera stream. It is a tuple of the form
711
+ `(width, height)`. If the camera stream is not open, this will return
712
+ `None`.
713
+
714
+ """
715
+ return self.info.frameSize if self.info else None
716
+
717
+ @property
718
+ def frameRate(self):
719
+ """Frame rate of the camera stream (`float`).
720
+
721
+ This is the frame rate of the camera stream. If the camera stream is
722
+ not open, this will return `None`.
723
+
724
+ """
725
+ return self.info.frameRate if self.info else None
726
+
727
+ @property
728
+ def frameInterval(self):
729
+ """Frame interval of the camera stream (`float`).
730
+
731
+ This is the time between frames in seconds. It is calculated as
732
+ `1.0 / frameRate`. If the camera stream is not open, this will return
733
+ `None`.
734
+
735
+ """
736
+ return self._frameInterval
737
+
738
+ @property
739
+ def pixelFormat(self):
740
+ """Pixel format of the camera stream (`str`).
741
+
742
+ This is the pixel format of the camera stream. If the camera stream is
743
+ not open, this will return `None`.
744
+
745
+ """
746
+ return self.info.pixelFormat if self.info else None
747
+
748
+ @property
749
+ def codecFormat(self):
750
+ """Codec format of the camera stream (`str`).
751
+
752
+ This is the codec format of the camera stream. If the camera stream is
753
+ not open, this will return `None`.
754
+
755
+ """
756
+ return self.info.codecFormat if self.info else None
757
+
758
+ @property
759
+ def cameraAPI(self):
760
+ """Camera API used to access the camera stream (`str`).
761
+
762
+ This is the camera API used to access the camera stream. If the camera
763
+ stream is not open, this will return `None`.
764
+
765
+ """
766
+ return self.info.cameraAPI if self.info else None
767
+
768
+ @property
769
+ def bufferSecs(self):
770
+ """Number of seconds to buffer frames from the camera stream (`float`).
771
+
772
+ This is the number of seconds to buffer frames from the camera stream.
773
+ This allows frames to be buffered in memory until they are needed. This
774
+ allows the camera stream to be read asynchronously and prevents frames
775
+ from being dropped if the main thread is busy.
776
+
777
+ """
778
+ return self._bufferSecs
779
+
780
+ def getMetadata(self):
781
+ """Get metadata about the camera stream.
782
+
783
+ Returns
784
+ -------
785
+ dict
786
+ Dictionary containing metadata about the camera stream. Returns an
787
+ empty dictionary if no metadata is available.
788
+
789
+ """
790
+ if self._capture is None:
791
+ return {}
792
+
793
+ # get metadata from the capture stream
794
+ return self._capture.get_metadata() if self._capture else {}
795
+
796
+ @property
797
+ def frameSizeBytes(self):
798
+ """Size of the image in bytes (`int`).
799
+
800
+ This is the size of the image in bytes. It is calculated as
801
+ `width * height * 3`, where `width` and `height` are the dimensions of
802
+ the camera stream. If the camera stream is not open, this will return
803
+ `0`.
804
+
805
+ """
806
+ if self._frameSize is None:
807
+ return 0
808
+
809
+ return self._frameSizeBytes
810
+
811
+ @property
812
+ def frameCount(self):
813
+ """Number of frames read from the camera stream (`int`).
814
+
815
+ This is the number of frames read from the camera stream since the last
816
+ time the camera was opened. If the camera stream is not open, this will
817
+ return `0`.
818
+
819
+ """
820
+ return self._frameCount
821
+
822
+ @property
823
+ def streamTime(self):
824
+ """Current stream time in seconds (`float`).
825
+
826
+ This is the current stream time in seconds. It is calculated as the
827
+ difference between the current time and the absolute recording start
828
+ time. If the camera stream is not open, this will return `-1.0`.
829
+
830
+ """
831
+ if self._cameraAPI == CAMERA_API_AVFOUNDATION:
832
+ return time.time() if self._capture is not None else -1.0
833
+ else:
834
+ return self._capture.get_pts() if self._capture is not None else -1.0
835
+
836
+ def _toNumpyView(self, frame):
837
+ """Convert a frame to a Numpy view.
838
+
839
+ This function converts a frame to a Numpy view. The frame is returned as
840
+ a Numpy array. The resulting array will be in the correct format to
841
+ upload to OpenGL as a texture.
842
+
843
+ Parameters
844
+ ----------
845
+ frame : Any
846
+ The frame to convert.
847
+
848
+ Returns
849
+ -------
850
+ numpy.ndarray
851
+ The converted frame in RGB format.
852
+
853
+ """
854
+ return np.asarray(frame, dtype=np.uint8)
855
+
856
+ # --------------------------------------------------------------------------
857
+ # Platform-specific camera frame aquisition methods
858
+ #
859
+ # These methods are used to open, close, and read frames from the camera
860
+ # stream. They are platform-specific and are called depending on the
861
+ # camera library being used.
862
+ #
863
+
864
+ # --------------------------------------------------------------------------
865
+ # FFPyPlayer-specific methods
866
+ #
867
+
868
+ def _openFFPyPlayer(self):
869
+ """Open the camera stream using FFmpeg (ffpyplayer).
870
+
871
+ This method should be called to open the camera stream using FFmpeg.
872
+ It should initialize the camera and prepare it for reading frames.
873
+
874
+ """
875
+ # configure the camera stream reader
876
+ ff_opts = {} # ffmpeg options
877
+ lib_opts = {} # ffpyplayer options
878
+ _camera = CAMERA_NULL_VALUE
879
+ _frameRate = CAMERA_NULL_VALUE
880
+
881
+ # setup commands for FFMPEG
882
+ if self._captureAPI == CAMERA_API_DIRECTSHOW: # windows
883
+ ff_opts['f'] = 'dshow'
884
+ _camera = 'video={}'.format(self.info.name)
885
+ _frameRate = self._frameRate
886
+ if self._pixelFormat:
887
+ ff_opts['pixel_format'] = self._pixelFormat
888
+ if self._codecFormat:
889
+ ff_opts['vcodec'] = self._codecFormat
890
+ elif self._captureAPI == CAMERA_API_AVFOUNDATION: # darwin
891
+ ff_opts['f'] = 'avfoundation'
892
+ ff_opts['i'] = _camera = self._device
893
+
894
+ # handle pixel formats using FourCC
895
+ global pixelFormatTbl
896
+ ffmpegPixFmt = pixelFormatTbl.get(self._pixelFormat, None)
897
+
898
+ if ffmpegPixFmt is None:
899
+ raise FormatNotFoundError(
900
+ "Cannot find suitable FFMPEG pixel format for '{}'. Try a "
901
+ "different format or camera.".format(
902
+ self._pixelFormat))
903
+
904
+ self._pixelFormat = ffmpegPixFmt
905
+
906
+ # this needs to be exactly specified if using NTSC
907
+ if math.isclose(CAMERA_FRAMERATE_NTSC, self._frameRate):
908
+ _frameRate = CAMERA_FRAMERATE_NOMINAL_NTSC
909
+ else:
910
+ _frameRate = str(self._frameRate)
911
+
912
+ # need these since hardware acceleration is not possible on Mac yet
913
+ lib_opts['fflags'] = 'nobuffer'
914
+ lib_opts['flags'] = 'low_delay'
915
+ lib_opts['pixel_format'] = self._pixelFormat
916
+ lib_opts['use_wallclock_as_timestamps'] = '1'
917
+ # ff_opts['framedrop'] = True
918
+ # ff_opts['fast'] = True
919
+ elif self._captureAPI == CAMERA_API_VIDEO4LINUX2:
920
+ raise OSError(
921
+ "Sorry, camera does not support Linux at this time. However, "
922
+ "it will in future versions.")
923
+
924
+ else:
925
+ raise RuntimeError("Unsupported camera API specified.")
926
+
927
+ # set library options
928
+ camWidth, camHeight = self._frameSize
929
+ logging.debug(
930
+ "Using camera mode {}x{} at {} fps".format(
931
+ camWidth, camHeight, _frameRate))
932
+
933
+ # configure the real-time buffer size, we compute using RGB8 since this
934
+ # is uncompressed and represents the largest size we can expect
935
+ self._frameSizeBytes = int(camWidth * camHeight * 3)
936
+ framesToBufferCount = int(self._bufferSecs * self._frameRate)
937
+ _bufferSize = int(self._frameSizeBytes * framesToBufferCount)
938
+ logging.debug(
939
+ "Setting real-time buffer size to {} bytes "
940
+ "for {} seconds of video ({} frames @ {} fps)".format(
941
+ _bufferSize,
942
+ self._bufferSecs,
943
+ framesToBufferCount,
944
+ self._frameRate)
945
+ )
946
+
947
+ # common settings across libraries
948
+ ff_opts['low_delay'] = True # low delay for real-time playback
949
+ # ff_opts['framedrop'] = True
950
+ # ff_opts['use_wallclock_as_timestamps'] = True
951
+ ff_opts['fast'] = True
952
+ # ff_opts['sync'] = 'ext'
953
+ ff_opts['rtbufsize'] = str(_bufferSize) # set the buffer size
954
+ ff_opts['an'] = True
955
+ # ff_opts['infbuf'] = True # enable infinite buffering
956
+
957
+ # for ffpyplayer, we need to set the video size and framerate
958
+ lib_opts['video_size'] = '{width}x{height}'.format(
959
+ width=camWidth, height=camHeight)
960
+ lib_opts['framerate'] = str(_frameRate)
961
+ ff_opts['loglevel'] = 'error'
962
+ ff_opts['nostdin'] = True
963
+
964
+ # open the media player
965
+ from ffpyplayer.player import MediaPlayer
966
+ self._capture = MediaPlayer(
967
+ _camera,
968
+ ff_opts=ff_opts,
969
+ lib_opts=lib_opts)
970
+
971
+ # compute the frame interval, needed for generating timestamps
972
+ self._frameInterval = 1.0 / self._frameRate
973
+
974
+ # get metadata from the capture stream
975
+ tStart = time.time() # start time for the stream
976
+ metadataTimeout = 5.0 # timeout for metadata retrieval
977
+ while time.time() - tStart < metadataTimeout: # wait for metadata
978
+ streamMetadata = self._capture.get_metadata()
979
+ if streamMetadata['src_vid_size'] != (0, 0):
980
+ break
981
+ time.sleep(0.001) # wait for metadata to be available
982
+ else:
983
+ msg = (
984
+ "Failed to obtain stream metadata (possibly caused by a device "
985
+ "already in use by other application)."
986
+ )
987
+ logging.error(msg)
988
+ raise CameraNotReadyError(msg)
989
+
990
+ self._metadata = streamMetadata # store the metadata for later use
991
+
992
+ # check if the camera metadata matches the requested settings
993
+ if streamMetadata['src_vid_size'] != tuple(self._frameSize):
994
+ raise CameraFrameSizeNotSupportedError(
995
+ "Camera does not support the requested frame size "
996
+ "{size}. Supported sizes are: {supportedSizes}".format(
997
+ size=self._frameSize,
998
+ supportedSizes=streamMetadata['src_vid_size']))
999
+
1000
+ # pause the camera stream
1001
+ self._capture.set_pause(True)
1002
+
1003
+ def _closeFFPyPlayer(self):
1004
+ """Close the camera stream opened with FFmpeg (ffpyplayer).
1005
+
1006
+ This method should be called to close the camera stream and release any
1007
+ resources associated with it.
1008
+
1009
+ """
1010
+ if self._capture is not None:
1011
+ # self._capture.set_pause(True) # pause the stream
1012
+ self._capture.close_player()
1013
+
1014
+ def _getFramesFFPyPlayer(self):
1015
+ """Get the most recent frames from the camera stream opened with FFmpeg
1016
+ (ffpyplayer).
1017
+
1018
+ Returns
1019
+ -------
1020
+ numpy.ndarray
1021
+ Most recent frames from the camera stream. Returns `None` if no
1022
+ frames are available.
1023
+
1024
+ """
1025
+ if self._capture is None:
1026
+ raise PlayerNotAvailableError(
1027
+ "Camera stream is not open. Call `open()` first.")
1028
+
1029
+ # read all buffered frames from the camera stream until we get nothing
1030
+ recentFrames = []
1031
+ while 1:
1032
+ frame, status = self._capture.get_frame()
1033
+
1034
+ if status == CAMERA_STATUS_EOF or status == CAMERA_STATUS_PAUSED:
1035
+ break
1036
+
1037
+ if frame is None: # ditto
1038
+ break
1039
+
1040
+ img, curPts = frame
1041
+ if curPts < self._absRecStreamStartTime and self._isRecording:
1042
+ del img # free the memory used by the frame
1043
+ # if the frame is before the recording start time, skip it
1044
+ continue
1045
+
1046
+ self._frameCount += 1 # increment the frame count
1047
+
1048
+ recentFrames.append((
1049
+ img,
1050
+ curPts-self._absRecStreamStartTime,
1051
+ curPts))
1052
+
1053
+ return recentFrames
1054
+
1055
+ # --------------------------------------------------------------------------
1056
+ # OpenCV-specific methods
1057
+ #
1058
+
1059
+ def _convertFrameToRGBOpenCV(self, frame):
1060
+ """Convert a frame to RGB format using OpenCV.
1061
+
1062
+ This function converts a frame to RGB format. The frame is returned as
1063
+ a Numpy array. The resulting array will be in the correct format to
1064
+ upload to OpenGL as a texture.
1065
+
1066
+ Parameters
1067
+ ----------
1068
+ frame : numpy.ndarray
1069
+ The frame to convert.
1070
+
1071
+ Returns
1072
+ -------
1073
+ numpy.ndarray
1074
+ The converted frame in RGB format.
1075
+
1076
+ """
1077
+ import cv2
1078
+
1079
+ # this can be done in the shader to save CPU use, will figure out later
1080
+ return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
1081
+
1082
+ def _openOpenCV(self):
1083
+ """Open the camera stream using OpenCV.
1084
+
1085
+ This method should be called to open the camera stream using OpenCV.
1086
+ It should initialize the camera and prepare it for reading frames.
1087
+
1088
+ """
1089
+ pass
1090
+
1091
+ def _closeOpenCV(self):
1092
+ """Close the camera stream opened with OpenCV.
1093
+
1094
+ This method should be called to close the camera stream and release any
1095
+ resources associated with it.
1096
+
1097
+ """
1098
+ pass
1099
+
1100
+ def _getFramesOpenCV(self):
1101
+ """Get the most recent frames from the camera stream opened with OpenCV.
1102
+
1103
+ Returns
1104
+ -------
1105
+ numpy.ndarray
1106
+ Most recent frames from the camera stream. Returns `None` if no
1107
+ frames are available.
1108
+
1109
+ """
1110
+ if self._capture is None:
1111
+ raise PlayerNotAvailableError(
1112
+ "Camera stream is not open. Call `open()` first.")
1113
+
1114
+ pass
1115
+
1116
+ # --------------------------------------------------------------------------
1117
+ # Public methods for camera stream management
1118
+ #
1119
+
1120
+ def __hash__(self):
1121
+ """Hash on the camera device name and library used."""
1122
+ return hash((self._device, self._captureLib))
1123
+
1124
+ def open(self):
1125
+ """Open the camera stream.
1126
+
1127
+ This method should be called to open the camera stream. It should
1128
+ initialize the camera and prepare it for reading frames.
1129
+
1130
+ """
1131
+ if self._captureLib == 'ffpyplayer':
1132
+ self._openFFPyPlayer()
1133
+
1134
+ global _openCaptureInterfaces
1135
+ _openCaptureInterfaces.add(self)
1136
+
1137
+ def close(self):
1138
+ """Close the camera stream.
1139
+
1140
+ This method should be called to close the camera stream and release any
1141
+ resources associated with it.
1142
+
1143
+ """
1144
+ if self.isRecording:
1145
+ self.stop() # stop the recording if it is in progress
1146
+ logging.warning(
1147
+ "CameraDevice.close() called while recording. Stopping.")
1148
+
1149
+ if self._captureLib == 'ffpyplayer':
1150
+ self._closeFFPyPlayer()
1151
+
1152
+ self._capture = None # reset the capture object
1153
+
1154
+ global _openCaptureInterfaces
1155
+ if self in _openCaptureInterfaces:
1156
+ _openCaptureInterfaces.remove(self)
1157
+
1158
+ @property
1159
+ def isOpen(self):
1160
+ """Check if the camera stream is open.
1161
+
1162
+ Returns
1163
+ -------
1164
+ bool
1165
+ `True` if the camera stream is open, `False` otherwise.
1166
+
1167
+ """
1168
+ return self._capture is not None
1169
+
1170
+ def record(self):
1171
+ """Start recording camera frames to memory.
1172
+
1173
+ This method should be called to start recording the camera stream.
1174
+ Frame timestamps will be generated based on the current time when
1175
+ this method is called. The frames will be stored and made available
1176
+ through the `getFrames()` method.
1177
+
1178
+ To get precise audio synchronization:
1179
+
1180
+ 1. Start the microphone recording
1181
+ 2. Store samples somehwere keeping track of the absolute time of the
1182
+ first audio sample.
1183
+ 3. Call this method to start the camera recording and store the
1184
+ returned start time.
1185
+ 4. When the recording is stopped, compute the offset between the
1186
+ absolute start time of the audio recording and the absolute start
1187
+ time of the camera recording. Compute the postion of the first
1188
+ audio sample in the audio buffer by multiplying the offset by the
1189
+ sample rate of the audio recording. This will give you the
1190
+ position of the first audio sample in the audio buffer
1191
+ corresponding to the very beginning of the first camera frame.
1192
+
1193
+ Returns
1194
+ -------
1195
+ float
1196
+ The absolute start time of the recording in seconds. Use this value
1197
+ to syncronize audio recording with the capture stream.
1198
+
1199
+ """
1200
+ if not self.isOpen:
1201
+ raise RuntimeError("Camera stream is not open. Call `open()` first.")
1202
+
1203
+ self._frameCount = 0 # reset the frame count
1204
+ self._clearFrameStore() # clear the frame store
1205
+ self._capture.set_pause(False) # start the capture stream
1206
+
1207
+ # need to use a different timebase on macOS, due to a bug
1208
+ if self._cameraAPI == CAMERA_API_AVFOUNDATION:
1209
+ self._absRecStreamStartTime = time.time()
1210
+ else:
1211
+ self._absRecStreamStartTime = self._capture.get_pts() # get the absolute start time
1212
+
1213
+ self._absRecExpStartTime = core.getTime() # experiment start time in seconds
1214
+ self._isRecording = True
1215
+
1216
+ return self._absRecStreamStartTime
1217
+
1218
+ def start(self):
1219
+ """Start recording the camera stream.
1220
+
1221
+ Alias for `record()`. This method is provided for compatibility with
1222
+ other camera interfaces that may use `start()` to begin recording.
1223
+
1224
+ """
1225
+ return self.record() # start recording and return the start time
1226
+
1227
+ def stop(self):
1228
+ """Stop recording the camera stream.
1229
+
1230
+ This method should be called to stop recording the camera stream. It
1231
+ will stop capturing frames from the camera and clear the frame store.
1232
+
1233
+ """
1234
+ self._capture.set_pause(True) # pause the capture stream
1235
+
1236
+ if self._cameraAPI == CAMERA_API_AVFOUNDATION:
1237
+ absStopTime = time.time()
1238
+ else:
1239
+ absStopTime = self._capture.get_pts()
1240
+
1241
+ self._isRecording = False
1242
+
1243
+ return absStopTime
1244
+
1245
+ @property
1246
+ def isRecording(self):
1247
+ """Check if the camera stream is currently recording (`bool`).
1248
+
1249
+ Returns
1250
+ -------
1251
+ bool
1252
+ `True` if the camera stream is currently recording, `False`
1253
+ otherwise.
1254
+
1255
+ """
1256
+ return self._isRecording
1257
+
1258
+ def getFrames(self):
1259
+ """Get the most recent frames from the camera stream.
1260
+
1261
+ This method returns frame captured since the last call to this method.
1262
+ If no frames are available or `record()` has not been previously called,
1263
+ it returns an empty list.
1264
+
1265
+ You must call this method periodically at an interval of at least
1266
+ `bufferSecs` seconds or risk losing frames.
1267
+
1268
+ Returns
1269
+ -------
1270
+ list
1271
+ List of frames from the camera stream. Returns an empty list if no
1272
+ frames are available.
1273
+
1274
+ """
1275
+ if self._captureLib == 'ffpyplayer':
1276
+ return self._getFramesFFPyPlayer()
1277
+
1278
+
1279
+ # class name alias for legacy support
1280
+ CameraInterface = CameraDevice
1281
+
1282
+
1283
+ # keep track of camera devices that are opened
1284
+ _openCameras = {}
1285
+
1286
+
1287
+ class Camera:
1288
+ """Class for displaying and recording video from a USB/PCI connected camera.
1289
+
1290
+ This class is capable of opening, recording, and saving camera video streams
1291
+ to disk. Camera stream reading/writing is done in a separate thread,
1292
+ allowing capture to occur in the background while the main thread is free to
1293
+ perform other tasks. This allows for capture to occur at higher frame rates
1294
+ than the display refresh rate. Audio recording is also supported if a
1295
+ microphone interface is provided, where recording will be synchronized with
1296
+ the video stream (as best as possible). Video and audio can be saved to disk
1297
+ either as a single file or as separate files.
1298
+
1299
+ GNU/Linux is supported only by the OpenCV backend (`cameraLib='opencv'`).
1300
+
1301
+ Parameters
1302
+ ----------
1303
+ device : str or int
1304
+ Camera to open a stream with. If the ID is not valid, an error will be
1305
+ raised when `open()` is called. Value can be a string or number. String
1306
+ values are platform-dependent: a DirectShow URI or camera name on
1307
+ Windows, or a camera name/index on MacOS. Specifying a number (>=0) is a
1308
+ platform-independent means of selecting a camera. PsychoPy enumerates
1309
+ possible camera devices and makes them selectable without explicitly
1310
+ having the name of the cameras attached to the system. Use caution when
1311
+ specifying an integer, as the same index may not reference the same
1312
+ camera every time.
1313
+ mic : :class:`~psychopy.sound.microphone.Microphone` or None
1314
+ Microphone to record audio samples from during recording. The microphone
1315
+ input device must not be in use when `record()` is called. The audio
1316
+ track will be merged with the video upon calling `save()`. Make sure
1317
+ that `Microphone.maxRecordingSize` is specified to a reasonable value to
1318
+ prevent the audio track from being truncated. Specifying a microphone
1319
+ adds some latency to starting and stopping camera recording due to the
1320
+ added overhead involved with synchronizing the audio and video streams.
1321
+ frameRate : int or None
1322
+ Frame rate to record the camera stream at. If `None`, the camera's
1323
+ default frame rate will be used.
1324
+ frameSize : tuple or None
1325
+ Size (width, height) of the camera stream frames to record. If `None`,
1326
+ the camera's default frame size will be used.
1327
+ cameraLib : str
1328
+ Interface library (backend) to use for accessing the camera. May either
1329
+ be `ffpyplayer` or `opencv`. If `None`, the default library for the
1330
+ recommended by the PsychoPy developers will be used. Switching camera
1331
+ libraries could help resolve issues with camera compatibility. More
1332
+ camera libraries may be installed via extension packages.
1333
+ bufferSecs : float
1334
+ Size of the real-time camera stream buffer specified in seconds. This
1335
+ will tell the library to allocate a buffer that can hold enough
1336
+ frames to cover the specified number of seconds of video. This should
1337
+ be large enough to cover the time it takes to process frames in the
1338
+ main thread.
1339
+ win : :class:`~psychopy.visual.Window` or None
1340
+ Optional window associated with this camera. Some functionality may
1341
+ require an OpenGL context for presenting frames to the screen. If you
1342
+ are not planning to display the camera stream, this parameter can be
1343
+ safely ignored.
1344
+ name : str
1345
+ Label for the camera for logging purposes.
1346
+ keepFrames : int
1347
+ Number of frames to keep in memory for the camera stream. Calling
1348
+ `getVideoFrames()` will return the most recent `keepFrames` frames from
1349
+ the camera stream. If `keepFrames` is set to `0`, no frames will be kept
1350
+ in memory and the camera stream will not be buffered. This is useful if
1351
+ the user desires to access raw frame data from the camera stream.
1352
+ latencyBias : float
1353
+ Latency bias to correct for asychrony between the camera and the
1354
+ microphone. This is the amount of time in seconds to add to the
1355
+ microphone recording start time to shift the audio track to match
1356
+ corresponding events in the video stream. This is needed for some
1357
+ cameras whose drivers do not accurately report timestamps for camera
1358
+ frames. Positive values will shift the audio track forward in time, and
1359
+ negative values will shift backwards.
1360
+ usageMode : str
1361
+ Usage mode hint for the camera aquisition. This with enable
1362
+ optimizations for specific applications that will improve performance
1363
+ and reduce memory usage. The default value is 'video', which is suitable
1364
+ for recording video streams with audio efficently. The 'cv' mode is for
1365
+ computer vision applications where frames from the camera stream are
1366
+ processed in real-time (e.g. object detection, tracking, etc.) and the
1367
+ video is not being saved to disk. Audio will not be recorded in this
1368
+ mode even if a microphone is provided.
1369
+
1370
+ Examples
1371
+ --------
1372
+ Opening a camera stream and closing it::
1373
+
1374
+ camera = Camera(device=0)
1375
+ camera.open() # exception here on invalid camera
1376
+ camera.close()
1377
+
1378
+ Recording 5 seconds of video and saving it to disk::
1379
+
1380
+ cam = Camera(0)
1381
+ cam.open()
1382
+ cam.record() # starts recording
1383
+
1384
+ while cam.recordingTime < 5.0: # record for 5 seconds
1385
+ if event.getKeys('q'):
1386
+ break
1387
+ cam.update()
1388
+
1389
+ cam.stop() # stops recording
1390
+ cam.save('myVideo.mp4')
1391
+ cam.close()
1392
+
1393
+ Providing a microphone as follows enables audio recording::
1394
+
1395
+ mic = Microphone(0)
1396
+ cam = Camera(0, mic=mic)
1397
+
1398
+ Overriding the default frame rate and size (if `cameraLib` supports it)::
1399
+
1400
+ cam = Camera(0, frameRate=30, frameSize=(640, 480), cameraLib=u'opencv')
1401
+
1402
+ """
1403
+ def __init__(self, device=0, mic=None, cameraLib=u'ffpyplayer',
1404
+ frameRate=None, frameSize=None, bufferSecs=4, win=None,
1405
+ name='cam', keepFrames=5, usageMode='video'):
1406
+ # add attributes for setters
1407
+ self.__dict__.update(
1408
+ {'_device': None,
1409
+ '_captureThread': None,
1410
+ '_mic': None,
1411
+ '_outFile': None,
1412
+ '_mode': u'video',
1413
+ '_frameRate': None,
1414
+ '_frameRateFrac': None,
1415
+ '_frameSize': None,
1416
+ '_size': None,
1417
+ '_cameraLib': u''})
1418
+
1419
+ self._cameraLib = cameraLib
1420
+
1421
+ # handle device
1422
+ self._capture = None
1423
+ if isinstance(device, CameraDevice):
1424
+ # if given a device object, use it
1425
+ self._capture = device
1426
+ elif device is None:
1427
+ # if given None, get the first available device
1428
+ for name, obj in DeviceManager.getInitialisedDevices(CameraDevice).items():
1429
+ self._capture = obj
1430
+ break
1431
+ # if there are none, set one up
1432
+ if self._capture is None:
1433
+ for profile in CameraDevice.getAvailableDevices():
1434
+ self._capture = DeviceManager.addDevice(**profile)
1435
+ break
1436
+ elif isinstance(device, str):
1437
+ if DeviceManager.getDevice(device):
1438
+ self._capture = DeviceManager.getDevice(device)
1439
+ else:
1440
+ # get available devices
1441
+ availableDevices = CameraDevice.getAvailableDevices()
1442
+ # if given a device name, try to find it
1443
+ for profile in availableDevices:
1444
+ if profile['deviceName'] != device:
1445
+ continue
1446
+ paramsMatch = all([
1447
+ profile.get(key) == value
1448
+ for key, value in {
1449
+ 'deviceName': device,
1450
+ 'captureLib': cameraLib,
1451
+ 'frameRate': frameRate if frameRate is not None else True, # get first
1452
+ 'frameSize': frameSize if frameSize is not None else True
1453
+ }.items() if value is not None
1454
+ ])
1455
+ if not paramsMatch:
1456
+ continue
1457
+
1458
+ device = profile['device']
1459
+ break
1460
+
1461
+ # anything else, try to initialise a new device from params
1462
+ self._capture = CameraDevice(
1463
+ device=device,
1464
+ captureLib=cameraLib,
1465
+ frameRate=frameRate,
1466
+ frameSize=frameSize,
1467
+ pixelFormat=None, # use default pixel format
1468
+ codecFormat=None, # use default codec format
1469
+ captureAPI=None # use default capture API
1470
+ )
1471
+ else:
1472
+ # anything else, try to initialise a new device from params
1473
+ self._capture = CameraDevice(
1474
+ device=device,
1475
+ captureLib=cameraLib,
1476
+ frameRate=frameRate,
1477
+ frameSize=frameSize,
1478
+ pixelFormat=None, # use default pixel format
1479
+ codecFormat=None, # use default codec format
1480
+ captureAPI=None # use default capture API
1481
+ )
1482
+ # from here on in the init, use the device index as `device`
1483
+ device = self._capture.device
1484
+ # get info from device
1485
+ self._cameraInfo = self._capture.info
1486
+
1487
+ # handle microphone
1488
+ self.mic = None
1489
+ if isinstance(mic, MicrophoneDevice):
1490
+ # if given a device object, use it
1491
+ self.mic = mic
1492
+ elif isinstance(mic, Microphone):
1493
+ # if given a Microphone, use its device
1494
+ self.mic = mic.device
1495
+ elif mic is None:
1496
+ # if given None, get the first available device
1497
+ for name, obj in DeviceManager.getInitialisedDevices(MicrophoneDevice).items():
1498
+ self.mic = obj
1499
+ break
1500
+ # if there are none, set one up
1501
+ if self.mic is None:
1502
+ for profile in MicrophoneDevice.getAvailableDevices():
1503
+ self.mic = DeviceManager.addDevice(**profile)
1504
+ break
1505
+ elif isinstance(mic, str) and DeviceManager.getDevice(mic) is not None:
1506
+ # if given a device name, get the device
1507
+ self.mic = DeviceManager.getDevice(mic)
1508
+ else:
1509
+ # anything else, try to initialise a new device from params
1510
+ self.mic = MicrophoneDevice(
1511
+ index=mic
1512
+ )
1513
+
1514
+ # current camera frame since the start of recording
1515
+ self.status = NOT_STARTED
1516
+ self._bufferSecs = float(bufferSecs)
1517
+ self._lastFrame = None # use None to avoid imports for ImageStim
1518
+ self._keepFrames = keepFrames # number of frames to keep in memory
1519
+ self._frameCount = 0 # number of frames read from the camera stream
1520
+ self._frameStore = collections.deque(maxlen=keepFrames)
1521
+ self._usageMode = usageMode # usage mode for the camera
1522
+ self._unsaved = False # is there any footage not saved?
1523
+
1524
+ # other information
1525
+ self.name = name
1526
+ # timestamp data
1527
+ self._streamTime = 0.0
1528
+ # store win (unused but needs to be set/got safely for parity with JS)
1529
+ self._win = None
1530
+
1531
+ # recording properties
1532
+ self._isStarted = False # is the stream started?
1533
+ self._audioReady = False
1534
+ self._videoReady = False
1535
+
1536
+ self._latencyBias = 0.0 # latency bias in seconds
1537
+
1538
+ self._absVideoRecStartTime = -1.0
1539
+ self._absVideoRecStopTime = -1.0
1540
+ self._absAudioRecStartTime = -1.0
1541
+ self._absAudioRecStopTime = -1.0
1542
+
1543
+ # computed timestamps for when
1544
+ self._absAudioActualRecStartTime = -1.0
1545
+
1546
+ self._absAudioRecStartPos = -1.0 # in samples
1547
+ self._absAudioRecStopPos = -1.0
1548
+
1549
+ self._curPTS = 0.0 # current display timestamp
1550
+ self._isRecording = False
1551
+ self._generatePTS = False # use genreated PTS values for frames
1552
+
1553
+ # movie writer instance, this runs in a separate thread
1554
+ self._movieWriter = None
1555
+ self._tempVideoFile = None # temporary video file for recording
1556
+
1557
+ # thread for polling the microphone
1558
+ self._audioTrack = None # audio track from the recent recording
1559
+ # keep track of the last video file saved
1560
+ self._lastVideoFile = None
1561
+
1562
+ # OpenGL stuff, just declare these attributes for now
1563
+ self._pixbuffId = None
1564
+ self._textureId = None
1565
+ self._interpolate = True # use bilinear interpolation by default
1566
+ self._texFilterNeedsUpdate = True # flag to update texture filtering
1567
+ self._texBufferSizeBytes = None # size of the texture buffer
1568
+
1569
+ # computer vison mode
1570
+ self._objClassfiers = {} # list of classifiers for CV mode
1571
+
1572
+ # keep track of files to merge
1573
+ self._filesToMerge = [] # list of tuples (videoFile, audioFile)
1574
+
1575
+ self.setWin(win) # sets up OpenGL stuff if needed
1576
+
1577
+ def authorize(self):
1578
+ """Get permission to access the camera. Not implemented locally yet.
1579
+ """
1580
+ pass # NOP
1581
+
1582
+ @property
1583
+ def latencyBias(self):
1584
+ """Latency bias in seconds (`float`).
1585
+
1586
+ This is the latency bias that is applied to the timestamps of the frames
1587
+ in the camera stream. This is useful for synchronizing the camera stream
1588
+ with other devices such as microphones or audio interfaces. The default
1589
+ value is `0.0`, which means no latency bias is applied.
1590
+
1591
+ """
1592
+ return self._latencyBias
1593
+
1594
+ @latencyBias.setter
1595
+ def latencyBias(self, value):
1596
+ """Set the latency bias in seconds (`float`).
1597
+
1598
+ This is the latency bias that is applied to the timestamps of the frames
1599
+ in the camera stream. This is useful for synchronizing the camera stream
1600
+ with other devices such as microphones or audio interfaces. The default
1601
+ value is `0.0`, which means no latency bias is applied.
1602
+
1603
+ Parameters
1604
+ ----------
1605
+ value : float
1606
+ Latency bias in seconds.
1607
+
1608
+ """
1609
+ if not isinstance(value, (int, float)):
1610
+ raise TypeError("Latency bias must be a number.")
1611
+
1612
+ self._latencyBias = float(value)
1613
+
1614
+ @property
1615
+ def streamTime(self):
1616
+ """Current stream time in seconds (`float`).
1617
+
1618
+ This is the current absolute time in seconds from the time the PC was
1619
+ booted. This is not the same as the recording time, which is the time
1620
+ since the recording started. This is useful for generating timestamps
1621
+ across multiple cameras or devices using the same time source.
1622
+
1623
+ """
1624
+ return self._capture.streamTime
1625
+
1626
+ @property
1627
+ def recordingTime(self):
1628
+ """Time in seconds since the recording started (`float`).
1629
+
1630
+ This is the time since the recording started. This is useful for
1631
+ generating timestamps for frames in the recording. If the recording has
1632
+ not started, this will return `0.0`.
1633
+
1634
+ """
1635
+ if self._absRecStreamStartTime < 0:
1636
+ return 0.0
1637
+
1638
+ if self._cameraAPI == CAMERA_API_AVFOUNDATION:
1639
+ return time.time() - self._absRecStreamStartTime
1640
+
1641
+ # for other APIs, use the PTS value
1642
+ curPts = self._capture.get_pts()
1643
+ if curPts is None:
1644
+ return 0.0
1645
+
1646
+ # return the difference between the current PTS and the absolute start time
1647
+ return self._capture.get_pts() - self._absRecStreamStartTime
1648
+
1649
+ @property
1650
+ def isReady(self):
1651
+ """Is the camera ready (`bool`)?
1652
+
1653
+ The camera is ready when the following conditions are met. First, we've
1654
+ created a player interface and opened it. Second, we have received
1655
+ metadata about the stream. At this point we can assume that the camera
1656
+ is 'hot' and the stream is being read.
1657
+
1658
+ This is a legacy property used to support older versions of PsychoPy.
1659
+ The `isOpened` property should be used instead.
1660
+
1661
+ """
1662
+ return self.isStarted
1663
+
1664
+ @property
1665
+ def frameSize(self):
1666
+ """Size of the video frame obtained from recent metadata (`float` or
1667
+ `None`).
1668
+
1669
+ Only valid after an `open()` and successive `_enqueueFrame()` call as
1670
+ metadata needs to be obtained from the stream. Returns `None` if not
1671
+ valid.
1672
+ """
1673
+ if self._cameraInfo is None:
1674
+ return None
1675
+
1676
+ return self._cameraInfo.frameSize
1677
+
1678
+ @property
1679
+ def frameRate(self):
1680
+ """Frame rate of the video stream (`float` or `None`).
1681
+
1682
+ Only valid after an `open()` and successive `_enqueueFrame()` call as
1683
+ metadata needs to be obtained from the stream. Returns `None` if not
1684
+ valid.
1685
+
1686
+ """
1687
+ if self._cameraInfo is None:
1688
+ return None
1689
+
1690
+ return self._cameraInfo.frameRate
1691
+
1692
+ @property
1693
+ def frameInterval(self):
1694
+ """Frame interval in seconds (`float`).
1695
+
1696
+ This is the time between frames in the video stream. This is computed
1697
+ from the frame rate of the video stream. If the frame rate is not set,
1698
+ this will return `None`.
1699
+
1700
+ """
1701
+ if self._cameraInfo is None or self._cameraInfo.frameRate is None:
1702
+ return -1.0
1703
+
1704
+ return 1.0 / self._cameraInfo.frameRate
1705
+
1706
+ def _assertCameraReady(self):
1707
+ """Assert that the camera is ready. Raises a `CameraNotReadyError` if
1708
+ the camera is not ready.
1709
+ """
1710
+ if not self.isReady:
1711
+ raise CameraNotReadyError("Camera is not ready.")
1712
+
1713
+ @property
1714
+ def isRecording(self):
1715
+ """`True` if the video is presently recording (`bool`)."""
1716
+ # Status flags as properties are pretty useful for users since they are
1717
+ # self documenting and prevent the user from touching the status flag
1718
+ # attribute directly.
1719
+ #
1720
+ return self._isRecording
1721
+
1722
+ @property
1723
+ def isStarted(self):
1724
+ """`True` if the stream has started (`bool`). This status is given after
1725
+ `open()` has been called on this object.
1726
+ """
1727
+ if hasattr(self, "_isStarted"):
1728
+ return self._isStarted
1729
+
1730
+ @property
1731
+ def isNotStarted(self):
1732
+ """`True` if the stream may not have started yet (`bool`). This status
1733
+ is given before `open()` or after `close()` has been called on this
1734
+ object.
1735
+ """
1736
+ return not self.isStarted
1737
+
1738
+ @property
1739
+ def isStopped(self):
1740
+ """`True` if the recording has stopped (`bool`). This does not mean that
1741
+ the stream has stopped, `getVideoFrame()` will still yield frames until
1742
+ `close()` is called.
1743
+ """
1744
+ return not self._isRecording
1745
+
1746
+ @property
1747
+ def metadata(self):
1748
+ """Video metadata retrieved during the last frame update
1749
+ (`MovieMetadata`).
1750
+ """
1751
+ return self.getMetadata()
1752
+
1753
+ def getMetadata(self):
1754
+ """Get stream metadata.
1755
+
1756
+ Returns
1757
+ -------
1758
+ MovieMetadata vor None
1759
+ Metadata about the video stream, retrieved during the last frame
1760
+ update (`_enqueueFrame` call). If no metadata is available,
1761
+ returns `None`. This is useful for getting information about the
1762
+ video stream such as frame size, frame rate, pixel format, etc.
1763
+
1764
+ """
1765
+ return self._capture.getMetadata() if self._capture else None
1766
+
1767
+ _getCamerasCache = {}
1768
+
1769
+ @staticmethod
1770
+ def getCameras(cameraLib='ffpyplayer'):
1771
+ """Get information about installed cameras on this system.
1772
+
1773
+ Returns
1774
+ -------
1775
+ dict
1776
+ Mapping of camera information objects.
1777
+
1778
+ """
1779
+ # not pluggable yet, needs to be made available via extensions
1780
+ return CameraDevice.getCameras(
1781
+ cameraLib=cameraLib)
1782
+
1783
+ @staticmethod
1784
+ def getAvailableDevices():
1785
+ devices = []
1786
+ for dev in st.getCameras():
1787
+ for spec in dev:
1788
+ devices.append({
1789
+ 'device': spec['index'],
1790
+ 'name': spec['device_name'],
1791
+ 'frameRate': spec['frameRate'],
1792
+ 'frameSize': spec['frameSize'],
1793
+ 'pixelFormat': spec['pixelFormat'],
1794
+ 'codecFormat': spec['codecFormat'],
1795
+ 'cameraAPI': spec['cameraAPI']
1796
+ })
1797
+
1798
+ return devices
1799
+
1800
+ @staticmethod
1801
+ def getCameraDescriptions(collapse=False):
1802
+ """Get a mapping or list of camera descriptions.
1803
+
1804
+ Camera descriptions are a compact way of representing camera settings
1805
+ and formats. Description strings can be used to specify which camera
1806
+ device and format to use with it to the `Camera` class.
1807
+
1808
+ Descriptions have the following format (example)::
1809
+
1810
+ '[Live! Cam Sync 1080p] 160x120@30fps, mjpeg'
1811
+
1812
+ This shows a specific camera format for the 'Live! Cam Sync 1080p'
1813
+ webcam which supports 160x120 frame size at 30 frames per second. The
1814
+ last value is the codec or pixel format used to decode the stream.
1815
+ Different pixel formats and codecs vary in performance.
1816
+
1817
+ Parameters
1818
+ ----------
1819
+ collapse : bool
1820
+ Return camera information as string descriptions instead of
1821
+ `CameraInfo` objects. This provides a more compact way of
1822
+ representing camera formats in a (reasonably) human-readable format.
1823
+
1824
+ Returns
1825
+ -------
1826
+ dict or list
1827
+ Mapping (`dict`) of camera descriptions, where keys are camera names
1828
+ (`str`) and values are a `list` of format description strings
1829
+ associated with the camera. If `collapse=True`, all descriptions
1830
+ will be returned in a single flat list. This might be more useful
1831
+ for specifying camera formats from a single GUI list control.
1832
+
1833
+ """
1834
+ return getCameraDescriptions(collapse=collapse)
1835
+
1836
+ @property
1837
+ def device(self):
1838
+ """Camera to use (`str` or `None`).
1839
+
1840
+ String specifying the name of the camera to open a stream with. This
1841
+ must be set prior to calling `start()`. If the name is not valid, an
1842
+ error will be raised when `start()` is called.
1843
+
1844
+ """
1845
+ return self._device
1846
+
1847
+ @device.setter
1848
+ def device(self, value):
1849
+ if value in (None, "None", "none", "Default", "default"):
1850
+ value = 0
1851
+
1852
+ self._device = value
1853
+
1854
+ @property
1855
+ def _hasPlayer(self):
1856
+ """`True` if we have an active media player instance.
1857
+ """
1858
+ # deprecated - remove in future versions and use `isStarted` instead
1859
+ return self.isStarted
1860
+
1861
+ @property
1862
+ def mic(self):
1863
+ """Microphone to record audio samples from during recording
1864
+ (:class:`~psychopy.sound.microphone.Microphone` or `None`).
1865
+
1866
+ If `None`, no audio will be recorded. Cannot be set after opening a
1867
+ camera stream.
1868
+ """
1869
+ return self._mic
1870
+
1871
+ @mic.setter
1872
+ def mic(self, value):
1873
+ if self.isStarted:
1874
+ raise CameraError("Cannot set microphone after starting camera.")
1875
+
1876
+ self._mic = value
1877
+
1878
+ @property
1879
+ def _hasAudio(self):
1880
+ """`True` if we have a microphone object for audio recording.
1881
+ """
1882
+ return self._mic is not None
1883
+
1884
+ @property
1885
+ def win(self):
1886
+ """Window which frames are being presented (`psychopy.visual.Window` or
1887
+ `None`).
1888
+ """
1889
+ return self._win
1890
+
1891
+ @win.setter
1892
+ def win(self, value):
1893
+ self._win = value
1894
+
1895
+ @property
1896
+ def frameCount(self):
1897
+ """Number of frames captured in the present recording (`int`).
1898
+ """
1899
+ if not self._isRecording:
1900
+ return 0
1901
+
1902
+ totalFramesBuffered = (
1903
+ len(self._captureFrames) + self._captureThread.framesWaiting)
1904
+
1905
+ return totalFramesBuffered
1906
+
1907
+ @property
1908
+ def keepFrames(self):
1909
+ """Number of frames to keep in memory for the camera stream (`int`).
1910
+ """
1911
+ return self._keepFrames
1912
+
1913
+ @keepFrames.setter
1914
+ def keepFrames(self, value):
1915
+ if value < 0:
1916
+ raise ValueError("`keepFrames` must be a non-negative integer.")
1917
+
1918
+ self._keepFrames = value
1919
+ oldFrames = self._frameStore
1920
+ oldStoreSize = len(self._frameStore)
1921
+
1922
+ if oldStoreSize == self._keepFrames:
1923
+ # nothing to do, size is the same
1924
+ return
1925
+
1926
+ # change the size of the frame store
1927
+ self._frameStore = collections.deque(maxlen=self._keepFrames)
1928
+
1929
+ if oldStoreSize > self._keepFrames:
1930
+ logging.warning(
1931
+ "Reducing `keepFrames` from {} to {} will discard the oldest "
1932
+ "frames in the buffer.".format(oldStoreSize, self._keepFrames))
1933
+
1934
+ # add back frames
1935
+ if oldStoreSize > 0:
1936
+ # copy the last `keepFrames` frames to the new store
1937
+ for i in range(oldStoreSize - self._keepFrames, oldStoreSize):
1938
+ self._frameStore.append(oldFrames[i])
1939
+
1940
+ @property
1941
+ def recordingTime(self):
1942
+ """Current recording timestamp (`float`).
1943
+
1944
+ This returns the timestamp of the last frame captured in the recording.
1945
+
1946
+ This value increases monotonically from the last `record()` call. It
1947
+ will reset once `stop()` is called. This value is invalid outside
1948
+ `record()` and `stop()` calls.
1949
+
1950
+ """
1951
+ return self.frameCount * self._capture.frameInterval
1952
+
1953
+ @property
1954
+ def recordingBytes(self):
1955
+ """Current size of the recording in bytes (`int`).
1956
+ """
1957
+ if not self._isRecording:
1958
+ return 0
1959
+
1960
+ return -1
1961
+
1962
+ def _assertMediaPlayer(self):
1963
+ """Assert that we have a media player instance open.
1964
+
1965
+ This will raise a `RuntimeError` if there is no player open. Use this
1966
+ function to ensure that a player is present before running subsequent
1967
+ code.
1968
+ """
1969
+ if self._capture is not None:
1970
+ return
1971
+
1972
+ raise PlayerNotAvailableError('Media player not initialized.')
1973
+
1974
+ @property
1975
+ def isReady(self):
1976
+ """`True` if the video and audio capture devices are in a ready state
1977
+ (`bool`).
1978
+
1979
+ When this is `True`, the audio and video streams are properly started.
1980
+
1981
+ """
1982
+ return self._audioReady and self._videoReady
1983
+
1984
+ def open(self):
1985
+ """Open the camera stream and begin decoding frames (if available).
1986
+
1987
+ This function returns when the camera is ready to start getting
1988
+ frames.
1989
+
1990
+ Call `record()` to start recording frames to memory. Captured frames
1991
+ came be saved to disk using `save()`.
1992
+
1993
+ """
1994
+ if self._hasPlayer:
1995
+ raise RuntimeError('Cannot open `MediaPlayer`, already opened.')
1996
+
1997
+ # Camera interface to use, these are hard coded but support for each is
1998
+ # provided by an extension.
1999
+ # desc = self._cameraInfo.description()
2000
+
2001
+ self._capture.open()
2002
+
2003
+ if self.win is not None:
2004
+ # if we have a window, setup texture buffers for displaying
2005
+ self._setupTextureBuffers()
2006
+
2007
+ # open the mic when the camera opens
2008
+ if hasattr(self.mic, "open"):
2009
+ self.mic.open()
2010
+
2011
+ self._isStarted = True
2012
+
2013
+ def record(self, clearLastRecording=True, waitForStart=False):
2014
+ """Start recording frames.
2015
+
2016
+ This function will start recording frames and audio (if available). The
2017
+ value of `lastFrame` will be updated as new frames arrive and the
2018
+ `frameCount` will increase. You can access image data for the most
2019
+ recent frame to be captured using `lastFrame`.
2020
+
2021
+ If this is called before `open()` the camera stream will be opened
2022
+ automatically. This is not recommended as it may incur a longer than
2023
+ expected delay in the recording start time.
2024
+
2025
+ Warnings
2026
+ --------
2027
+ If a recording has been previously made without calling `save()` it will
2028
+ be discarded if `record()` is called again unless
2029
+ `clearLastRecording=False`.
2030
+
2031
+ Parameters
2032
+ ----------
2033
+ clearLastRecording : bool
2034
+ Clear the frame buffer before starting the recording. If `True`,
2035
+ the frame buffer will be cleared before starting the recording. If
2036
+ `False`, the frame buffer will be kept and new frames will be added
2037
+ to the buffer. Default is `True`. This is deprecated and will
2038
+ eventually be removed in a future version of PsychoPy. The recording
2039
+ is always cleared when `record()` is called, so this parameter is
2040
+ ignored.
2041
+ waitForStart : bool
2042
+ Capture video only when the camera and microphone are ready. This
2043
+ will result in a longer delay before the recording starts, but will
2044
+ ensure the microphone is actually recording valid samples. In some
2045
+ cases this will result in a delay of up to 1 second before the
2046
+ recording starts.
2047
+
2048
+ """
2049
+ if self.isNotStarted:
2050
+ self.open() # open the camera stream if we call record() first
2051
+ logging.warning(
2052
+ "Called `Camera.record()` before opening the camera stream, "
2053
+ "opening now. This is not recommended as it may incur a longer "
2054
+ "than expected delay in the recording start time."
2055
+ )
2056
+
2057
+ if self._isRecording:
2058
+ logging.warning(
2059
+ "Called `Camera.record()` while already recording, stopping "
2060
+ "the previous recording first."
2061
+ )
2062
+ self.stop()
2063
+
2064
+ # clear previous frames
2065
+ if clearLastRecording:
2066
+ self._frameStore.clear() # clear frames from last recording
2067
+
2068
+ self._capture._clearFrameStore()
2069
+
2070
+ # reset the movie writer
2071
+ self._openMovieFileWriter()
2072
+
2073
+ # reset audio flags
2074
+ self._audioReady = self._videoReady = False
2075
+
2076
+ # reset the last frame
2077
+ self._lastFrame = None
2078
+
2079
+ # start camera recording
2080
+ self._absVideoRecStartTime = self._capture.record()
2081
+
2082
+ # start microphone recording
2083
+ if self._usageMode == CAMERA_MODE_VIDEO:
2084
+ if self.mic is not None:
2085
+ audioStartTime = self.mic.start(
2086
+ waitForStart=int(waitForStart), # wait until the mic is ready
2087
+ )
2088
+ self._absAudioRecStartTime = self._capture.streamTime
2089
+ if waitForStart:
2090
+ self._absAudioActualRecStartTime = audioStartTime # time it will be ready
2091
+ else:
2092
+ self._absAudioActualRecStartTime = self._absAudioRecStartTime
2093
+
2094
+ self._isRecording = True # set recording flag
2095
+ # do an initial poll to avoid frame dropping
2096
+ self.update()
2097
+ # mark that there's unsaved footage
2098
+ self._unsaved = True
2099
+
2100
+ def start(self, waitForStart=True):
2101
+ """Start the camera stream.
2102
+
2103
+ This will start the camera stream and begin decoding frames. If the
2104
+ camera is already started, this will do nothing. Use `record()` to start
2105
+ recording frames to memory.
2106
+
2107
+ """
2108
+ return self.record(clearLastRecording=False, waitForStart=waitForStart)
2109
+
2110
+ def stop(self):
2111
+ """Stop recording frames and audio (if available).
2112
+ """
2113
+ # poll any remaining frames and stop
2114
+ self.update()
2115
+
2116
+ # stop the camera stream
2117
+ self._absVideoRecStopTime = self._capture.stop()
2118
+
2119
+ # stop audio recording if we have a microphone
2120
+ if self.hasMic and not self.mic._stream._closed:
2121
+ _, overflows = self.mic.poll()
2122
+
2123
+ if overflows > 0:
2124
+ logging.warning(
2125
+ "Audio recording overflowed {} times before stopping, "
2126
+ "some audio samples may be lost.".format(overflows))
2127
+ audioStopTime, _, _, _ = self.mic.stop(
2128
+ blockUntilStopped=0)
2129
+
2130
+ self._audioReady = self._videoReady = False # reset camera ready flags
2131
+ self._isRecording = False
2132
+
2133
+ self._closeMovieFileWriter()
2134
+
2135
+ def close(self):
2136
+ """Close the camera.
2137
+
2138
+ This will close the camera stream and free up any resources used by the
2139
+ device. If the camera is currently recording, this will stop the
2140
+ recording, but will not discard any frames. You may still call `save()`
2141
+ to save the frames to disk.
2142
+
2143
+ """
2144
+ self._closeMovieFileWriter()
2145
+
2146
+ self._capture.close() # close the camera stream
2147
+ self._capture = None # clear the capture object
2148
+
2149
+ if self.mic is not None:
2150
+ self.mic.close()
2151
+
2152
+ self._isStarted = False
2153
+
2154
+ def _mergeAudioVideoTracks(self, videoTrackFile, audioTrackFile,
2155
+ filename, writerOpts=None):
2156
+ """Use FFMPEG to merge audio and video tracks into a single file.
2157
+
2158
+ Parameters
2159
+ ----------
2160
+ videoTrackFile : str
2161
+ Path to the video track file to merge.
2162
+ audioTrackFile : str
2163
+ Path to the audio track file to merge.
2164
+ filename : str
2165
+ Path to the output file to save the merged audio and video tracks.
2166
+ writerOpts : dict or None
2167
+ Options to pass to the movie writer. If `None`, default options
2168
+ will be used. This is useful for specifying the codec, bitrate,
2169
+ etc. for the output file.
2170
+
2171
+ Returns
2172
+ -------
2173
+ str
2174
+ Path to the output file with merged audio and video tracks.
2175
+
2176
+ """
2177
+ import subprocess as sp
2178
+
2179
+ # check if the video and audio track files exist
2180
+ if not os.path.exists(videoTrackFile):
2181
+ raise FileNotFoundError(
2182
+ "Video track file `{}` does not exist.".format(videoTrackFile))
2183
+ if not os.path.exists(audioTrackFile):
2184
+ raise FileNotFoundError(
2185
+ "Audio track file `{}` does not exist.".format(audioTrackFile))
2186
+
2187
+ # check if the output file already exists
2188
+ if os.path.exists(filename):
2189
+ logging.warning(
2190
+ "Output file `{}` already exists, it will be overwritten.".format(filename))
2191
+ os.remove(filename)
2192
+
2193
+ # build the command to merge audio and video tracks
2194
+ cmd = [
2195
+ 'ffmpeg',
2196
+ '-loglevel', 'error', # suppress output except errors
2197
+ '-nostdin', # do not read from stdin
2198
+ '-y', # overwrite output file if it exists
2199
+ '-i', videoTrackFile, # input video track
2200
+ '-i', audioTrackFile, # input audio track
2201
+ '-c:v', 'copy', # copy video codec
2202
+ '-c:a', 'aac', # use AAC for audio codec
2203
+ '-strict', 'experimental', # allow experimental codecs
2204
+ '-threads', 'auto', # use all available threads
2205
+ '-shortest' # stop when the shortest input ends
2206
+ ]
2207
+ # add output file
2208
+ cmd.append(filename)
2209
+
2210
+ # apply any writer options if provided
2211
+ if writerOpts is not None:
2212
+ for key, value in writerOpts.items():
2213
+ if isinstance(value, str):
2214
+ cmd.append('-' + key)
2215
+ cmd.append(value)
2216
+ elif isinstance(value, bool) and value:
2217
+ cmd.append('-' + key)
2218
+ elif isinstance(value, (int, float)):
2219
+ cmd.append('-' + key)
2220
+ cmd.append(str(value))
2221
+
2222
+ logging.debug(
2223
+ "Merging audio and video tracks with command: {}".format(' '.join(cmd))
2224
+ )
2225
+
2226
+ # run the command to merge audio and video tracks
2227
+ try:
2228
+ proc = sp.Popen(
2229
+ cmd,
2230
+ stdout=sp.PIPE,
2231
+ stderr=sp.PIPE,
2232
+ stdin=sp.DEVNULL if hasattr(sp, 'DEVNULL') else None,
2233
+ universal_newlines=True, # use text mode for output
2234
+ text=True
2235
+ )
2236
+ proc.wait() # wait for the process to finish
2237
+ if proc.returncode != 0:
2238
+ logging.error(
2239
+ "FFMPEG returned non-zero exit code {} for command: {}".format(
2240
+ proc.returncode, cmd
2241
+ )
2242
+ )
2243
+ # wait for the process to finish
2244
+ except sp.CalledProcessError as e:
2245
+ logging.error(
2246
+ "Failed to merge audio and video tracks: {}".format(e))
2247
+ return None
2248
+
2249
+ logging.info(
2250
+ "Merged audio and video tracks into `{}`".format(filename))
2251
+
2252
+ return filename
2253
+
2254
+ def save(self, filename, useThreads=True, mergeAudio=True, writerOpts=None):
2255
+ """Save the last recording to file.
2256
+
2257
+ This will write frames to `filename` acquired since the last call of
2258
+ `record()` and subsequent `stop()`. If `record()` is called again before
2259
+ `save()`, the previous recording will be deleted and lost.
2260
+
2261
+ This is a slow operation and will block for some time depending on the
2262
+ length of the video. This can be sped up by setting `useThreads=True` if
2263
+ supported.
2264
+
2265
+ Parameters
2266
+ ----------
2267
+ filename : str
2268
+ File to save the resulting video to, should include the extension.
2269
+ useThreads : bool
2270
+ Use threading where possible to speed up the saving process.
2271
+ mergeAudio : bool
2272
+ Merge the audio track from the microphone with the video into a
2273
+ single file if `True`. If `False`, the audio track will be saved
2274
+ to a separate file with the same name as `filename`, but with a
2275
+ `.wav` extension. This is useful if you want to process the audio
2276
+ track separately, or merge it with the video later on as the process
2277
+ is computationally expensive and memory consuming. Default is
2278
+ `True`.
2279
+ writerOpts : dict or None
2280
+ Options to pass to the movie writer. If `None`, default options
2281
+ will be used.
2282
+
2283
+ """
2284
+ # stop if still recording
2285
+ if self._isRecording:
2286
+ self.stop()
2287
+ logging.warning(
2288
+ "Called `Camera.save()` while recording, stopping the "
2289
+ "recording first."
2290
+ )
2291
+
2292
+ # if there's nothing to unsaved, do nothing
2293
+ if not self._unsaved:
2294
+ return
2295
+
2296
+ # check if we have an active movie writer
2297
+ if self._movieWriter is not None:
2298
+ self._movieWriter.close() # close the movie writer
2299
+
2300
+ # check if we have a temp movie file
2301
+ videoTrackFile = self._tempVideoFile
2302
+
2303
+ # write the temporary audio track to file if we have one
2304
+ tStart = time.time() # start time for the operation
2305
+ if self.mic is not None:
2306
+ audioTrack = self.mic.getRecording()
2307
+
2308
+ if audioTrack is not None:
2309
+ logging.debug(
2310
+ "Saving audio track to file `{}`...".format(filename))
2311
+
2312
+ # trim off samples before the recording started
2313
+ audioTrack = audioTrack.trimmed(
2314
+ direction='start',
2315
+ duration=self._absAudioRecStartPos,
2316
+ units='samples')
2317
+
2318
+ if mergeAudio:
2319
+ logging.debug("Merging audio track with video track...")
2320
+ # save it to a temp file
2321
+ import tempfile
2322
+ tempAudioFile = tempfile.NamedTemporaryFile(
2323
+ suffix='.wav', delete=False)
2324
+ audioTrackFile = tempAudioFile.name
2325
+ tempAudioFile.close() # close the file so we can use it later
2326
+ audioTrack.save(audioTrackFile)
2327
+
2328
+ # # composite audio a video tracks using MoviePy (huge thanks to
2329
+ # # that team)
2330
+ # from moviepy.video.io.VideoFileClip import VideoFileClip
2331
+ # from moviepy.audio.io.AudioFileClip import AudioFileClip
2332
+ # from moviepy.audio.AudioClip import CompositeAudioClip
2333
+
2334
+ # videoClip = VideoFileClip(videoTrackFile)
2335
+ # audioClip = AudioFileClip(audioTrackFile)
2336
+ # videoClip.audio = CompositeAudioClip([audioClip])
2337
+
2338
+ # # default options for the writer, needed or we can crash
2339
+ # moviePyOpts = {
2340
+ # 'logger': None
2341
+ # }
2342
+
2343
+ # if writerOpts is not None: # make empty dict if not provided
2344
+ # moviePyOpts.update(writerOpts)
2345
+
2346
+ # # transcode with the format the user wants
2347
+ # videoClip.write_videofile(
2348
+ # filename,
2349
+ # **moviePyOpts) # expand out options
2350
+
2351
+ # videoClip.close() # close the video clip
2352
+ # audioClip.close()
2353
+
2354
+ # merge audio and video tracks using FFMPEG
2355
+ mergedVideo = self._mergeAudioVideoTracks(
2356
+ videoTrackFile,
2357
+ audioTrackFile,
2358
+ filename,
2359
+ writerOpts=writerOpts)
2360
+
2361
+ os.remove(audioTrackFile) # remove the temp file
2362
+
2363
+ else:
2364
+ tAudioStart = time.time() # start time for audio saving
2365
+ # just save the audio file seperatley
2366
+ # check if the filename has an extension
2367
+ if '.' not in filename:
2368
+ audioTrackFile = filename + '.wav'
2369
+ else:
2370
+ # if it has an extension, use the same name but with .wav
2371
+ # extension
2372
+ rootName, _ = os.path.splitext(filename)
2373
+ audioTrackFile = rootName + '.wav'
2374
+
2375
+ audioTrack.save(audioTrackFile)
2376
+
2377
+ logging.info(
2378
+ "Saved recorded audio track to `{}` (took {:.6f} seconds)".format(
2379
+ audioTrackFile, time.time() - tAudioStart))
2380
+
2381
+ # just copy the video from the temp file to the final file
2382
+ import shutil
2383
+ shutil.copyfile(videoTrackFile, filename)
2384
+
2385
+ else:
2386
+ # just copy the video file to the destination
2387
+ import shutil
2388
+ shutil.copyfile(videoTrackFile, filename)
2389
+
2390
+ os.remove(videoTrackFile) # remove the temp file
2391
+
2392
+ logging.info(
2393
+ "Saved recorded video to `{}` (took {:.6f} seconds)".format(
2394
+ filename, time.time() - tStart))
2395
+
2396
+ self._frameStore.clear() # clear the frame store
2397
+ # mark that there's no longer unsaved footage
2398
+ self._unsaved = False
2399
+
2400
+ self._lastVideoFile = filename # store the last video file saved
2401
+
2402
+ return self._lastVideoFile
2403
+
2404
+ def _upload(self):
2405
+ """Upload video file to an online repository. Not implemented locally,
2406
+ needed for auto translate to JS.
2407
+ """
2408
+ pass # NOP
2409
+
2410
+ def _download(self):
2411
+ """Download video file to an online repository. Not implemented locally,
2412
+ needed for auto translate to JS.
2413
+ """
2414
+ pass # NOP
2415
+
2416
+ @property
2417
+ def lastClip(self):
2418
+ """File path to the last recording (`str` or `None`).
2419
+
2420
+ This value is only valid if a previous recording has been saved
2421
+ successfully (`save()` was called), otherwise it will be set to `None`.
2422
+
2423
+ """
2424
+ return self.getLastClip()
2425
+
2426
+ def getLastClip(self):
2427
+ """File path to the last saved recording.
2428
+
2429
+ This value is only valid if a previous recording has been saved to disk
2430
+ (`save()` was called).
2431
+
2432
+ Returns
2433
+ -------
2434
+ str or None
2435
+ Path to the file the most recent call to `save()` created. Returns
2436
+ `None` if no file is ready.
2437
+
2438
+ """
2439
+ return self._lastVideoFile
2440
+
2441
+ @property
2442
+ def lastFrame(self):
2443
+ """Most recent frame pulled from the camera (`VideoFrame`) since the
2444
+ last call of `getVideoFrame`.
2445
+ """
2446
+ return self._lastFrame
2447
+
2448
+ @property
2449
+ def frameCount(self):
2450
+ """Total number of frames captured in the current recording (`int`).
2451
+
2452
+ This is the total number of frames captured since the last call to
2453
+ `record()`. This value is reset when `record()` is called again.
2454
+
2455
+ """
2456
+ return self._frameCount
2457
+
2458
+ @property
2459
+ def hasMic(self):
2460
+ """`True` if the camera has a microphone attached (`bool`).
2461
+
2462
+ This is `True` if the camera has a microphone attached and is ready to
2463
+ record audio. If the camera does not have a microphone, this will be
2464
+ `False`.
2465
+
2466
+ """
2467
+ return self.mic is not None
2468
+
2469
+ def _convertFrameToRGBFFPyPlayer(self, frame):
2470
+ """Convert a frame to RGB format.
2471
+
2472
+ This function converts a frame to RGB format. The frame is returned as
2473
+ a Numpy array. The resulting array will be in the correct format to
2474
+ upload to OpenGL as a texture.
2475
+
2476
+ Parameters
2477
+ ----------
2478
+ frame : FFPyPlayer frame
2479
+ The frame to convert.
2480
+
2481
+ Returns
2482
+ -------
2483
+ numpy.ndarray
2484
+ The converted frame in RGB format.
2485
+
2486
+ """
2487
+ from ffpyplayer.pic import SWScale
2488
+ if frame.get_pixel_format() == 'rgb24': # already converted
2489
+ return frame
2490
+
2491
+ rgbImg = SWScale(
2492
+ self._metadata.size[0], self._metadata.size[1], # width, height
2493
+ frame.get_pixel_format(),
2494
+ ofmt='rgb24').scale(frame)
2495
+
2496
+ return rgbImg
2497
+
2498
+ def update(self):
2499
+ """Acquire the newest data from the camera and audio streams.
2500
+
2501
+ This must be called periodically to ensure that stream buffers are
2502
+ flushed before they overflow to prevent data loss. Furthermore,
2503
+ calling this too infrequently may result also result in more frames
2504
+ needing to be processed at once, which may result in performance issues.
2505
+
2506
+ Returns
2507
+ -------
2508
+ int
2509
+ Number of frames captured since the last call to this method. This
2510
+ will be `0` if no new frames were captured since the last call,
2511
+ indicating that the poll function is getting called too
2512
+ frequently or that the camera is not producing new frames (i.e.
2513
+ paused or closed). If `-1` is returned, it indicates that the
2514
+ either or both the camera and microphone are not in a ready state
2515
+ albiet both interfaces are open. This can happen if `update()` is
2516
+ called very shortly after `record()`.
2517
+
2518
+ Examples
2519
+ --------
2520
+ Capture camera frames in a loop::
2521
+
2522
+ while cam.recordingTime < 10.0: # record for 10 seconds
2523
+ numFrames = cam.update() # update the camera stream
2524
+ if numFrames > 0:
2525
+ frame = cam.getVideoFrame() # get the most recent frame
2526
+ # do something with the frame, e.g. display it
2527
+ else:
2528
+ # return last frame or placeholder frame if nothing new
2529
+
2530
+ """
2531
+ # poll camera for new frames
2532
+ newFrames = self._capture.getFrames() # get new frames from the camera
2533
+
2534
+ if not self._videoReady and newFrames:
2535
+ # if we have new frames, we can set the video ready flag
2536
+ self._videoReady = True
2537
+
2538
+ if self.hasMic and not self.mic._stream._closed:
2539
+ # poll the microphone for audio samples
2540
+ audioPos, overflows = self.mic.poll()
2541
+
2542
+ if (not self._audioReady) and self._videoReady:
2543
+ nNewFrames = len(newFrames)
2544
+ # determine which video frame the audio starts at that we aquired
2545
+ keepFrames = []
2546
+ for i, frame in enumerate(newFrames):
2547
+ _, _, streamTime = frame
2548
+ if streamTime >= self._absAudioActualRecStartTime:
2549
+ keepFrames.append(frame)
2550
+
2551
+ # If we arrived at the audio start time and there is a video
2552
+ # frame captured after that, we can compute the exact position
2553
+ # of the sample in the audio track that corresponds to that
2554
+ # frame. This will allow us to align the audio and video streams
2555
+ # when saving the video file.
2556
+ if keepFrames:
2557
+ _, _, streamTime = keepFrames[0]
2558
+
2559
+ # delta between the first video frame's capture timestamp
2560
+ # and the time the mic reported itself as ready. Used to
2561
+ # align the audio and video streams
2562
+ frameSyncFudge = (
2563
+ streamTime - self._absAudioActualRecStartTime)
2564
+
2565
+ # compute exact time the first audio sample was recorded
2566
+ # from the audio position and actual recording start time
2567
+ absFirstAudioSampleTime = \
2568
+ self._absAudioActualRecStartTime - (
2569
+ audioPos / self.mic.sampleRateHz)
2570
+
2571
+ # compute how many samples we will discard from the audio
2572
+ # track to align it with the video stream
2573
+ self._absAudioRecStartPos = \
2574
+ ((streamTime - absFirstAudioSampleTime) + \
2575
+ frameSyncFudge + self._latencyBias) * self.mic.sampleRateHz
2576
+ self._absAudioRecStartPos = int(self._absAudioRecStartPos)
2577
+
2578
+ # convert to samples
2579
+ self._audioReady = True
2580
+
2581
+ newFrames = keepFrames # keep only frames after the audio start time
2582
+
2583
+ else:
2584
+ self._audioReady = True # no mic, so we just set the flag
2585
+
2586
+ if not self.isReady:
2587
+ # if the camera is not ready, return -1 to indicate that we are not
2588
+ # ready to process frames yet
2589
+ return -1
2590
+
2591
+ if not newFrames:
2592
+ # if no new frames were captured, return 0 to indicate that we have
2593
+ # no new frames to process
2594
+ return 0
2595
+
2596
+ # put last frames into the frame store
2597
+ nNewFrames = len(newFrames)
2598
+ if nNewFrames > self._frameStore.maxlen:
2599
+ logging.warning(
2600
+ "Frame store overflowed, some frames may have been lost. "
2601
+ "Consider increasing the `keepFrames` parameter when creating "
2602
+ "the camera object or polling the camera more frequently."
2603
+ )
2604
+
2605
+ self._frameCount += nNewFrames # update total frames count
2606
+ # push all frames into the frame store
2607
+ for colorData, pts, streamTime in newFrames:
2608
+ # if camera is in CV mode, convert the frame to RGB
2609
+ if self._usageMode == CAMERA_MODE_CV:
2610
+ colorData = self._convertFrameToRGBFFPyPlayer(colorData)
2611
+ # add the frame to the frame store
2612
+ self._frameStore.append((colorData, pts, streamTime))
2613
+
2614
+ # if we have frames, update the last frame
2615
+ colorData, pts, streamTime = newFrames[-1]
2616
+ self._lastFrame = (
2617
+ self._convertFrameToRGBFFPyPlayer(colorData), # convert to RGB, nop if already
2618
+ pts, # presentation timestamp
2619
+ streamTime
2620
+ )
2621
+
2622
+ self._pixelTransfer() # transfer frames to the GPU if we have a window
2623
+
2624
+ # write frames out to video file
2625
+ if self._usageMode == CAMERA_MODE_VIDEO:
2626
+ for frame in newFrames:
2627
+ self._submitFrameToFile(frame)
2628
+ elif self._usageMode == CAMERA_MODE_CV:
2629
+ pass
2630
+
2631
+ return nNewFrames # return number of frames we got
2632
+
2633
+ def poll(self):
2634
+ """Poll the camera for new frames.
2635
+
2636
+ Alias for `update()`.
2637
+ """
2638
+ return self.update()
2639
+
2640
+ def getVideoFrames(self):
2641
+ """Get the most recent frame from the stream (if available).
2642
+
2643
+ Returns
2644
+ -------
2645
+ list of tuple
2646
+ List of recent video frames. This will return a list of frame images
2647
+ as numpy arrays, their presentation timestamp in the recording, and
2648
+ the absolute stream time in seconds. Frames will be converted
2649
+ to RGB format if they are not already. The number of frames returned
2650
+ will be limited by the `keepFrames` parameter set when creating the
2651
+ camera object. If no frames are available, an empty list will be
2652
+ returned.
2653
+
2654
+ """
2655
+ self.update()
2656
+
2657
+ recentFrames = [
2658
+ self._convertFrameToRGBFFPyPlayer(frame) for frame in self._frameStore]
2659
+
2660
+ return recentFrames
2661
+
2662
+ def getRecentVideoFrame(self):
2663
+ """Get the most recent video frame from the camera.
2664
+
2665
+ Returns
2666
+ -------
2667
+ VideoFrame or None
2668
+ Most recent video frame. Returns `None` if no frame was available,
2669
+ or we timed out.
2670
+
2671
+ """
2672
+ self.update()
2673
+
2674
+ return self._lastFrame[0] if self._lastFrame else None
2675
+
2676
+ # --------------------------------------------------------------------------
2677
+ # Audio track
2678
+ #
2679
+
2680
+ def getAudioTrack(self):
2681
+ """Get the audio track data.
2682
+
2683
+ Returns
2684
+ -------
2685
+ AudioClip or None
2686
+ Audio track data from the microphone if available, or `None` if
2687
+ no microphone is set or no audio was recorded.
2688
+
2689
+ """
2690
+ return self.mic.getRecording() if self.mic else None
2691
+
2692
+ # --------------------------------------------------------------------------
2693
+ # Video rendering
2694
+ #
2695
+ # These methods are used to render live video frames to a window. If a
2696
+ # window is set, this class will automamatically create the nessisary
2697
+ # OpenGL texture buffers and transfers the most recent video frame to the
2698
+ # GPU when `update` is called. The `ImageStim` class can access these
2699
+ # buffers for rendering by setting this class as the `image`.
2700
+ #
2701
+
2702
+ @property
2703
+ def win(self):
2704
+ """Window to render the video frames to (`psychopy.visual.Window` or
2705
+ `None`).
2706
+
2707
+ If `None`, no rendering will be done and the video frames will not be
2708
+ displayed. If a window is set, the video frames will be rendered to the
2709
+ window using OpenGL textures.
2710
+
2711
+ """
2712
+ return self._win
2713
+
2714
+ @win.setter
2715
+ def win(self, value):
2716
+ """Set the window to render the video frames to.
2717
+
2718
+ This will set the window to render the video frames to. If the window
2719
+ is not `None`, it will automatically create OpenGL texture buffers for
2720
+ rendering the video frames. If the window is `None`, no rendering will
2721
+ be done and the video frames will not be displayed.
2722
+
2723
+ Parameters
2724
+ ----------
2725
+ value : psychopy.visual.Window or None
2726
+ Window to render the video frames to. If `None`, no rendering will
2727
+ be done and the video frames will not be displayed.
2728
+
2729
+ """
2730
+ self.setWin(value)
2731
+
2732
+ def setWin(self, win):
2733
+ """Set the window to render the video frames to.
2734
+
2735
+ Parameters
2736
+ ----------
2737
+ win : psychopy.visual.Window
2738
+ Window to render the video frames to. If `None`, no rendering will
2739
+ be done and the video frames will not be displayed.
2740
+
2741
+ """
2742
+ self._win = win
2743
+
2744
+ # if we have a window, setup texture buffers for displaying
2745
+ if self._win is not None:
2746
+ self._setupTextureBuffers()
2747
+ return
2748
+
2749
+ # if we don't have a window, free any texture buffers
2750
+ self._freeTextureBuffers() # free any existing buffers
2751
+
2752
+ @property
2753
+ def interpolate(self):
2754
+ """Whether the video texture should be filtered using linear or nearest
2755
+ neighbor interpolation (`bool`).
2756
+
2757
+ If `True`, the video texture will be filtered using linear interpolation.
2758
+ If `False`, the video texture will be filtered using nearest neighbor
2759
+ interpolation (pass-through). Default is `True`.
2760
+
2761
+ """
2762
+ return self._interpolate
2763
+
2764
+ @interpolate.setter
2765
+ def interpolate(self, value):
2766
+ """Set whether the video texture should be filtered using linear or
2767
+ nearest neighbor interpolation.
2768
+
2769
+ Parameters
2770
+ ----------
2771
+ value : bool
2772
+ If `True`, the video texture will be filtered using linear
2773
+ interpolation. If `False`, the video texture will be filtered using
2774
+ nearest neighbor interpolation (pass-through). Default is `True`.
2775
+
2776
+ """
2777
+ self.setTextureFilter(value)
2778
+
2779
+ def setTextureFilter(self, smooth=True):
2780
+ """Set whether the video texture should be filtered using linear or
2781
+ nearest neighbor interpolation.
2782
+
2783
+ Parameters
2784
+ ----------
2785
+ smooth : bool
2786
+ If `True`, the video texture will be filtered using linear
2787
+ interpolation. If `False`, the video texture will be filtered using
2788
+ nearest neighbor interpolation (pass-through.) Default is `True`.
2789
+
2790
+ """
2791
+ self._interpolate = bool(smooth)
2792
+ self._texFilterNeedsUpdate = True # flag to update texture filtering
2793
+
2794
+ def _freeTextureBuffers(self):
2795
+ """Free any texture buffers used by the camera.
2796
+
2797
+ This is used to free up any texture buffers used by the camera. This
2798
+ is called when the camera is closed or when the window is closed.
2799
+ """
2800
+ import pyglet.gl as GL # needed for OpenGL texture management
2801
+
2802
+ try:
2803
+ # delete buffers and textures if previously created
2804
+ if self._pixbuffId is not None and self._pixbuffId.value > 0:
2805
+ GL.glDeleteBuffers(1, self._pixbuffId)
2806
+ # delete the old texture if present
2807
+ if self._textureId is not None and self._textureId.value > 0:
2808
+ GL.glDeleteTextures(1, self._textureId)
2809
+ except (TypeError, AttributeError):
2810
+ pass
2811
+
2812
+ # clear the IDs
2813
+ self._pixbuffId = GL.GLuint(0)
2814
+ self._textureId = GL.GLuint(0)
2815
+
2816
+ def _setupTextureBuffers(self):
2817
+ """Setup texture buffers for the camera.
2818
+
2819
+ This allocates OpenGL texture buffers for video frames to be written
2820
+ to which then can be rendered to the screen. This is only called if the
2821
+ camera is opened and a window is set.
2822
+
2823
+ """
2824
+ if self.win is None:
2825
+ return
2826
+
2827
+ self._freeTextureBuffers() # free any existing buffers
2828
+
2829
+ import pyglet.gl as GL
2830
+
2831
+ # get the size of the movie frame and compute the buffer size
2832
+ vidWidth, vidHeight = self.frameSize
2833
+ nBufferBytes = self._texBufferSizeBytes = (
2834
+ vidWidth * vidHeight * 3)
2835
+
2836
+ # Create the pixel buffer object which will serve as the texture memory
2837
+ # store. Pixel data will be copied to this buffer each frame.
2838
+ GL.glGenBuffers(1, ctypes.byref(self._pixbuffId))
2839
+ GL.glBindBuffer(GL.GL_PIXEL_UNPACK_BUFFER, self._pixbuffId)
2840
+ GL.glBufferData(
2841
+ GL.GL_PIXEL_UNPACK_BUFFER,
2842
+ nBufferBytes * ctypes.sizeof(GL.GLubyte),
2843
+ None,
2844
+ GL.GL_STREAM_DRAW) # one-way app -> GL
2845
+ GL.glBindBuffer(GL.GL_PIXEL_UNPACK_BUFFER, 0)
2846
+
2847
+ # Create a texture which will hold the data streamed to the pixel
2848
+ # buffer. Only one texture needs to be allocated.
2849
+ GL.glEnable(GL.GL_TEXTURE_2D)
2850
+ GL.glGenTextures(1, ctypes.byref(self._textureId))
2851
+ GL.glBindTexture(GL.GL_TEXTURE_2D, self._textureId)
2852
+ GL.glTexImage2D(
2853
+ GL.GL_TEXTURE_2D,
2854
+ 0,
2855
+ GL.GL_RGB8,
2856
+ vidWidth, vidHeight, # frame dims in pixels
2857
+ 0,
2858
+ GL.GL_RGB,
2859
+ GL.GL_UNSIGNED_BYTE,
2860
+ None)
2861
+
2862
+ # setup texture filtering
2863
+ if self._interpolate:
2864
+ texFilter = GL.GL_LINEAR
2865
+ else:
2866
+ texFilter = GL.GL_NEAREST
2867
+
2868
+ GL.glTexParameteri(
2869
+ GL.GL_TEXTURE_2D,
2870
+ GL.GL_TEXTURE_MAG_FILTER,
2871
+ texFilter)
2872
+ GL.glTexParameteri(
2873
+ GL.GL_TEXTURE_2D,
2874
+ GL.GL_TEXTURE_MIN_FILTER,
2875
+ texFilter)
2876
+ GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP)
2877
+ GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP)
2878
+ GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
2879
+ GL.glDisable(GL.GL_TEXTURE_2D)
2880
+
2881
+ GL.glFlush() # make sure all buffers are ready
2882
+
2883
+ def _pixelTransfer(self):
2884
+ """Copy pixel data from video frame to texture.
2885
+
2886
+ This is called when a new frame is available. The pixel data is copied
2887
+ from the video frame to the texture store on the GPU.
2888
+
2889
+ """
2890
+ if self.win is None:
2891
+ return # no window to render to
2892
+
2893
+ import pyglet.gl as GL
2894
+
2895
+ # get the size of the movie frame and compute the buffer size
2896
+ vidWidth, vidHeight = self.frameSize
2897
+
2898
+ # compute the buffer size
2899
+ nBufferBytes = self._texBufferSizeBytes
2900
+
2901
+ # bind pixel unpack buffer
2902
+ GL.glBindBuffer(GL.GL_PIXEL_UNPACK_BUFFER, self._pixbuffId)
2903
+
2904
+ # Free last storage buffer before mapping and writing new frame
2905
+ # data. This allows the GPU to process the extant buffer in VRAM
2906
+ # uploaded last cycle without being stalled by the CPU accessing it.
2907
+ GL.glBufferData(
2908
+ GL.GL_PIXEL_UNPACK_BUFFER,
2909
+ nBufferBytes * ctypes.sizeof(GL.GLubyte),
2910
+ None,
2911
+ GL.GL_STREAM_DRAW)
2912
+
2913
+ # Map the buffer to client memory, `GL_WRITE_ONLY` to tell the
2914
+ # driver to optimize for a one-way write operation if it can.
2915
+ bufferPtr = GL.glMapBuffer(
2916
+ GL.GL_PIXEL_UNPACK_BUFFER,
2917
+ GL.GL_WRITE_ONLY)
2918
+
2919
+ # map the video frame to a memoryview
2920
+ # suggested by Alex Forrence (aforren1) originally in PR #6439
2921
+ videoBuffer = self._lastFrame[0].to_memoryview()[0].memview
2922
+ videoFrameArray = np.frombuffer(videoBuffer, dtype=np.uint8)
2923
+
2924
+ # copy the frame data to the buffer
2925
+ ctypes.memmove(bufferPtr,
2926
+ videoFrameArray.ctypes.data,
2927
+ nBufferBytes)
2928
+
2929
+ # Very important that we unmap the buffer data after copying, but
2930
+ # keep the buffer bound for setting the texture.
2931
+ GL.glUnmapBuffer(GL.GL_PIXEL_UNPACK_BUFFER)
2932
+
2933
+ # bind the texture in OpenGL
2934
+ GL.glEnable(GL.GL_TEXTURE_2D)
2935
+ GL.glActiveTexture(GL.GL_TEXTURE0)
2936
+ GL.glBindTexture(GL.GL_TEXTURE_2D, self._textureId)
2937
+
2938
+ # copy the PBO to the texture (blocks on AMD for some reason)
2939
+ GL.glTexSubImage2D(
2940
+ GL.GL_TEXTURE_2D, 0, 0, 0,
2941
+ vidWidth, vidHeight,
2942
+ GL.GL_RGB,
2943
+ GL.GL_UNSIGNED_BYTE,
2944
+ 0) # point to the presently bound buffer
2945
+
2946
+ # update texture filtering only if needed
2947
+ if self._texFilterNeedsUpdate:
2948
+ if self._interpolate:
2949
+ texFilter = GL.GL_LINEAR
2950
+ else:
2951
+ texFilter = GL.GL_NEAREST
2952
+
2953
+ GL.glTexParameteri(
2954
+ GL.GL_TEXTURE_2D,
2955
+ GL.GL_TEXTURE_MAG_FILTER,
2956
+ texFilter)
2957
+ GL.glTexParameteri(
2958
+ GL.GL_TEXTURE_2D,
2959
+ GL.GL_TEXTURE_MIN_FILTER,
2960
+ texFilter)
2961
+
2962
+ self._texFilterNeedsUpdate = False
2963
+
2964
+ # important to unbind the PBO
2965
+ GL.glBindBuffer(GL.GL_PIXEL_UNPACK_BUFFER, 0)
2966
+ GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
2967
+ GL.glDisable(GL.GL_TEXTURE_2D)
2968
+
2969
+ @property
2970
+ def colorTexture(self):
2971
+ """OpenGL texture ID for the most recent video frame (`int` or `None`).
2972
+
2973
+ This is the OpenGL texture ID that can be used to render the most
2974
+ recent video frame to a window. If no window is set, this will be `None`.
2975
+ """
2976
+ if self._textureId is None or self._textureId.value <= 0:
2977
+ return None
2978
+
2979
+ return self._textureId
2980
+
2981
+ @property
2982
+ def colorTextureSizeBytes(self):
2983
+ """Size of the texture buffer used for rendering video frames
2984
+ (`int` or `None`).
2985
+
2986
+ This returns the size of the texture buffer in bytes used for rendering
2987
+ video frames. This is only valid if the camera is opened.
2988
+
2989
+ """
2990
+ if self._cameraInfo is None:
2991
+ return None
2992
+
2993
+ return self._texBufferSizeBytes
2994
+
2995
+ # --------------------------------------------------------------------------
2996
+ # Movie writer platform-specific methods
2997
+ #
2998
+ # These are used to write frames to a movie file. We used to use the
2999
+ # `MovieFileWriter` class for this, but for now were implimenting this
3000
+ # directly in the camera class. This may change in the future.
3001
+ #
3002
+
3003
+ def _openMovieFileWriterFFPyPlayer(self, filename, encoderOpts=None):
3004
+ """Open a movie file writer using the FFPyPlayer library.
3005
+
3006
+ Parameters
3007
+ ----------
3008
+ filename : str
3009
+ File to save the resulting video to, should include the extension.
3010
+ encoderOpts : dict or None
3011
+ Options to pass to the encoder. This is a dictionary of options
3012
+ specific to the encoder library being used. See the documentation
3013
+ for `~psychopy.tools.movietools.MovieFileWriter` for more details.
3014
+
3015
+ """
3016
+ from ffpyplayer.writer import MediaWriter
3017
+
3018
+ encoderOpts = encoderOpts or {}
3019
+
3020
+ # options to configure the writer
3021
+ frameWidth, frameHeight = self.frameSize
3022
+
3023
+ writerOptions = {
3024
+ 'pix_fmt_in': 'yuv420p', # default for now using mp4
3025
+ 'width_in': frameWidth,
3026
+ 'height_in': frameHeight,
3027
+ 'codec': 'libx264',
3028
+ 'frame_rate': (int(self._capture.frameRate), 1)}
3029
+
3030
+ self._curPTS = 0.0 # current pts for the movie writer
3031
+
3032
+ self._generatePTS = False # whether to generate PTS for the movie writer
3033
+ if filename.endswith('.mp4'):
3034
+ self._generatePTS = True # generate PTS for mp4 files
3035
+ logging.debug(
3036
+ "MP4 format detected, PTS will be generated for the movie " \
3037
+ "writer.")
3038
+
3039
+ self._movieWriter = MediaWriter(
3040
+ filename,
3041
+ [writerOptions],
3042
+ fmt='mp4',
3043
+ overwrite=True, # overwrite existing file
3044
+ libOpts=encoderOpts)
3045
+
3046
+ def _submitFrameToFileFFPyPlayer(self, frames):
3047
+ """Submit a frame to the movie file writer thread using FFPyPlayer.
3048
+
3049
+ This is used to submit frames to the movie file writer thread. It is
3050
+ called by the camera interface when a new frame is captured.
3051
+
3052
+ Parameters
3053
+ ----------
3054
+ frames : list of tuples
3055
+ Color data and presentation timestamps to submit to the movie file
3056
+ writer thread.
3057
+
3058
+ Returns
3059
+ -------
3060
+ int
3061
+ Number of bytes written the the movie file.
3062
+
3063
+ """
3064
+ if self._movieWriter is None:
3065
+ raise RuntimeError(
3066
+ "Attempting to call `_submitFrameToFileFFPyPlayer()` before "
3067
+ "`_openMovieFileWriterFFPyPlayer()`.")
3068
+
3069
+ from ffpyplayer.pic import SWScale
3070
+
3071
+ if not isinstance(frames, list):
3072
+ frames = [frames] # ensure frames is a list
3073
+
3074
+ # write frames to the movie file writer
3075
+ bytesOut = 0
3076
+ for colorData, pts, _ in frames:
3077
+ # do color conversion if needed
3078
+ frameWidth, frameHeight = colorData.get_size()
3079
+ sws = SWScale(
3080
+ frameWidth, frameHeight,
3081
+ colorData.get_pixel_format(),
3082
+ ofmt='yuv420p')
3083
+
3084
+ if self._generatePTS:
3085
+ pts = self._curPTS # use current for PTS
3086
+ self._curPTS += self._capture.frameInterval # increment dts by frame interval
3087
+
3088
+ bytesOut = self._movieWriter.write_frame(
3089
+ img=sws.scale(colorData),
3090
+ pts=pts,
3091
+ stream=0)
3092
+
3093
+ return bytesOut
3094
+
3095
+ def _closeMovieFileWriterFFPyPlayer(self):
3096
+ """Close the movie file writer using the FFPyPlayer library.
3097
+
3098
+ This will close the movie file writer and free up any resources used by
3099
+ the writer. If the writer is not open, this will do nothing.
3100
+ """
3101
+ if self._movieWriter is not None:
3102
+ logging.debug(
3103
+ "Closing movie file writer using FFPyPlayer...")
3104
+ self._movieWriter.close()
3105
+ else:
3106
+ logging.debug(
3107
+ "Attempting to call `_closeMovieFileWriterFFPyPlayer()` "
3108
+ "without an open movie file writer.")
3109
+
3110
+ #
3111
+ # Movie file writer methods
3112
+ #
3113
+ # These methods are used to open and close a movie file writer to save
3114
+ # frames to disk. We don't expose these methods to the user directly, but
3115
+ # they are used internally.
3116
+ #
3117
+
3118
+ def _openMovieFileWriter(self, encoderLib=None, encoderOpts=None):
3119
+ """Open a movie file writer to save frames to disk.
3120
+
3121
+ This will open a movie file writer to save frames to disk. The frames
3122
+ will be saved to a temporary file and then merged with the audio
3123
+ track (if available) when `save()` is called.
3124
+
3125
+ Parameters
3126
+ ----------
3127
+ encoderLib : str or None
3128
+ Encoder library to use for saving the video. This can be either
3129
+ `'ffpyplayer'` or `'opencv'`. If `None`, the same library that was
3130
+ used to open the camera stream. Default is `None`.
3131
+ encoderOpts : dict or None
3132
+ Options to pass to the encoder. This is a dictionary of options
3133
+ specific to the encoder library being used. See the documentation
3134
+ for `~psychopy.tools.movietools.MovieFileWriter` for more details.
3135
+
3136
+ Returns
3137
+ -------
3138
+ str
3139
+ Path to the temporary file that will be used to save the video. The
3140
+ file will be deleted when the movie file writer is closed or when
3141
+ `save()` is called.
3142
+
3143
+ """
3144
+ if self._movieWriter is not None:
3145
+ return self._tempVideoFile # already open, return temp file
3146
+
3147
+ if encoderLib is None:
3148
+ encoderLib = self._cameraLib
3149
+ logging.debug(
3150
+ "Using encoder library '{}' to save video.".format(encoderLib))
3151
+
3152
+ # check if we have a temporary file to write to
3153
+ import tempfile
3154
+ # create a temporary file to write the video to
3155
+ tempVideoFile = tempfile.NamedTemporaryFile(
3156
+ suffix='.mp4', delete=True)
3157
+ self._tempVideoFile = tempVideoFile.name
3158
+ tempVideoFile.close()
3159
+
3160
+ logging.debug("Using temporary file '{}' for video.".format(self._tempVideoFile))
3161
+
3162
+ # check if the encoder library name string is valid
3163
+ if encoderLib not in ('ffpyplayer'):
3164
+ raise ValueError(
3165
+ "Invalid value for parameter `encoderLib`, expected one of "
3166
+ "`'ffpyplayer'` or `'opencv'`.")
3167
+
3168
+ if encoderLib == 'ffpyplayer':
3169
+ self._openMovieFileWriterFFPyPlayer(
3170
+ self._tempVideoFile, encoderOpts=encoderOpts)
3171
+ else:
3172
+ raise ValueError(
3173
+ "Invalid value for parameter `encoderLib`, expected one of "
3174
+ "`'ffpyplayer'` or `'opencv'`.")
3175
+
3176
+ return self._tempVideoFile
3177
+
3178
+ def _submitFrameToFile(self, frames, pts=None):
3179
+ """Submit a frame to the movie file writer thread.
3180
+
3181
+ This is used to submit frames to the movie file writer thread. It is
3182
+ called by the camera interface when a new frame is captured.
3183
+
3184
+ Parameters
3185
+ ----------
3186
+ frames : MovieFrame
3187
+ Frame to submit to the movie file writer thread.
3188
+ pts : float or None
3189
+ Presentation timestamp for the frame. If `None`, timestamps will be
3190
+ generated automatically by the movie file writer. This is only used
3191
+ if the movie file writer is configured to generate PTS values.
3192
+
3193
+ """
3194
+ if self._movieWriter is None:
3195
+ raise RuntimeError(
3196
+ "Attempting to call `_submitFrameToFile()` before "
3197
+ "`_openMovieFileWriter()`.")
3198
+
3199
+ tStart = time.time() # start time for the operation
3200
+ if self._cameraLib == 'ffpyplayer':
3201
+ toReturn = self._submitFrameToFileFFPyPlayer(frames)
3202
+ else:
3203
+ raise ValueError(
3204
+ "Invalid value for parameter `encoderLib`, expected "
3205
+ "`'ffpyplayer'.")
3206
+
3207
+ logging.debug(
3208
+ "Submitted {} frames to the movie file writer (took {:.6f} seconds)".format(
3209
+ len(frames), time.time() - tStart))
3210
+
3211
+ return toReturn
3212
+
3213
+ def _closeMovieFileWriter(self):
3214
+ """Close the movie file writer.
3215
+
3216
+ This will close the movie file writer and free up any resources used by
3217
+ the writer. If the writer is not open, this will do nothing.
3218
+ """
3219
+ if self._movieWriter is None:
3220
+ logging.warning(
3221
+ "Attempting to call `_closeMovieFileWriter()` without an open "
3222
+ "movie file writer.")
3223
+ return
3224
+
3225
+ if self._cameraLib == 'ffpyplayer':
3226
+ self._closeMovieFileWriterFFPyPlayer()
3227
+ else:
3228
+ raise ValueError(
3229
+ "Invalid value for parameter `encoderLib`, expected one of "
3230
+ "`'ffpyplayer'` or `'opencv'`.")
3231
+
3232
+ self._movieWriter = None
3233
+
3234
+ # --------------------------------------------------------------------------
3235
+ # Destructor
3236
+ #
3237
+
3238
+ def __del__(self):
3239
+ """Try to cleanly close the camera and output file.
3240
+ """
3241
+ if hasattr(self, '_capture'):
3242
+ if self._capture is not None:
3243
+ try:
3244
+ self.close()
3245
+ except AttributeError:
3246
+ pass
3247
+
3248
+ if hasattr(self, '_movieWriter'):
3249
+ if self._movieWriter is not None:
3250
+ try:
3251
+ self._movieWriter.close()
3252
+ except AttributeError:
3253
+ pass
3254
+
3255
+
3256
+ DeviceManager.registerClassAlias("camera", "psychopy.hardware.camera.Camera")
3257
+
3258
+
3259
+ # ------------------------------------------------------------------------------
3260
+ # Functions
3261
+ #
3262
+
3263
+ def _getCameraInfoMacOS():
3264
+ """Get a list of capabilities associated with a camera attached to the
3265
+ system.
3266
+
3267
+ This is used by `getCameraInfo()` for querying camera details on MacOS.
3268
+ Don't call this function directly unless testing.
3269
+
3270
+ Returns
3271
+ -------
3272
+ list of CameraInfo
3273
+ List of camera descriptors.
3274
+
3275
+ """
3276
+ if platform.system() != 'Darwin':
3277
+ raise OSError(
3278
+ "Cannot query cameras with this function, platform not 'Darwin'.")
3279
+
3280
+ # import objc # may be needed in the future for more advanced stuff
3281
+ import AVFoundation as avf # only works on MacOS
3282
+ import CoreMedia as cm
3283
+
3284
+ # get a list of capture devices
3285
+ allDevices = avf.AVCaptureDevice.devices()
3286
+
3287
+ # get video devices
3288
+ videoDevices = {}
3289
+ devIdx = 0
3290
+ for device in allDevices:
3291
+ devFormats = device.formats()
3292
+ if devFormats[0].mediaType() != 'vide': # not a video device
3293
+ continue
3294
+
3295
+ # camera details
3296
+ cameraName = device.localizedName()
3297
+
3298
+ # found video formats
3299
+ supportedFormats = []
3300
+ for _format in devFormats:
3301
+ # get the format description object
3302
+ formatDesc = _format.formatDescription()
3303
+
3304
+ # get dimensions in pixels of the video format
3305
+ dimensions = cm.CMVideoFormatDescriptionGetDimensions(formatDesc)
3306
+ frameHeight = dimensions.height
3307
+ frameWidth = dimensions.width
3308
+
3309
+ # Extract the codec in use, pretty useless since FFMPEG uses its
3310
+ # own conventions, we'll need to map these ourselves to those
3311
+ # values
3312
+ codecType = cm.CMFormatDescriptionGetMediaSubType(formatDesc)
3313
+
3314
+ # Convert codec code to a FourCC code using the following byte
3315
+ # operations.
3316
+ #
3317
+ # fourCC = ((codecCode >> 24) & 0xff,
3318
+ # (codecCode >> 16) & 0xff,
3319
+ # (codecCode >> 8) & 0xff,
3320
+ # codecCode & 0xff)
3321
+ #
3322
+ pixelFormat4CC = ''.join(
3323
+ [chr((codecType >> bits) & 0xff) for bits in (24, 16, 8, 0)])
3324
+
3325
+ # Get the range of supported framerate, use the largest since the
3326
+ # ranges are rarely variable within a format.
3327
+ frameRateRange = _format.videoSupportedFrameRateRanges()[0]
3328
+ frameRateMax = frameRateRange.maxFrameRate()
3329
+ # frameRateMin = frameRateRange.minFrameRate() # don't use for now
3330
+
3331
+ # Create a new camera descriptor
3332
+ thisCamInfo = CameraInfo(
3333
+ index=devIdx,
3334
+ name=cameraName,
3335
+ pixelFormat=pixelFormat4CC, # macs only use pixel format
3336
+ codecFormat=CAMERA_NULL_VALUE,
3337
+ frameSize=(int(frameWidth), int(frameHeight)),
3338
+ frameRate=frameRateMax,
3339
+ cameraAPI=CAMERA_API_AVFOUNDATION,
3340
+ cameraLib="ffpyplayer",
3341
+ )
3342
+
3343
+ supportedFormats.append(thisCamInfo)
3344
+
3345
+ devIdx += 1
3346
+
3347
+ # add to output dictionary
3348
+ videoDevices[cameraName] = supportedFormats
3349
+
3350
+ return videoDevices
3351
+
3352
+
3353
+ def _getCameraInfoWindows():
3354
+ """Get a list of capabilities for the specified associated with a camera
3355
+ attached to the system.
3356
+
3357
+ This is used by `getCameraInfo()` for querying camera details on Windows.
3358
+ Don't call this function directly unless testing.
3359
+
3360
+ Returns
3361
+ -------
3362
+ list of CameraInfo
3363
+ List of camera descriptors.
3364
+
3365
+ """
3366
+ if platform.system() != 'Windows':
3367
+ raise OSError(
3368
+ "Cannot query cameras with this function, platform not 'Windows'.")
3369
+
3370
+ # FFPyPlayer can query the OS via DirectShow for Windows cameras
3371
+ from ffpyplayer.tools import list_dshow_devices
3372
+ videoDevs, _, names = list_dshow_devices()
3373
+
3374
+ # get all the supported modes for the camera
3375
+ videoDevices = {}
3376
+
3377
+ # iterate over names
3378
+ devIndex = 0
3379
+ for devURI in videoDevs.keys():
3380
+ supportedFormats = []
3381
+ cameraName = names[devURI]
3382
+ for _format in videoDevs[devURI]:
3383
+ pixelFormat, codecFormat, frameSize, frameRateRng = _format
3384
+ _, frameRateMax = frameRateRng
3385
+ temp = CameraInfo(
3386
+ index=devIndex,
3387
+ name=cameraName,
3388
+ pixelFormat=pixelFormat,
3389
+ codecFormat=codecFormat,
3390
+ frameSize=frameSize,
3391
+ frameRate=frameRateMax,
3392
+ cameraAPI=CAMERA_API_DIRECTSHOW,
3393
+ cameraLib="ffpyplayer",
3394
+ )
3395
+ supportedFormats.append(temp)
3396
+ devIndex += 1
3397
+
3398
+ videoDevices[names[devURI]] = supportedFormats
3399
+
3400
+ return videoDevices
3401
+
3402
+
3403
+ # Mapping for platform specific camera getter functions used by `getCameras`.
3404
+ _cameraGetterFuncTbl = {
3405
+ 'Darwin': _getCameraInfoMacOS,
3406
+ 'Windows': _getCameraInfoWindows
3407
+ }
3408
+
3409
+
3410
+ def getCameras():
3411
+ """Get information about installed cameras and their formats on this system.
3412
+
3413
+ Use `getCameraDescriptions` to get a mapping or list of human-readable
3414
+ camera formats.
3415
+
3416
+ Returns
3417
+ -------
3418
+ dict
3419
+ Mapping where camera names (`str`) are keys and values are and array of
3420
+ `CameraInfo` objects.
3421
+
3422
+ """
3423
+ systemName = platform.system() # get the system name
3424
+
3425
+ # lookup the function for the given platform
3426
+ getCamerasFunc = _cameraGetterFuncTbl.get(systemName, None)
3427
+ if getCamerasFunc is None: # if unsupported
3428
+ raise OSError(
3429
+ "Cannot get cameras, unsupported platform '{}'.".format(
3430
+ systemName))
3431
+
3432
+ return getCamerasFunc()
3433
+
3434
+
3435
+ def getCameraDescriptions(collapse=False):
3436
+ """Get a mapping or list of camera descriptions.
3437
+
3438
+ Camera descriptions are a compact way of representing camera settings and
3439
+ formats. Description strings can be used to specify which camera device and
3440
+ format to use with it to the `Camera` class.
3441
+
3442
+ Descriptions have the following format (example)::
3443
+
3444
+ '[Live! Cam Sync 1080p] 160x120@30fps, mjpeg'
3445
+
3446
+ This shows a specific camera format for the 'Live! Cam Sync 1080p' webcam
3447
+ which supports 160x120 frame size at 30 frames per second. The last value
3448
+ is the codec or pixel format used to decode the stream. Different pixel
3449
+ formats and codecs vary in performance.
3450
+
3451
+ Parameters
3452
+ ----------
3453
+ collapse : bool
3454
+ Return camera information as string descriptions instead of `CameraInfo`
3455
+ objects. This provides a more compact way of representing camera formats
3456
+ in a (reasonably) human-readable format.
3457
+
3458
+ Returns
3459
+ -------
3460
+ dict or list
3461
+ Mapping (`dict`) of camera descriptions, where keys are camera names
3462
+ (`str`) and values are a `list` of format description strings associated
3463
+ with the camera. If `collapse=True`, all descriptions will be returned
3464
+ in a single flat list. This might be more useful for specifying camera
3465
+ formats from a single GUI list control.
3466
+
3467
+ """
3468
+ connectedCameras = getCameras()
3469
+
3470
+ cameraDescriptions = {}
3471
+ for devName, formats in connectedCameras.items():
3472
+ cameraDescriptions[devName] = [
3473
+ _format.description() for _format in formats]
3474
+
3475
+ if not collapse:
3476
+ return cameraDescriptions
3477
+
3478
+ # collapse to a list if requested
3479
+ collapsedList = []
3480
+ for _, formatDescs in cameraDescriptions.items():
3481
+ collapsedList.extend(formatDescs)
3482
+
3483
+ return collapsedList
3484
+
3485
+
3486
+ def getFormatsForDevice(device):
3487
+ """Get a list of formats available for the given device.
3488
+
3489
+ Parameters
3490
+ ----------
3491
+ device : str or int
3492
+ Name or index of the device
3493
+
3494
+ Returns
3495
+ -------
3496
+ list
3497
+ List of formats, specified as strings in the format
3498
+ `{width}x{height}@{frame rate}fps`
3499
+ """
3500
+ # get all devices
3501
+ connectedCameras = getCameras()
3502
+ # get formats for this device
3503
+ formats = connectedCameras.get(device, [])
3504
+ # sanitize
3505
+ formats = [f"{_format.frameSize[0]}x{_format.frameSize[1]}@{_format.frameRate}fps" for _format in formats]
3506
+
3507
+ return formats
3508
+
3509
+
3510
+ def getAllCameraInterfaces():
3511
+ """Get a list of all camera interfaces supported by the system.
3512
+
3513
+ Returns
3514
+ -------
3515
+ dict
3516
+ Mapping of camera interface class names and references to the class.
3517
+
3518
+ """
3519
+ # get all classes in this module
3520
+ classes = inspect.getmembers(sys.modules[__name__], inspect.isclass)
3521
+
3522
+ # filter for classes that are camera interfaces
3523
+ cameraInterfaces = {}
3524
+ for name, cls in classes:
3525
+ if issubclass(cls, CameraDevice):
3526
+ cameraInterfaces[name] = cls
3527
+
3528
+ return cameraInterfaces
3529
+
3530
+
3531
+ def getOpenCameras():
3532
+ """Get a list of all open cameras.
3533
+
3534
+ Returns
3535
+ -------
3536
+ list
3537
+ List of references to open camera objects.
3538
+
3539
+ """
3540
+ global _openCameras
3541
+
3542
+ return _openCameras.copy()
3543
+
3544
+
3545
+ def closeAllOpenCameras():
3546
+ """Close all open cameras.
3547
+
3548
+ This closes all open cameras and releases any resources associated with
3549
+ them. This should only be called before exiting the application or after you
3550
+ are done using the cameras.
3551
+
3552
+ This is automatically called when the application exits to cleanly free up
3553
+ resources, as it is registered with `atexit` when the module is imported.
3554
+
3555
+ Returns
3556
+ -------
3557
+ int
3558
+ Number of cameras closed. Useful for debugging to ensure all cameras
3559
+ were closed.
3560
+
3561
+ """
3562
+ global _openCameras
3563
+
3564
+ numCameras = len(_openCameras)
3565
+ for cam in _openCameras:
3566
+ cam.close()
3567
+
3568
+ _openCameras.clear()
3569
+
3570
+ return numCameras
3571
+
3572
+
3573
+ def renderVideo(outputFile, videoFile, audioFile=None, removeFiles=False):
3574
+ """Render a video.
3575
+
3576
+ Combine visual and audio streams into a single movie file. This is used
3577
+ mainly for compositing video and audio data for the camera. Video and audio
3578
+ should have roughly the same duration.
3579
+
3580
+ This is a legacy function used originally for compositing video and audio
3581
+ data from the camera. It is not used anymore internally, but is kept here
3582
+ for reference and may be removed in the future. If you need to composite
3583
+ video and audio data, use `movietools.addAudioToMovie` instead.
3584
+
3585
+ Parameters
3586
+ ----------
3587
+ outputFile : str
3588
+ Filename to write the movie to. Should have the extension of the file
3589
+ too.
3590
+ videoFile : str
3591
+ Video file path.
3592
+ audioFile : str or None
3593
+ Audio file path. If not provided the movie file will simply be copied
3594
+ to `outFile`.
3595
+ removeFiles : bool
3596
+ If `True`, the video (`videoFile`) and audio (`audioFile`) files will be
3597
+ deleted after the movie is rendered.
3598
+
3599
+ Returns
3600
+ -------
3601
+ int
3602
+ Size of the resulting file in bytes.
3603
+
3604
+ """
3605
+ # if no audio file, just copy the video file
3606
+ if audioFile is None:
3607
+ import shutil
3608
+ shutil.copyfile(videoFile, outputFile)
3609
+ if removeFiles:
3610
+ os.remove(videoFile) # delete the old movie file
3611
+ return os.path.getsize(outputFile)
3612
+
3613
+ # merge video and audio, now using the new `movietools` module
3614
+ movietools.addAudioToMovie(
3615
+ videoFile,
3616
+ audioFile,
3617
+ outputFile,
3618
+ useThreads=False, # didn't use this before
3619
+ removeFiles=removeFiles)
3620
+
3621
+ return os.path.getsize(outputFile)
3622
+
3623
+
3624
+ # ------------------------------------------------------------------------------
3625
+ # Cleanup functions
3626
+ #
3627
+ # These functions are used to clean up resources when the application exits,
3628
+ # usually unexpectedly. This helps to ensure hardware interfaces are closed
3629
+ # and resources are freed up as best we can.
3630
+ #
3631
+
3632
+ import atexit
3633
+
3634
+
3635
+ def _closeAllCaptureInterfaces():
3636
+ """Close all open capture interfaces.
3637
+
3638
+ This is registered with `atexit` to ensure that all open cameras are closed
3639
+ when the application exits. This is important to free up resources and
3640
+ ensure that cameras are not left open unintentionally.
3641
+
3642
+ """
3643
+ global _openCaptureInterfaces
3644
+
3645
+ for cap in _openCaptureInterfaces.copy():
3646
+ try:
3647
+ cap.close()
3648
+ except Exception as e:
3649
+ logging.error(f"Error closing camera interface {cap}: {e}")
3650
+
3651
+
3652
+ # Register the function to close all cameras on exit
3653
+ atexit.register(_closeAllCaptureInterfaces)
3654
+
3655
+ # ------------------------------------------------------------------------------
3656
+ if __name__ == "__main__":
3657
+ pass