psychopy 2024.2.5__py3-none-any.whl → 2025.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of psychopy might be problematic. Click here for more details.

Files changed (863) hide show
  1. psychopy/CHANGELOG.txt +4 -4
  2. psychopy/GIT_SHA +1 -1
  3. psychopy/LICENSE.txt +1 -1
  4. psychopy/VERSION +1 -1
  5. psychopy/__init__.py +10 -7
  6. psychopy/alerts/__init__.py +1 -1
  7. psychopy/alerts/_alerts.py +53 -17
  8. psychopy/alerts/_errorHandler.py +3 -4
  9. psychopy/alerts/alertsCatalogue/3210.yaml +27 -0
  10. psychopy/alerts/alertsCatalogue/3610.yaml +19 -0
  11. psychopy/alerts/alertsCatalogue/4130.yaml +19 -0
  12. psychopy/alerts/alertsCatalogue/alertCategories.yaml +8 -1
  13. psychopy/alerts/alertsCatalogue/alertmsg.py +1 -1
  14. psychopy/alerts/alerttools.py +0 -16
  15. psychopy/app/Resources/betasplash.png +0 -0
  16. psychopy/app/Resources/betasplash@2x.png +0 -0
  17. psychopy/app/Resources/classic/case.png +0 -0
  18. psychopy/app/Resources/classic/case@2x.png +0 -0
  19. psychopy/app/Resources/classic/fileaudio.png +0 -0
  20. psychopy/app/Resources/classic/fileaudio@2x.png +0 -0
  21. psychopy/app/Resources/classic/filecss.png +0 -0
  22. psychopy/app/Resources/classic/filecss@2x.png +0 -0
  23. psychopy/app/Resources/classic/filecsv.png +0 -0
  24. psychopy/app/Resources/classic/filecsv@2x.png +0 -0
  25. psychopy/app/Resources/classic/filedesign.png +0 -0
  26. psychopy/app/Resources/classic/filedesign@2x.png +0 -0
  27. psychopy/app/Resources/classic/filefont.png +0 -0
  28. psychopy/app/Resources/classic/filefont@2x.png +0 -0
  29. psychopy/app/Resources/classic/filegit.png +0 -0
  30. psychopy/app/Resources/classic/filegit@2x.png +0 -0
  31. psychopy/app/Resources/classic/filehtml.png +0 -0
  32. psychopy/app/Resources/classic/filehtml@2x.png +0 -0
  33. psychopy/app/Resources/classic/fileimage.png +0 -0
  34. psychopy/app/Resources/classic/fileimage@2x.png +0 -0
  35. psychopy/app/Resources/classic/fileinfo.png +0 -0
  36. psychopy/app/Resources/classic/fileinfo@2x.png +0 -0
  37. psychopy/app/Resources/classic/filejs.png +0 -0
  38. psychopy/app/Resources/classic/filejs@2x.png +0 -0
  39. psychopy/app/Resources/classic/filejson.png +0 -0
  40. psychopy/app/Resources/classic/filejson@2x.png +0 -0
  41. psychopy/app/Resources/classic/filepkg.png +0 -0
  42. psychopy/app/Resources/classic/filepkg@2x.png +0 -0
  43. psychopy/app/Resources/classic/filepsyexp.png +0 -0
  44. psychopy/app/Resources/classic/filepsyexp@2x.png +0 -0
  45. psychopy/app/Resources/classic/filepy.png +0 -0
  46. psychopy/app/Resources/classic/filepy@2x.png +0 -0
  47. psychopy/app/Resources/classic/filetxt.png +0 -0
  48. psychopy/app/Resources/classic/filetxt@2x.png +0 -0
  49. psychopy/app/Resources/classic/fileunknown.png +0 -0
  50. psychopy/app/Resources/classic/fileunknown@2x.png +0 -0
  51. psychopy/app/Resources/classic/filevideo.png +0 -0
  52. psychopy/app/Resources/classic/filevideo@2x.png +0 -0
  53. psychopy/app/Resources/classic/find.png +0 -0
  54. psychopy/app/Resources/classic/find@2x.png +0 -0
  55. psychopy/app/Resources/classic/loop.png +0 -0
  56. psychopy/app/Resources/classic/loop@2x.png +0 -0
  57. psychopy/app/Resources/classic/regex.png +0 -0
  58. psychopy/app/Resources/classic/regex@2x.png +0 -0
  59. psychopy/app/Resources/dark/case.png +0 -0
  60. psychopy/app/Resources/dark/case@2x.png +0 -0
  61. psychopy/app/Resources/dark/fileaudio.png +0 -0
  62. psychopy/app/Resources/dark/fileaudio@2x.png +0 -0
  63. psychopy/app/Resources/dark/filecss.png +0 -0
  64. psychopy/app/Resources/dark/filecss@2x.png +0 -0
  65. psychopy/app/Resources/dark/filecsv.png +0 -0
  66. psychopy/app/Resources/dark/filecsv@2x.png +0 -0
  67. psychopy/app/Resources/dark/filedesign.png +0 -0
  68. psychopy/app/Resources/dark/filedesign@2x.png +0 -0
  69. psychopy/app/Resources/dark/filefont.png +0 -0
  70. psychopy/app/Resources/dark/filefont@2x.png +0 -0
  71. psychopy/app/Resources/dark/filegit.png +0 -0
  72. psychopy/app/Resources/dark/filegit@2x.png +0 -0
  73. psychopy/app/Resources/dark/filehtml.png +0 -0
  74. psychopy/app/Resources/dark/filehtml@2x.png +0 -0
  75. psychopy/app/Resources/dark/fileimage.png +0 -0
  76. psychopy/app/Resources/dark/fileimage@2x.png +0 -0
  77. psychopy/app/Resources/dark/fileinfo.png +0 -0
  78. psychopy/app/Resources/dark/fileinfo@2x.png +0 -0
  79. psychopy/app/Resources/dark/filejs.png +0 -0
  80. psychopy/app/Resources/dark/filejs@2x.png +0 -0
  81. psychopy/app/Resources/dark/filejson.png +0 -0
  82. psychopy/app/Resources/dark/filejson@2x.png +0 -0
  83. psychopy/app/Resources/dark/filepkg.png +0 -0
  84. psychopy/app/Resources/dark/filepkg@2x.png +0 -0
  85. psychopy/app/Resources/dark/filepsyexp.png +0 -0
  86. psychopy/app/Resources/dark/filepsyexp@2x.png +0 -0
  87. psychopy/app/Resources/dark/filepy.png +0 -0
  88. psychopy/app/Resources/dark/filepy@2x.png +0 -0
  89. psychopy/app/Resources/dark/filetxt.png +0 -0
  90. psychopy/app/Resources/dark/filetxt@2x.png +0 -0
  91. psychopy/app/Resources/dark/fileunknown.png +0 -0
  92. psychopy/app/Resources/dark/fileunknown@2x.png +0 -0
  93. psychopy/app/Resources/dark/filevideo.png +0 -0
  94. psychopy/app/Resources/dark/filevideo@2x.png +0 -0
  95. psychopy/app/Resources/dark/find.png +0 -0
  96. psychopy/app/Resources/dark/find@2x.png +0 -0
  97. psychopy/app/Resources/dark/loop.png +0 -0
  98. psychopy/app/Resources/dark/loop@2x.png +0 -0
  99. psychopy/app/Resources/dark/regex.png +0 -0
  100. psychopy/app/Resources/dark/regex@2x.png +0 -0
  101. psychopy/app/Resources/light/case.png +0 -0
  102. psychopy/app/Resources/light/case@2x.png +0 -0
  103. psychopy/app/Resources/light/fileaudio.png +0 -0
  104. psychopy/app/Resources/light/fileaudio@2x.png +0 -0
  105. psychopy/app/Resources/light/filecss.png +0 -0
  106. psychopy/app/Resources/light/filecss@2x.png +0 -0
  107. psychopy/app/Resources/light/filecsv.png +0 -0
  108. psychopy/app/Resources/light/filecsv@2x.png +0 -0
  109. psychopy/app/Resources/light/filedesign.png +0 -0
  110. psychopy/app/Resources/light/filedesign@2x.png +0 -0
  111. psychopy/app/Resources/light/filefont.png +0 -0
  112. psychopy/app/Resources/light/filefont@2x.png +0 -0
  113. psychopy/app/Resources/light/filegit.png +0 -0
  114. psychopy/app/Resources/light/filegit@2x.png +0 -0
  115. psychopy/app/Resources/light/filehtml.png +0 -0
  116. psychopy/app/Resources/light/filehtml@2x.png +0 -0
  117. psychopy/app/Resources/light/fileimage.png +0 -0
  118. psychopy/app/Resources/light/fileimage@2x.png +0 -0
  119. psychopy/app/Resources/light/fileinfo.png +0 -0
  120. psychopy/app/Resources/light/fileinfo@2x.png +0 -0
  121. psychopy/app/Resources/light/filejs.png +0 -0
  122. psychopy/app/Resources/light/filejs@2x.png +0 -0
  123. psychopy/app/Resources/light/filejson.png +0 -0
  124. psychopy/app/Resources/light/filejson@2x.png +0 -0
  125. psychopy/app/Resources/light/filepkg.png +0 -0
  126. psychopy/app/Resources/light/filepkg@2x.png +0 -0
  127. psychopy/app/Resources/light/filepsyexp.png +0 -0
  128. psychopy/app/Resources/light/filepsyexp@2x.png +0 -0
  129. psychopy/app/Resources/light/filepy.png +0 -0
  130. psychopy/app/Resources/light/filepy@2x.png +0 -0
  131. psychopy/app/Resources/light/filetxt.png +0 -0
  132. psychopy/app/Resources/light/filetxt@2x.png +0 -0
  133. psychopy/app/Resources/light/fileunknown.png +0 -0
  134. psychopy/app/Resources/light/fileunknown@2x.png +0 -0
  135. psychopy/app/Resources/light/filevideo.png +0 -0
  136. psychopy/app/Resources/light/filevideo@2x.png +0 -0
  137. psychopy/app/Resources/light/find.png +0 -0
  138. psychopy/app/Resources/light/find@2x.png +0 -0
  139. psychopy/app/Resources/light/loop.png +0 -0
  140. psychopy/app/Resources/light/loop@2x.png +0 -0
  141. psychopy/app/Resources/light/regex.png +0 -0
  142. psychopy/app/Resources/light/regex@2x.png +0 -0
  143. psychopy/app/Resources/routine_templates/Basic.psyexp +0 -1
  144. psychopy/app/Resources/routine_templates/Misc.psyexp +0 -1
  145. psychopy/app/Resources/routine_templates/Online.psyexp +0 -2
  146. psychopy/app/Resources/routine_templates/Trials.psyexp +0 -1
  147. psychopy/app/Resources/splash.png +0 -0
  148. psychopy/app/Resources/splash@2x.png +0 -0
  149. psychopy/app/__init__.py +49 -8
  150. psychopy/app/__main__.py +3 -0
  151. psychopy/app/_psychopyApp.py +134 -125
  152. psychopy/app/builder/builder.py +42 -22
  153. psychopy/app/builder/dialogs/__init__.py +44 -11
  154. psychopy/app/builder/dialogs/dlgsCode.py +1 -1
  155. psychopy/app/builder/dialogs/dlgsConditions.py +1 -1
  156. psychopy/app/builder/dialogs/findDlg.py +106 -20
  157. psychopy/app/builder/dialogs/paramCtrls.py +42 -1
  158. psychopy/app/builder/validators.py +1 -1
  159. psychopy/app/coder/codeEditorBase.py +8 -8
  160. psychopy/app/coder/coder.py +32 -29
  161. psychopy/app/coder/fileBrowser.py +68 -22
  162. psychopy/app/coder/folding.py +1 -1
  163. psychopy/app/coder/psychoParser.py +1 -1
  164. psychopy/app/coder/repl.py +1 -1
  165. psychopy/app/coder/sourceTree.py +1 -1
  166. psychopy/app/connections/__init__.py +1 -1
  167. psychopy/app/connections/news.py +1 -1
  168. psychopy/app/connections/sendusage.py +1 -1
  169. psychopy/app/connections/updates.py +1 -1
  170. psychopy/app/console.py +1 -1
  171. psychopy/app/errorDlg.py +1 -1
  172. psychopy/app/frametracker.py +1 -1
  173. psychopy/app/idle.py +1 -1
  174. psychopy/app/jobs.py +5 -2
  175. psychopy/app/linuxconfig/__init__.py +1 -1
  176. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  177. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +4 -4
  178. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  179. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  180. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  181. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +3 -3
  182. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  183. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  184. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  185. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  186. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +4 -4
  187. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  188. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +4 -4
  189. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  190. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +4 -4
  191. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  192. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +2 -2
  193. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  194. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +1 -1
  195. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  196. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  197. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +2 -2
  198. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  199. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +2 -2
  200. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  201. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +2 -2
  202. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  203. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  204. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +2 -2
  205. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  206. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +3421 -2396
  207. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  208. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  209. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +2 -2
  210. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  211. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  212. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  213. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  214. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +4 -4
  215. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  216. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  217. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  218. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +2 -2
  219. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  220. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +2 -2
  221. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  222. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +3 -3
  223. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  224. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +2 -2
  225. psychopy/app/{builder/localizedStrings.py → localizedStrings.py} +173 -26
  226. psychopy/app/pavlovia_ui/__init__.py +1 -1
  227. psychopy/app/pavlovia_ui/_base.py +1 -1
  228. psychopy/app/pavlovia_ui/functions.py +1 -1
  229. psychopy/app/pavlovia_ui/menu.py +1 -1
  230. psychopy/app/pavlovia_ui/project.py +1 -1
  231. psychopy/app/pavlovia_ui/search.py +1 -1
  232. psychopy/app/pavlovia_ui/sync.py +1 -1
  233. psychopy/app/pavlovia_ui/user.py +1 -1
  234. psychopy/app/plugin_manager/dialog.py +34 -96
  235. psychopy/app/plugin_manager/packages.py +10 -14
  236. psychopy/app/plugin_manager/plugins.py +64 -4
  237. psychopy/app/preferencesDlg.py +12 -37
  238. psychopy/app/psychopyApp.py +130 -44
  239. psychopy/app/ribbon.py +1 -0
  240. psychopy/app/runner/runner.py +19 -7
  241. psychopy/app/runner/scriptProcess.py +11 -6
  242. psychopy/app/stdout/stdOutRich.py +9 -2
  243. psychopy/app/themes/fonts.py +1 -1
  244. psychopy/app/themes/icons.py +2 -38
  245. psychopy/app/ui/__init__.py +1 -1
  246. psychopy/app/utils.py +3 -3
  247. psychopy/assets/default.mp3 +0 -0
  248. psychopy/assets/fonts/NotoSans-Bold.ttf +0 -0
  249. psychopy/assets/fonts/NotoSans-BoldItalic.ttf +0 -0
  250. psychopy/assets/fonts/NotoSans-Italic.ttf +0 -0
  251. psychopy/assets/fonts/NotoSans-Regular.ttf +0 -0
  252. psychopy/assets/voicekeyThresholdStim.wav +0 -0
  253. psychopy/clock.py +4 -1
  254. psychopy/colors.py +12 -5
  255. psychopy/core.py +1 -1
  256. psychopy/data/base.py +1 -1
  257. psychopy/data/experiment.py +151 -46
  258. psychopy/data/routine.py +27 -1
  259. psychopy/data/staircase.py +2 -1
  260. psychopy/data/trial.py +62 -14
  261. psychopy/data/utils.py +2 -2
  262. psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +333 -218
  263. psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +261 -239
  264. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +319 -180
  265. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +204 -116
  266. psychopy/demos/builder/Experiments/BART/assets/background.jpg +0 -0
  267. psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
  268. psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
  269. psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
  270. psychopy/demos/builder/Experiments/BART/bart.psyexp +779 -866
  271. psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +242 -180
  272. psychopy/demos/builder/Experiments/GoNoGo/gng.psyexp +419 -406
  273. psychopy/demos/builder/Experiments/dragAndDrop/README.md +2 -37
  274. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +460 -1204
  275. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/blank_grid.png +0 -0
  276. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/make_shapes.psyexp +221 -0
  277. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/readme.md +4 -0
  278. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_1.png +0 -0
  279. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_10.png +0 -0
  280. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_2.png +0 -0
  281. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_3.png +0 -0
  282. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_4.png +0 -0
  283. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_5.png +0 -0
  284. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_6.png +0 -0
  285. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_7.png +0 -0
  286. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_8.png +0 -0
  287. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_9.png +0 -0
  288. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  289. psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +583 -542
  290. psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +458 -427
  291. psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +588 -550
  292. psychopy/demos/builder/Experiments/stroop/stroop.psyexp +303 -207
  293. psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +390 -215
  294. psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +390 -215
  295. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +357 -331
  296. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -2
  297. psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +287 -277
  298. psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +370 -320
  299. psychopy/demos/builder/Feature Demos/noise/noise.psyexp +452 -399
  300. psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +168 -133
  301. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +4 -3
  302. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +420 -392
  303. psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +917 -871
  304. psychopy/demos/builder/Feature Demos/visualValidator/readme.md +7 -0
  305. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +200 -0
  306. psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +25 -9
  307. psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +372 -361
  308. psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +25 -9
  309. psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +320 -235
  310. psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +790 -651
  311. psychopy/demos/builder/Hardware/camera/camera.psyexp +326 -246
  312. psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +432 -327
  313. psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +440 -321
  314. psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +190 -181
  315. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +323 -312
  316. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy.psyexp +360 -0
  317. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy_legacy.psyexp +312 -0
  318. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +329 -273
  319. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy.psyexp +360 -0
  320. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy_legacy.psyexp +312 -0
  321. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +450 -404
  322. psychopy/demos/builder/Hardware/pump/pump.psyexp +593 -329
  323. psychopy/demos/builder/Helper Tools/achorVSalignment/FlowCircular-Regular.ttf +0 -0
  324. psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +336 -274
  325. psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +200 -149
  326. psychopy/demos/builder/Helper Tools/colors/colors.psyexp +300 -279
  327. psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +677 -564
  328. psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +214 -158
  329. psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +195 -146
  330. psychopy/demos/coder/experiment control/runtimeInfo.py +1 -1
  331. psychopy/devices/__init__.py +1 -1
  332. psychopy/event.py +1 -1
  333. psychopy/exceptions.py +1 -1
  334. psychopy/experiment/__init__.py +1 -1
  335. psychopy/experiment/_experiment.py +30 -9
  336. psychopy/experiment/components/__init__.py +1 -6
  337. psychopy/experiment/components/_base.py +44 -19
  338. psychopy/experiment/components/aperture/__init__.py +1 -1
  339. psychopy/experiment/components/brush/__init__.py +2 -2
  340. psychopy/experiment/components/button/__init__.py +24 -28
  341. psychopy/experiment/components/camera/__init__.py +38 -39
  342. psychopy/experiment/components/code/__init__.py +1 -1
  343. psychopy/experiment/components/dots/__init__.py +1 -1
  344. psychopy/experiment/components/eyetracker_record/__init__.py +7 -3
  345. psychopy/experiment/components/form/__init__.py +3 -3
  346. psychopy/experiment/components/grating/__init__.py +1 -1
  347. psychopy/experiment/components/image/__init__.py +1 -1
  348. psychopy/experiment/components/joyButtons/__init__.py +2 -2
  349. psychopy/experiment/components/joyButtons/virtualJoyButtons.py +1 -1
  350. psychopy/experiment/components/joystick/__init__.py +3 -3
  351. psychopy/experiment/components/joystick/virtualJoystick.py +1 -1
  352. psychopy/experiment/components/keyboard/__init__.py +98 -122
  353. psychopy/experiment/components/microphone/__init__.py +54 -98
  354. psychopy/experiment/components/mouse/__init__.py +92 -93
  355. psychopy/experiment/components/movie/__init__.py +28 -50
  356. psychopy/experiment/components/parallelOut/__init__.py +3 -3
  357. psychopy/experiment/components/polygon/__init__.py +2 -3
  358. psychopy/experiment/components/roi/__init__.py +2 -2
  359. psychopy/experiment/components/routineSettings/__init__.py +2 -0
  360. psychopy/experiment/components/serialOut/__init__.py +7 -7
  361. psychopy/experiment/components/settings/__init__.py +317 -313
  362. psychopy/experiment/components/settings/eyetracking.py +108 -0
  363. psychopy/experiment/components/slider/__init__.py +5 -5
  364. psychopy/experiment/components/sound/__init__.py +168 -78
  365. psychopy/experiment/components/soundsensor/__init__.py +361 -0
  366. psychopy/experiment/components/soundsensor/classic/soundsensor.png +0 -0
  367. psychopy/experiment/components/soundsensor/classic/soundsensor@2x.png +0 -0
  368. psychopy/experiment/components/soundsensor/dark/soundsensor.png +0 -0
  369. psychopy/experiment/components/soundsensor/dark/soundsensor@2x.png +0 -0
  370. psychopy/experiment/components/soundsensor/light/soundsensor.png +0 -0
  371. psychopy/experiment/components/soundsensor/light/soundsensor@2x.png +0 -0
  372. psychopy/experiment/components/static/__init__.py +59 -33
  373. psychopy/experiment/components/text/__init__.py +2 -6
  374. psychopy/experiment/components/textbox/__init__.py +3 -7
  375. psychopy/experiment/components/unknown/__init__.py +2 -0
  376. psychopy/experiment/components/unknownPlugin/__init__.py +2 -0
  377. psychopy/experiment/exports.py +1 -1
  378. psychopy/experiment/flow.py +2 -2
  379. psychopy/experiment/localization.py +1 -1
  380. psychopy/experiment/loops.py +43 -10
  381. psychopy/experiment/params.py +6 -4
  382. psychopy/experiment/plugins.py +8 -1
  383. psychopy/experiment/py2js.py +1 -1
  384. psychopy/experiment/py2js_transpiler.py +1 -1
  385. psychopy/experiment/routines/__init__.py +1 -1
  386. psychopy/experiment/routines/_base.py +23 -24
  387. psychopy/experiment/routines/audioValidator/__init__.py +343 -0
  388. psychopy/experiment/routines/audioValidator/classic/audio_validator.png +0 -0
  389. psychopy/experiment/routines/audioValidator/classic/audio_validator@2x.png +0 -0
  390. psychopy/experiment/routines/audioValidator/dark/audio_validator.png +0 -0
  391. psychopy/experiment/routines/audioValidator/dark/audio_validator@2x.png +0 -0
  392. psychopy/experiment/routines/audioValidator/light/audio_validator.png +0 -0
  393. psychopy/experiment/routines/audioValidator/light/audio_validator@2x.png +0 -0
  394. psychopy/experiment/routines/eyetracker_calibrate/__init__.py +76 -17
  395. psychopy/experiment/routines/eyetracker_validate/__init__.py +1 -1
  396. psychopy/experiment/routines/unknown/__init__.py +2 -0
  397. psychopy/experiment/routines/{photodiodeValidator → visualValidator}/__init__.py +89 -120
  398. psychopy/experiment/routines/visualValidator/classic/visual_validator.png +0 -0
  399. psychopy/experiment/routines/visualValidator/classic/visual_validator@2x.png +0 -0
  400. psychopy/experiment/routines/visualValidator/dark/visual_validator.png +0 -0
  401. psychopy/experiment/routines/visualValidator/dark/visual_validator@2x.png +0 -0
  402. psychopy/experiment/routines/visualValidator/light/visual_validator.png +0 -0
  403. psychopy/experiment/routines/visualValidator/light/visual_validator@2x.png +0 -0
  404. psychopy/experiment/utils.py +1 -1
  405. psychopy/gui/__init__.py +1 -1
  406. psychopy/gui/qtgui.py +15 -6
  407. psychopy/gui/wxgui.py +1 -1
  408. psychopy/hardware/__init__.py +0 -1
  409. psychopy/hardware/base.py +147 -16
  410. psychopy/hardware/bbtk/__init__.py +10 -16
  411. psychopy/hardware/brainproducts.py +7 -13
  412. psychopy/hardware/button.py +21 -2
  413. psychopy/hardware/buttonbox/__init__.py +1 -1
  414. psychopy/hardware/camera/__init__.py +18 -5
  415. psychopy/hardware/cedrus.py +5 -16
  416. psychopy/hardware/crs/__init__.py +1 -1
  417. psychopy/hardware/crs/bits.py +36 -33
  418. psychopy/hardware/crs/colorcal.py +8 -11
  419. psychopy/hardware/crs/optical.py +7 -10
  420. psychopy/hardware/crs/shaders.py +18 -5
  421. psychopy/hardware/emotiv.py +1 -1
  422. psychopy/hardware/emulator.py +11 -5
  423. psychopy/hardware/exceptions.py +86 -0
  424. psychopy/hardware/forp.py +18 -23
  425. psychopy/hardware/gammasci.py +8 -3
  426. psychopy/hardware/iolab.py +8 -19
  427. psychopy/hardware/joystick/__init__.py +865 -266
  428. psychopy/hardware/joystick/_base.py +251 -0
  429. psychopy/hardware/joystick/backend_glfw.py +306 -0
  430. psychopy/hardware/joystick/backend_pyglet.py +309 -0
  431. psychopy/hardware/joystick/mappings.py +287 -0
  432. psychopy/hardware/keyboard.py +4 -2
  433. psychopy/hardware/labhackers.py +1 -1
  434. psychopy/hardware/labjacks.py +9 -13
  435. psychopy/hardware/{photodiode.py → lightsensor.py} +146 -203
  436. psychopy/hardware/listener.py +9 -8
  437. psychopy/hardware/manager.py +24 -35
  438. psychopy/hardware/microphone.py +535 -155
  439. psychopy/hardware/minolta.py +14 -4
  440. psychopy/hardware/mouse/__init__.py +1 -1
  441. psychopy/hardware/photometer/__init__.py +2 -2
  442. psychopy/hardware/pr.py +14 -4
  443. psychopy/hardware/qmix.py +18 -27
  444. psychopy/hardware/serialdevice.py +43 -12
  445. psychopy/hardware/soundsensor.py +473 -0
  446. psychopy/hardware/spatial/__init__.py +231 -0
  447. psychopy/hardware/speaker.py +298 -36
  448. psychopy/hardware/triggerbox/__init__.py +1 -1
  449. psychopy/hardware/triggerbox/base.py +1 -1
  450. psychopy/hardware/triggerbox/parallel.py +1 -1
  451. psychopy/info.py +1 -1
  452. psychopy/iohub/devices/eyetracker/__init__.py +10 -18
  453. psychopy/iohub/devices/eyetracker/calibration/procedure.py +15 -33
  454. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +2 -2
  455. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +1 -0
  456. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +10 -0
  457. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +1 -0
  458. psychopy/iohub/devices/mouse/linux2.py +2 -1
  459. psychopy/layout.py +1 -1
  460. psychopy/liaison.py +91 -39
  461. psychopy/locale_setup.py +11 -1
  462. psychopy/localization/__init__.py +1 -1
  463. psychopy/localization/_localization.py +1 -1
  464. psychopy/localization/messages.pot +2 -2
  465. psychopy/logging.py +14 -12
  466. psychopy/microphone.py +4 -3
  467. psychopy/misc.py +1 -1
  468. psychopy/monitors/MonitorCenter.py +3 -3
  469. psychopy/monitors/__init__.py +1 -1
  470. psychopy/monitors/calibData.py +1 -1
  471. psychopy/monitors/calibTools.py +3 -2
  472. psychopy/platform_specific/__init__.py +1 -1
  473. psychopy/platform_specific/darwin.py +1 -1
  474. psychopy/platform_specific/linux.py +1 -1
  475. psychopy/platform_specific/win32.py +1 -1
  476. psychopy/plugins/__init__.py +26 -17
  477. psychopy/plugins/util.py +65 -0
  478. psychopy/preferences/Darwin.spec +11 -3
  479. psychopy/preferences/FreeBSD.spec +11 -3
  480. psychopy/preferences/Linux.spec +11 -3
  481. psychopy/preferences/Windows.spec +11 -3
  482. psychopy/preferences/__init__.py +1 -1
  483. psychopy/preferences/baseNoArch.spec +11 -3
  484. psychopy/preferences/hints.py +78 -61
  485. psychopy/preferences/preferences.py +82 -13
  486. psychopy/projects/__init__.py +1 -1
  487. psychopy/projects/pavlovia.py +29 -13
  488. psychopy/scripts/psyexpCompile.py +1 -1
  489. psychopy/session.py +81 -8
  490. psychopy/sound/__init__.py +25 -10
  491. psychopy/sound/_base.py +134 -34
  492. psychopy/sound/audioclip.py +38 -15
  493. psychopy/sound/audiodevice.py +1 -1
  494. psychopy/sound/backend_ptb.py +53 -321
  495. psychopy/sound/backend_pygame.py +7 -3
  496. psychopy/sound/backend_pyo.py +53 -22
  497. psychopy/sound/backend_pysound.py +10 -27
  498. psychopy/sound/backend_sounddevice.py +33 -21
  499. psychopy/sound/exceptions.py +1 -1
  500. psychopy/sound/microphone.py +83 -5
  501. psychopy/sound/transcribe.py +3 -3
  502. psychopy/tests/data/test_basic_run.py +1 -0
  503. psychopy/tests/data/test_sounds/default_16000.wav +0 -0
  504. psychopy/tests/data/test_sounds/default_192000.wav +0 -0
  505. psychopy/tests/data/test_sounds/default_22050.wav +0 -0
  506. psychopy/tests/data/test_sounds/default_32000.wav +0 -0
  507. psychopy/tests/data/test_sounds/default_44100.wav +0 -0
  508. psychopy/tests/data/test_sounds/default_48000.wav +0 -0
  509. psychopy/tests/data/test_sounds/default_8000.wav +0 -0
  510. psychopy/tests/data/test_sounds/default_96000.wav +0 -0
  511. psychopy/tests/test_alerts/test_alerttools.py +2 -1
  512. psychopy/tests/test_app/test_command_line.py +65 -0
  513. psychopy/tests/test_data/test_TrialHandler2.py +18 -7
  514. psychopy/tests/test_demos/test_builder_demos.py +1 -1
  515. psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +5238 -4188
  516. psychopy/tests/test_experiment/needs_wx/test_components.py +1 -1
  517. psychopy/tests/test_experiment/test_components/test_RoutineSettingsComponent.py +16 -0
  518. psychopy/tests/test_experiment/test_components/test_all_components.py +1 -1
  519. psychopy/tests/test_experiment/test_components/test_base_components.py +26 -17
  520. psychopy/tests/test_experiment/test_loops.py +12 -4
  521. psychopy/tests/test_experiment/test_params.py +17 -4
  522. psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +2 -2
  523. psychopy/tests/test_hardware/test_photodiode.py +66 -0
  524. psychopy/tests/test_liaison/test_Liaison.py +4 -4
  525. psychopy/tests/test_misc/test_clock.py +2 -0
  526. psychopy/tests/test_misc/test_color.py +2 -0
  527. psychopy/tests/test_misc/test_event.py +1 -0
  528. psychopy/tests/test_plugins/__init__.py +0 -0
  529. psychopy/tests/test_plugins/test_plugin_stubs.py +125 -0
  530. psychopy/tests/test_sound/test_sound.py +111 -40
  531. psychopy/tests/test_tools/test_colorspacetools.py +24 -23
  532. psychopy/tests/test_tools/test_mathtools.py +9 -9
  533. psychopy/tests/test_tools/test_viewtools.py +101 -0
  534. psychopy/tests/test_validators/__init__.py +0 -0
  535. psychopy/tests/test_validators/test_voicekeyValidator.py +95 -0
  536. psychopy/tests/test_visual/test_all_stimuli.py +2 -2
  537. psychopy/tests/test_visual/test_basevisual.py +37 -7
  538. psychopy/tests/test_visual/test_button.py +2 -2
  539. psychopy/tests/test_visual/test_circle.py +10 -2
  540. psychopy/tests/test_visual/test_dots.py +1 -1
  541. psychopy/tests/test_visual/test_form.py +13 -13
  542. psychopy/tests/test_visual/test_gamma.py +3 -3
  543. psychopy/tests/test_visual/test_image.py +2 -2
  544. psychopy/tests/test_visual/test_progress.py +2 -2
  545. psychopy/tests/test_visual/test_roi.py +8 -2
  546. psychopy/tests/test_visual/test_shape.py +2 -2
  547. psychopy/tests/test_visual/test_slider.py +2 -2
  548. psychopy/tests/test_visual/test_target.py +2 -2
  549. psychopy/tests/test_visual/test_textbox.py +6 -8
  550. psychopy/tests/test_visual/test_winScalePos.py +6 -5
  551. psychopy/tests/test_visual/test_window.py +17 -0
  552. psychopy/tests/utils.py +51 -1
  553. psychopy/tools/__init__.py +1 -1
  554. psychopy/tools/arraytools.py +2 -2
  555. psychopy/tools/attributetools.py +52 -1
  556. psychopy/tools/audiotools.py +4 -1
  557. psychopy/tools/colorspacetools.py +6 -4
  558. psychopy/tools/coordinatetools.py +1 -1
  559. psychopy/tools/fileerrortools.py +21 -9
  560. psychopy/tools/filetools.py +2 -2
  561. psychopy/tools/fontmanager.py +47 -28
  562. psychopy/tools/gltools.py +2967 -559
  563. psychopy/tools/imagetools.py +1 -1
  564. psychopy/tools/mathtools.py +997 -127
  565. psychopy/tools/monitorunittools.py +7 -1
  566. psychopy/tools/movietools.py +1 -2
  567. psychopy/tools/pkgtools.py +157 -127
  568. psychopy/tools/plottools.py +1 -1
  569. psychopy/tools/rifttools.py +1 -1
  570. psychopy/tools/stereotools.py +1 -1
  571. psychopy/tools/stimulustools.py +172 -2
  572. psychopy/tools/stringtools.py +22 -2
  573. psychopy/tools/systemtools.py +1 -1
  574. psychopy/tools/typetools.py +1 -1
  575. psychopy/tools/unittools.py +1 -1
  576. psychopy/tools/versionchooser.py +3 -1
  577. psychopy/tools/viewtools.py +54 -70
  578. psychopy/tools/wizard.py +2 -2
  579. psychopy/validation/__init__.py +6 -0
  580. psychopy/validation/audio.py +74 -0
  581. psychopy/validation/visual.py +115 -0
  582. psychopy/visual/__init__.py +1 -4
  583. psychopy/visual/aperture.py +9 -6
  584. psychopy/visual/backends/__init__.py +1 -1
  585. psychopy/visual/backends/_base.py +1 -1
  586. psychopy/visual/backends/gamma.py +1 -1
  587. psychopy/visual/backends/glfwbackend.py +8 -12
  588. psychopy/visual/backends/pygamebackend.py +1 -1
  589. psychopy/visual/backends/pygletbackend.py +32 -11
  590. psychopy/visual/basevisual.py +93 -13
  591. psychopy/visual/brush.py +1 -1
  592. psychopy/visual/bufferimage.py +90 -10
  593. psychopy/visual/button.py +1 -1
  594. psychopy/visual/circle.py +12 -20
  595. psychopy/visual/custommouse.py +1 -1
  596. psychopy/visual/dot.py +80 -14
  597. psychopy/visual/elementarray.py +84 -10
  598. psychopy/visual/filters.py +1 -1
  599. psychopy/visual/form.py +43 -28
  600. psychopy/visual/grating.py +105 -25
  601. psychopy/visual/helpers.py +4 -2
  602. psychopy/visual/image.py +98 -20
  603. psychopy/visual/line.py +15 -25
  604. psychopy/visual/movie.py +1 -1
  605. psychopy/visual/movie2.py +4 -3
  606. psychopy/visual/movie3.py +4 -3
  607. psychopy/visual/movies/__init__.py +1 -1
  608. psychopy/visual/movies/frame.py +1 -1
  609. psychopy/visual/movies/metadata.py +1 -1
  610. psychopy/visual/movies/players/__init__.py +1 -1
  611. psychopy/visual/movies/players/_base.py +1 -1
  612. psychopy/visual/movies/players/ffpyplayer_player.py +2 -3
  613. psychopy/visual/nnlvs.py +9 -6
  614. psychopy/visual/noise.py +4 -16
  615. psychopy/visual/patch.py +4 -3
  616. psychopy/visual/pie.py +3 -14
  617. psychopy/visual/polygon.py +21 -28
  618. psychopy/visual/radial.py +4 -18
  619. psychopy/visual/ratingscale.py +4 -3
  620. psychopy/visual/rect.py +16 -25
  621. psychopy/visual/rift.py +8 -2
  622. psychopy/visual/secondorder.py +4 -16
  623. psychopy/visual/shaders.py +620 -286
  624. psychopy/visual/shape.py +281 -90
  625. psychopy/visual/simpleimage.py +1 -1
  626. psychopy/visual/slider.py +78 -25
  627. psychopy/visual/stim3d.py +5 -608
  628. psychopy/visual/text.py +13 -3
  629. psychopy/visual/textbox2/textbox2.py +188 -56
  630. psychopy/visual/vlcmoviestim.py +1 -1
  631. psychopy/visual/window.py +374 -201
  632. psychopy/web.py +1 -1
  633. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/METADATA +13 -12
  634. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/RECORD +647 -624
  635. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/WHEEL +1 -1
  636. psychopy/.DS_Store +0 -0
  637. psychopy/__init__.py.orig +0 -65
  638. psychopy/app/.DS_Store +0 -0
  639. psychopy/app/Resources/.DS_Store +0 -0
  640. psychopy/app/Resources/classic/filecsv16.png +0 -0
  641. psychopy/app/Resources/classic/fileimage16.png +0 -0
  642. psychopy/app/Resources/classic/fileunknown16.png +0 -0
  643. psychopy/app/Resources/dark/filecsv16.png +0 -0
  644. psychopy/app/Resources/dark/filecsv16@2x.png +0 -0
  645. psychopy/app/Resources/dark/fileimage16.png +0 -0
  646. psychopy/app/Resources/dark/fileimage16@2x.png +0 -0
  647. psychopy/app/Resources/dark/fileunknown16.png +0 -0
  648. psychopy/app/Resources/dark/fileunknown16@2x.png +0 -0
  649. psychopy/app/Resources/fonts/OpenSans-Bold.ttf +0 -0
  650. psychopy/app/Resources/fonts/OpenSans-BoldItalic.ttf +0 -0
  651. psychopy/app/Resources/fonts/OpenSans-ExtraBold.ttf +0 -0
  652. psychopy/app/Resources/fonts/OpenSans-ExtraBoldItalic.ttf +0 -0
  653. psychopy/app/Resources/fonts/OpenSans-Italic.ttf +0 -0
  654. psychopy/app/Resources/fonts/OpenSans-Light.ttf +0 -0
  655. psychopy/app/Resources/fonts/OpenSans-LightItalic.ttf +0 -0
  656. psychopy/app/Resources/fonts/OpenSans-Regular.ttf +0 -0
  657. psychopy/app/Resources/fonts/OpenSans-SemiBold.ttf +0 -0
  658. psychopy/app/Resources/fonts/OpenSans-SemiBoldItalic.ttf +0 -0
  659. psychopy/app/Resources/light/filecsv16.png +0 -0
  660. psychopy/app/Resources/light/filecsv16@2x.png +0 -0
  661. psychopy/app/Resources/light/fileimage16.png +0 -0
  662. psychopy/app/Resources/light/fileimage16@2x.png +0 -0
  663. psychopy/app/Resources/light/fileunknown16.png +0 -0
  664. psychopy/app/Resources/light/fileunknown16@2x.png +0 -0
  665. psychopy/app/Resources/psychopySplash.png +0 -0
  666. psychopy/app/Resources/psychopySplash@2x.png +0 -0
  667. psychopy/app/builder/builder.py.orig +0 -3932
  668. psychopy/app/builder/dialogs/__init__.py.orig +0 -1679
  669. psychopy/app/builder/dialogs/paramCtrls.py.orig +0 -713
  670. psychopy/app/colorpicker/__init__.py.orig +0 -411
  671. psychopy/app/locale/ar_001/.DS_Store +0 -0
  672. psychopy/app/locale/ar_001/LC_MESSAGE/.DS_Store +0 -0
  673. psychopy/core.py.orig +0 -169
  674. psychopy/demos/builder/.DS_Store +0 -0
  675. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +0 -330
  676. psychopy/demos/builder/Experiments/.DS_Store +0 -0
  677. psychopy/demos/builder/Experiments/dragAndDrop/archived_conditions.xlsx +0 -0
  678. psychopy/demos/builder/Experiments/dragAndDrop/draw grid stim.py +0 -61
  679. psychopy/demos/builder/Experiments/dragAndDrop/shapeMaker.psyexp +0 -91
  680. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_1.png +0 -0
  681. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_10.png +0 -0
  682. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_2.png +0 -0
  683. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_3.png +0 -0
  684. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_4.png +0 -0
  685. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_5.png +0 -0
  686. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_6.png +0 -0
  687. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_7.png +0 -0
  688. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_8.png +0 -0
  689. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_9.png +0 -0
  690. psychopy/demos/builder/Experiments/dragAndDrop/updated_conditions.xlsx +0 -0
  691. psychopy/demos/builder/Tools/.DS_Store +0 -0
  692. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  693. psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
  694. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +0 -38
  695. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +0 -3418
  696. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  697. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +0 -2
  698. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +0 -15
  699. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  700. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +0 -562
  701. psychopy/demos/coder/.DS_Store +0 -0
  702. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  703. psychopy/demos/coder/iohub/.iohpid +0 -1
  704. psychopy/demos/coder/iohub/eyetracking/.iohpid +0 -1
  705. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  706. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  707. psychopy/experiment/_experiment.py.orig +0 -1032
  708. psychopy/experiment/components/.DS_Store +0 -0
  709. psychopy/experiment/components/_base.py.orig +0 -823
  710. psychopy/experiment/components/form/.DS_Store +0 -0
  711. psychopy/experiment/components/microphone/__init__.py.orig +0 -490
  712. psychopy/experiment/components/settings/__init__.py.orig +0 -1337
  713. psychopy/experiment/components/textbox/__init__.py.orig +0 -310
  714. psychopy/experiment/components/webcam/.DS_Store +0 -0
  715. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  716. psychopy/experiment/loops.py.orig +0 -829
  717. psychopy/experiment/params.py.orig +0 -408
  718. psychopy/experiment/routine.py.orig +0 -503
  719. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator.png +0 -0
  720. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator@2x.png +0 -0
  721. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator.png +0 -0
  722. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator@2x.png +0 -0
  723. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator.png +0 -0
  724. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator@2x.png +0 -0
  725. psychopy/hardware/.DS_Store +0 -0
  726. psychopy/hardware/brainproducts.py.orig +0 -680
  727. psychopy/hardware/iolab.py.orig +0 -238
  728. psychopy/iohub/datastore/__init__.py.orig +0 -443
  729. psychopy/iohub/datastore/util.py.orig +0 -692
  730. psychopy/iohub/devices/mouse/darwin.py.orig +0 -427
  731. psychopy/iohub/devices/mouse/linux2.py.orig +0 -198
  732. psychopy/preferences/.DS_Store +0 -0
  733. psychopy/projects/pavlovia.py.orig +0 -1295
  734. psychopy/tests/.DS_Store +0 -0
  735. psychopy/tests/data/.DS_Store +0 -0
  736. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  737. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  738. psychopy/tests/data/aperture1_norm_local.png +0 -0
  739. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  740. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  741. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  742. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  743. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  744. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  745. psychopy/tests/data/blend_add_height_local.png +0 -0
  746. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  747. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  748. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  749. psychopy/tests/data/blend_add_norm_local.png +0 -0
  750. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  751. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  752. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  753. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  754. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  755. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  756. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  757. psychopy/tests/data/circleHex_height_local.png +0 -0
  758. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  759. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  760. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  761. psychopy/tests/data/circleHex_norm_local.png +0 -0
  762. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  763. psychopy/tests/data/color_comparison_local.png +0 -0
  764. psychopy/tests/data/correctScript/.DS_Store +0 -0
  765. psychopy/tests/data/dots_height_local.png +0 -0
  766. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  767. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  768. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  769. psychopy/tests/data/dots_norm_local.png +0 -0
  770. psychopy/tests/data/dots_stencil_local.png +0 -0
  771. psychopy/tests/data/elarray1_height_local.png +0 -0
  772. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  773. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  774. psychopy/tests/data/elarray1_norm_local.png +0 -0
  775. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  776. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  777. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  778. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  779. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  780. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  781. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  782. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  783. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  784. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  785. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  786. psychopy/tests/data/gabor1_height_local.png +0 -0
  787. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  788. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  789. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  790. psychopy/tests/data/gabor1_norm_local.png +0 -0
  791. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  792. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  793. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  794. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  795. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  796. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  797. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  798. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  799. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  800. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  801. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  802. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  803. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  804. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  805. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  806. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  807. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  808. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  809. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  810. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  811. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  812. psychopy/tests/data/numpyImage_height_local.png +0 -0
  813. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  814. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  815. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  816. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  817. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  818. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  819. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  820. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  821. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  822. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  823. psychopy/tests/data/text1_height_local.png +0 -0
  824. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  825. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  826. psychopy/tests/data/text1_norm_local.png +0 -0
  827. psychopy/tests/data/text1_stencil_local.png +0 -0
  828. psychopy/tests/data/wedge1_height_local.png +0 -0
  829. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  830. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  831. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  832. psychopy/tests/data/wedge1_norm_local.png +0 -0
  833. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  834. psychopy/tests/test_app/.DS_Store +0 -0
  835. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  836. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +0 -177
  837. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  838. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  839. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +0 -168
  840. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  841. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  842. psychopy/tests/test_data/.DS_Store +0 -0
  843. psychopy/tests/test_hardware/test_CRS_BitsSharp.py +0 -67
  844. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +0 -68
  845. psychopy/tests/test_hardware/test_CRS_bitsShaders.py +0 -110
  846. psychopy/tests/test_visual/test_image.py.orig +0 -219
  847. psychopy/visual/basevisual.py.orig +0 -1723
  848. psychopy/visual/form.py.orig +0 -1181
  849. psychopy/visual/text.py.orig +0 -752
  850. psychopy/visual/textbox2/textbox2.py.orig +0 -1315
  851. psychopy/visual/windowwarp.py.orig +0 -463
  852. /psychopy/{app/cortex.log → alerts/alertsCatalogue/3600.yaml} +0 -0
  853. /psychopy/{app/Resources → assets}/fonts/Arvo-Bold.ttf +0 -0
  854. /psychopy/{app/Resources → assets}/fonts/Arvo-BoldItalic.ttf +0 -0
  855. /psychopy/{app/Resources → assets}/fonts/Arvo-Italic.ttf +0 -0
  856. /psychopy/{app/Resources → assets}/fonts/Arvo-Regular.ttf +0 -0
  857. /psychopy/{app/Resources → assets}/fonts/DejaVuSerif.ttf +0 -0
  858. /psychopy/{app/Resources → assets}/fonts/IndieFlower-Regular.ttf +0 -0
  859. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  860. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
  861. /psychopy/demos/builder/Experiments/{GoNoGo → goNoGo}/readme.md +0 -0
  862. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/entry_points.txt +0 -0
  863. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,324 +1,802 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
+ # Part of the PsychoPy library
5
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
6
+ # Distributed under the terms of the GNU General Public License (GPL).
7
+
4
8
  """Control joysticks and gamepads from within PsychoPy.
5
9
 
6
- You do need a window (and you need to be flipping it) for the joystick to be
7
- updated.
10
+ For most backends, you do need a window using the same backend (and you need to
11
+ be flipping it) for the joystick to be updated.
12
+
13
+ """
8
14
 
9
- Known issues:
10
- - currently under pyglet the joystick axes initialise to a value of zero
11
- and stay like this until the first time that axis moves
15
+ __all__ = [
16
+ 'Joystick',
17
+ 'JoystickError',
18
+ 'JoystickAxisNotAvailableError',
19
+ 'JoysticButtonNotAvailableError',
20
+ 'getJoystickInterfaces']
12
21
 
13
- - currently pygame (1.9.1) spits out lots of debug messages about the
14
- joystick and these can't be turned off :-/
22
+ from psychopy import logging, visual
23
+ from psychopy.hardware.joystick._base import BaseJoystickInterface
24
+ from psychopy.hardware.joystick.backend_pyglet import JoystickInterfacePyglet
25
+ from psychopy.hardware.joystick.backend_glfw import JoystickInterfaceGLFW
26
+ import psychopy.hardware.joystick.mappings as mappings
27
+ import psychopy.core as core
15
28
 
16
- Typical usage::
29
+ import math
30
+ import numpy as np
17
31
 
18
- from psychopy.hardware import joystick
19
- from psychopy import visual
32
+ # backend to use when creating joystick objects
33
+ backend = 'pyglet' # 'pyglet' or 'pygame'
20
34
 
21
- joystick.backend='pyglet' # must match the Window
22
- win = visual.Window([400,400], winType='pyglet')
35
+ # constants
36
+ JOYSTICK_AXIS_X = JOYSTICK_BUTTON_A = 0
37
+ JOYSTICK_AXIS_Y = JOYSTICK_BUTTON_B = 1
38
+ JOYSTICK_AXIS_Z = JOYSTICK_BUTTON_X = 2
39
+ JOYSTICK_AXIS_RX = JOYSTICK_BUTTON_Y = 3
40
+ JOYSTICK_AXIS_RY = 4
41
+ JOYSTICK_AXIS_RZ = 5
23
42
 
24
- nJoys = joystick.getNumJoysticks() # to check if we have any
25
- id = 0
26
- joy = joystick.Joystick(id) # id must be <= nJoys - 1
27
43
 
28
- nAxes = joy.getNumAxes() # for interest
29
- while True: # while presenting stimuli
30
- joy.getX()
31
- # ...
32
- win.flip() # flipping implicitly updates the joystick info
33
- """
44
+ class JoystickError(Exception):
45
+ """Exception raised for errors in the joystick module.
46
+ """
47
+ pass
34
48
 
35
- try:
36
- import pygame.joystick
37
- havePygame = True
38
- except Exception:
39
- havePygame = False
40
-
41
- try:
42
- from pyglet import input as pyglet_input # pyglet 1.2+
43
- from pyglet import app as pyglet_app
44
- havePyglet = True
45
- except Exception:
46
- havePyglet = False
47
-
48
- try:
49
- import glfw
50
- haveGLFW = True
51
- except ImportError:
52
- print("failed to import GLFW.")
53
- haveGLFW = False
54
49
 
55
- from psychopy import logging, visual
56
- backend = 'pyglet' # 'pyglet' or 'pygame'
50
+ class JoystickBackendNotAvailableError(JoystickError):
51
+ """Exception raised when the backend is not available.
52
+ """
53
+ pass
57
54
 
58
55
 
59
- def getNumJoysticks():
60
- """Return a count of the number of joysticks available."""
61
- if backend == 'pyglet':
62
- return len(pyglet_input.get_joysticks())
63
- elif backend == 'glfw':
64
- n_joys = 0
65
- for joy in range(glfw.JOYSTICK_1, glfw.JOYSTICK_LAST):
66
- if glfw.joystick_present(joy):
67
- n_joys += 1
56
+ class JoystickAxisNotAvailableError(JoystickError):
57
+ """Exception raised when an axis is not available on the joystick.
58
+ """
59
+ pass
68
60
 
69
- return n_joys
70
- else:
71
- pygame.joystick.init()
72
- return pygame.joystick.get_count()
73
61
 
62
+ class InvalidInputNameError(JoystickError):
63
+ """Exception raised when an input name is not valid.
64
+ """
65
+ pass
74
66
 
75
- if havePyglet:
76
- class PygletDispatcher:
77
- def dispatch_events(self):
78
- pyglet_app.platform_event_loop.step(timeout=0.001)
79
67
 
80
- pyglet_dispatcher = PygletDispatcher()
68
+ class JoysticButtonNotAvailableError(JoystickError):
69
+ """Exception raised when a button is not available on the joystick.
70
+ """
71
+ pass
81
72
 
82
73
 
83
74
  class Joystick:
75
+ """Class for interfacing with a multi-axis joystick or gamepad.
76
+
77
+ Upon creating a `Joystick` object, the joystick device is opened and the
78
+ states of the device's axes and buttons can be read.
79
+
80
+ Values for the axes are returned as floating point numbers, typically
81
+ between -1.0 and +1.0 unless scaling is applied. The values for the buttons
82
+ are returned as booleans, where True indicates the button is pressed down
83
+ at the time the device was last polled.
84
+
85
+ Scaling factors can be set for each axis to adjust the range of the axis
86
+ values. The scaling factor is a floating point value that is multiplied by
87
+ the axis value. If the scaling factor is negative, the axis value is
88
+ inverted. Deadzones can also be applied for each axis to prevent small
89
+ fluctuations in the joystick's resting position from being interpreted as
90
+ valid input. The deadzone is a floating point value between 0.0 and 1.0. If
91
+ the absolute value of the axis value is less than the deadzone, the axis
92
+ value is set to zero.
93
+
94
+ Device inputs can be named to provide a more human-readable interface. The
95
+ names can be set for axes, buttons, and hats where they can be used to get
96
+ the input values instead of using the integer indices. Furthermore,
97
+ like inputs can be grouped together under a single name. For example, both
98
+ X and Y of a thumbstick can be grouped together under the name 'thumbstick'.
99
+ When getting the value of the thumbstick, a tuple of the X and Y values is
100
+ returned instead of having to get each axis individually.
101
+
102
+ Parameters
103
+ ----------
104
+ device : int or str
105
+ The index or name of the joystick to control.
106
+
107
+ Examples
108
+ --------
109
+ Typical usage::
110
+
111
+ from psychopy.hardware import joystick
112
+ from psychopy import visual
113
+
114
+ joystick.backend='pyglet' # must match the Window
115
+ win = visual.Window([400,400], winType='pyglet')
116
+
117
+ nJoys = joystick.getNumJoysticks() # to check if we have any
118
+ id = 0
119
+ joy = joystick.Joystick(id) # id must be <= nJoys - 1
120
+
121
+ nAxes = joy.getNumAxes() # for interest
122
+ while True: # while presenting stimuli
123
+ joyX = joy.getX()
124
+ # ...
125
+ win.flip() # flipping implicitly updates the joystick info
126
+
127
+ Set the deadzone for axis 0 to 0.1::
128
+
129
+ joy.setAxisDeadzone(0, 0.1)
130
+
131
+ Set the scaling factor for 1 axis to 2.0::
132
+
133
+ joy.setAxisScale(1, 2.0)
134
+
135
+ Setting the names of the inputs can be useful for debugging and for
136
+ providing a more human-readable interface::
137
+
138
+ joy.setInputName('axis', 0, 'x')
139
+ joy.setInputName('axis', 1, 'y')
140
+
141
+ You can get the imput value by name by passing it to the get method for the
142
+ input type::
143
+
144
+ joy.getAxis('axis', 'x') # instead of joy.getAxis(0)
84
145
 
85
- def __init__(self, id):
86
- """An object to control a multi-axis joystick or gamepad.
87
-
88
- .. note:
89
-
90
- You do need to be flipping frames (or dispatching events manually)
91
- in order for the values of the joystick to be updated.
92
-
93
- :Known issues:
94
-
95
- Currently under pyglet backends the axis values initialise to zero
96
- rather than reading the current true value. This gets fixed on the
97
- first change to each axis.
98
- """
99
- self.id = id
100
- if backend == 'pyglet':
101
- joys = pyglet_input.get_joysticks()
102
- if id >= len(joys):
103
- logging.error("You don't have that many joysticks attached "
104
- "(remember that the first joystick has id=0 "
105
- "etc...)")
106
- else:
107
- self._device = joys[id]
108
- try:
109
- self._device.open()
110
- except pyglet_input.DeviceOpenException as e:
111
- pass
112
- self.name = self._device.device.name
113
- if len(visual.openWindows) == 0:
114
- logging.error(
115
- "You need to open a window before creating your joystick")
116
- else:
117
- for win in visual.openWindows:
118
- win()._eventDispatchers.append(pyglet_dispatcher)
119
- elif backend == 'glfw':
120
- # We can create a joystick anytime after glfwInit() is called, but
121
- # there should be a window open first.
122
- # Joystick events are processed when flipping the associated window.
123
- if not glfw.init():
124
- logging.error("GLFW could not be initialized. Exiting.")
125
-
126
- # get all available joysticks, GLFW supports up to 16.
127
- joys = []
128
- for joy in range(glfw.JOYSTICK_1, glfw.JOYSTICK_LAST):
129
- if glfw.joystick_present(joy):
130
- joys.append(joy)
131
-
132
- # error checks
133
- if not joys: # if the list is empty, no joysticks were found
134
- error_msg = ("No joysticks were found by the GLFW runtime. "
135
- "Check connections and try again.")
136
- logging.error(error_msg)
137
- raise RuntimeError(error_msg)
138
- elif id not in joys:
139
- error_msg = ("You don't have that many joysticks attached "
140
- "(remember that the first joystick has id=0 "
141
- "etc...)")
142
- logging.error(error_msg)
143
- raise RuntimeError(error_msg)
144
-
145
- self._device = id # just need the ID for GLFW
146
- self.name = glfw.get_joystick_name(self._device).decode("utf-8")
147
-
148
- if len(visual.openWindows) == 0:
149
- logging.error(
150
- "You need to open a window before creating your joystick")
151
- else:
152
- for win in visual.openWindows:
153
- # sending the raw ID to the window.
154
- win()._eventDispatchers.append(self._device)
146
+ Automatically set the input names to the default Xbox controller mapping
147
+ scheme::
155
148
 
149
+ joy.setInputScheme('xbox')
150
+ # ...
151
+ xVal, yVal = joy.getAxis('left_thumbstick')
152
+ leftTrigger, rightTrigger = joy.getAxis('triggers')
153
+
154
+ Notes
155
+ -----
156
+ * You do need to be flipping frames (or dispatching events manually) in
157
+ order for the values of the joystick to be updated.
158
+ * Currently under pyglet backends the axis values initialise to zero
159
+ rather than reading the current true value. This gets fixed on the first
160
+ change to each axis.
161
+ * Currently pygame (1.9.1) spits out lots of debug messages about the
162
+ joystick and these can't be turned off :-/
163
+ * The GLFW backend can be used without first opening a window and can be
164
+ used with other window backends.
165
+
166
+ """
167
+ def __init__(self, device=0, **kwargs):
168
+ # get the joystick device interface
169
+ try:
170
+ joyInterface = getJoystickInterfaces()[backend]
171
+ logging.info(
172
+ "Using joystick interface '{}' for backend '{}'".format(
173
+ joyInterface.__name__, backend))
174
+ except KeyError:
175
+ logging.error(
176
+ "No joystick interface found for backend '{}'".format(
177
+ backend))
178
+
179
+ # create a device interface
180
+ self._joy = joyInterface(device, **kwargs)
181
+
182
+ # input counts for the device, these don't chnage after opening
183
+ self._numAxes = self._joy.getNumAxes()
184
+ self._numButtons = self._joy.getNumButtons()
185
+ self._numHats = self._joy.getNumHats()
186
+
187
+ # axis value modifiers
188
+ self._axisScale = [1.0] * self._numAxes
189
+ self._axisDeadzone = [0.0] * self._numAxes
190
+
191
+ # device states
192
+ self._lastUpdateTime = 0.0 # in experiment time
193
+ self._axisVals = np.zeros(self._numAxes, dtype=np.float32)
194
+ self._btnStates = np.zeros(self._numButtons, dtype=bool)
195
+ self._hatStates = np.zeros((self._numHats, 2), dtype=np.int8)
196
+
197
+ # VR and motion tracking properties
198
+ self._pos = np.zeros(3, dtype=np.float32)
199
+ self._ori = np.array([0., 0., 0., 1.], dtype=np.float32)
200
+ self._angularVel = np.zeros(3, dtype=np.float32)
201
+ self._linearVel = np.zeros(3, dtype=np.float32)
202
+
203
+ # axis name mapping, some defaults are provided for common axes
204
+ self._inputNames = {}
205
+ self.setInputScheme('default') # use default mapping scheme
206
+
207
+ def __del__(self):
208
+ """Close the joystick device when the object is deleted.
209
+ """
210
+ if hasattr(self, '_joy'):
211
+ self.close()
212
+
213
+ def lastUpdateTime(self):
214
+ """Return the time of the last update to the joystick state.
215
+
216
+ Returns
217
+ -------
218
+ float
219
+ The time of the last update to the joystick state.
220
+
221
+ """
222
+ return self._lastUpdateTime
223
+
224
+ def poll(self):
225
+ """Poll the joystick device for the current state.
226
+
227
+ This method should be called at the beginning of each frame to update
228
+ the state of the joystick device. The time of the last update is stored
229
+ and can be accessed using the `lastUpdateTime` property.
230
+
231
+ """
232
+ self._joy.update()
233
+
234
+ # update the internal state of the joystick
235
+ self._axisVals[:] = self.getAllAxes()
236
+ self._btnStates[:] = self.getAllButtons()
237
+
238
+ if backend != 'glfw': # cannot use hats with GLFW
239
+ self._hatStates[:] = self.getAllHats()
240
+
241
+ # update the VR properties
242
+ if self.hasTracking:
243
+ self._pos = self.getPos()
244
+ self._ori = self.getOri()
245
+ self._angularVel = self.getAngularVelocity()
246
+ self._linearVel = self.getLinearVelocity()
247
+
248
+ if self._joy.trackerData is None:
249
+ self._lastUpdateTime = core.getTime()
156
250
  else:
157
- pygame.joystick.init()
158
- self._device = pygame.joystick.Joystick(id)
159
- self._device.init()
160
- self.name = self._device.get_name()
251
+ self._lastUpdateTime = self._joy.trackerData._absSampleTime
252
+
253
+ return self._lastUpdateTime
254
+
255
+ @staticmethod
256
+ def getAvailableDevices():
257
+ """Return a list of available joystick devices.
258
+
259
+ This method is used by `DeviceManager` to get a list of available
260
+ devices.
261
+
262
+ Returns
263
+ -------
264
+ list
265
+ A list of available joystick devices.
266
+
267
+ """
268
+ # use the selected backend class to get the available devices
269
+ return getJoystickInterfaces()[backend].getAvailableDevices()
270
+
271
+ @property
272
+ def inputLib(self):
273
+ """Input interface library used (`str`).
274
+ """
275
+ if not hasattr(self, '_joy'):
276
+ return None
277
+
278
+ return self._joy.inputLib
279
+
280
+ @property
281
+ def hasTracking(self):
282
+ """Check if the joystick has tracking capabilities.
283
+
284
+ Returns
285
+ -------
286
+ bool
287
+ True if the joystick has tracking capabilities, False otherwise.
288
+
289
+ """
290
+ return self._joy.hasTracking
291
+
292
+ def isSameDevice(self, otherDevice):
293
+ """Check if the device is the same as another device.
294
+
295
+ Parameters
296
+ ----------
297
+ otherDevice : Joystick
298
+ The other device to compare against.
299
+
300
+ Returns
301
+ -------
302
+ bool
303
+ True if the devices are the same, False otherwise.
304
+
305
+ """
306
+ # only need to check the index since the device ID is unique
307
+ return self._joy.isSameDevice(otherDevice._device)
308
+
309
+ def open(self):
310
+ """Open the joystick device.
311
+ """
312
+ if self.isOpen:
313
+ return
314
+
315
+ self._joy.open()
316
+
317
+ @property
318
+ def isOpen(self):
319
+ """Check if the joystick device is open.
320
+
321
+ Returns
322
+ -------
323
+ bool
324
+ True if the joystick device is open, False otherwise.
325
+
326
+ """
327
+ return self._joy.isOpen
328
+
329
+ def close(self):
330
+ """Close the joystick device.
331
+ """
332
+ if not self.isOpen:
333
+ return
334
+
335
+ self._joy.close()
336
+
337
+ @property
338
+ def name(self):
339
+ """Name of the joystick reported by the system (`str`).
340
+ """
341
+ return self.getName()
342
+
343
+ @property
344
+ def deviceIndex(self):
345
+ """The index of the joystick (`int`).
346
+ """
347
+ return self._deviceIndex
348
+
349
+ @property
350
+ def x(self):
351
+ """The X axis value (`float`).
352
+ """
353
+ return self.getX()
354
+
355
+ @property
356
+ def y(self):
357
+ """The Y axis value (`float`).
358
+ """
359
+ return self.getY()
360
+
361
+ @property
362
+ def z(self):
363
+ """The Z axis value (`float`).
364
+ """
365
+ return self.getZ()
366
+
367
+ @property
368
+ def rx(self):
369
+ """The RX axis value (`float`).
370
+ """
371
+ return self.getRX()
372
+
373
+ @property
374
+ def ry(self):
375
+ """The RY axis value (`float`).
376
+ """
377
+ return self.getRY()
378
+
379
+ @property
380
+ def rz(self):
381
+ """The RZ axis value (`float`).
382
+ """
383
+ return self.getRZ()
384
+
385
+ @property
386
+ def trackerData(self):
387
+ """Tracker data for the controller.
388
+
389
+ Returns
390
+ -------
391
+ `TrackerData` or `None`
392
+ The tracker data.
393
+
394
+ """
395
+ return self._joy.trackerData
161
396
 
162
397
  def getName(self):
163
- """Return the manufacturer-defined name describing the device."""
164
- return self.name
398
+ """Return the manufacturer-defined name describing the device (`str`).
399
+ """
400
+ return self._joy.getName()
165
401
 
166
- def getNumButtons(self):
167
- """Return the number of digital buttons on the device."""
168
- if backend == 'pyglet':
169
- return len(self._device.buttons)
170
- elif backend == 'glfw':
171
- _, count = glfw.get_joystick_buttons(self._device)
172
- return count
173
- else:
174
- return self._device.get_numbuttons()
402
+ def setInputScheme(self, mapping):
403
+ """Set the input mapping scheme for the joystick.
175
404
 
176
- def getButton(self, buttonId):
177
- """Get the state of a given button.
405
+ The input mapping scheme determines the names of the inputs for the
406
+ joystick. The mapping scheme can be set to 'default', 'xbox', or
407
+ 'custom'. The default mapping scheme provides names for the axes and
408
+ buttons that are common to most joysticks.
409
+
410
+ Note that setting the mapping scheme will overwrite any custom input
411
+ names that have been set prior to calling this method.
412
+
413
+ Parameters
414
+ ----------
415
+ mapping : str
416
+ The mapping scheme to set. Must be one of 'default', 'xbox', or
417
+ 'custom'.
178
418
 
179
- buttonId should be a value from 0 to the number of buttons-1
180
419
  """
181
- if backend == 'pyglet':
182
- return self._device.buttons[buttonId]
183
- elif backend == 'glfw':
184
- bs, _ = glfw.get_joystick_buttons(self._device)
185
- return bs[buttonId]
186
- else:
187
- return self._device.get_button(buttonId)
420
+ # get the mapping scheme
421
+ inputMap = mappings.getInputScheme(mapping, self.inputLib)
422
+ if inputMap is None:
423
+ raise ValueError("Invalid mapping scheme '{}'.".format(mapping))
424
+
425
+ logging.info(
426
+ "Setting input scheme for joystick to '{}'.".format(mapping))
427
+
428
+ # set the input names
429
+ self._inputNames = inputMap
430
+
431
+ def setInputName(self, inputType, inputIndex, name):
432
+ """Set the name of an input.
433
+
434
+ Parameters
435
+ ----------
436
+ inputType : str
437
+ The type of input to set the name for. Must be one of 'axis',
438
+ 'button', or 'hat'.
439
+ inputIndex : int or list of int
440
+ The index of the input to set the name for. If a list of indices is
441
+ supplied, multiple axes will be grouped together.
442
+ name : str or None
443
+ The name to set for the axis. If None, the name for the axis is
444
+ removed.
445
+
446
+ Raises
447
+ ------
448
+ ValueError
449
+ If the inputType is not 'axis', 'button', or 'hat'.
450
+
451
+ Examples
452
+ --------
453
+ Set the name of axis `0` to 'x' and get its value by name::
454
+
455
+ joy.setInputName('axis', 0, 'x')
456
+ xVal = joy.getAxis('x') # instead of joy.getAxis(0)
457
+
458
+ Joystick inputs often have multiple axes ganged together on a single
459
+ control, such as a thumbstick. You can group axes together by passing a
460
+ list of indices::
461
+
462
+ joy.setInputName('axis', [0, 1], 'left_thumbstick')
463
+ xVal, yVal = joy.getAxis('left_thumbstick') # returns 2 values
188
464
 
189
- def getAllButtons(self):
190
- """Get the state of all buttons as a list."""
191
- if backend == 'pyglet':
192
- return self._device.buttons
193
- elif backend == 'glfw':
194
- bs, count = glfw.get_joystick_buttons(self._device)
195
- return [bs[i] for i in range(count)]
196
- else:
197
- bs = []
198
- for id in range(self._device.get_numbuttons()):
199
- bs.append(self._device.get_button(id))
200
- return bs
465
+ """
466
+ if inputType not in ('axes', 'buttons', 'hats'):
467
+ raise ValueError("Input type must be 'axes', 'buttons', or 'hats'.")
468
+
469
+ if name is None:
470
+ if inputIndex in self._inputNames[inputType]:
471
+ del self._inputNames[inputType][inputIndex]
472
+ return
473
+
474
+ self._inputNames[inputType][inputIndex] = name
475
+
476
+ def _getIndexFromName(self, inputType, name):
477
+ """Get the index of an input from its name.
478
+
479
+ Parameters
480
+ ----------
481
+ inputType : str
482
+ The type of input to get the index for. Must be one of 'axis',
483
+ 'button', or 'hat'.
484
+ name : str
485
+ The name of the input to get the index for.
486
+
487
+ Returns
488
+ -------
489
+ int or None
490
+ The index of the input. If the input name is not found, `None` is
491
+ returned.
492
+
493
+ Raises
494
+ ------
495
+ InvalidInputNameError
496
+ If the input name is not valid or has not been set.
497
+
498
+ """
499
+ inputIndex = self._inputNames[inputType].get(name, None)
500
+ if inputIndex is not None:
501
+ return inputIndex
201
502
 
202
- def getAllHats(self):
203
- """Get the current values of all available hats as a list of tuples.
204
-
205
- Each value is a tuple (x, y) where x and y can be -1, 0, +1
206
- """
207
- hats = []
208
- if backend == 'pyglet':
209
- for ctrl in self._device.device.get_controls():
210
- if ctrl.name != None and 'hat' in ctrl.name:
211
- hats.append((self._device.hat_x, self._device.hat_y))
212
- elif backend == 'glfw':
213
- # GLFW treats hats as buttons
214
- pass
215
- else:
216
- for n in range(self._device.get_numhats()):
217
- hats.append(self._device.get_hat(n))
218
- return hats
503
+ raise InvalidInputNameError("Input name '{}' is not valid.".format(name))
219
504
 
220
- def getNumHats(self):
221
- """Get the number of hats on this joystick.
505
+ # --------------------------------------------------------------------------
506
+ # Axis filtering methods
507
+ #
222
508
 
223
- The GLFW backend makes no distinction between hats and buttons. Calling
224
- 'getNumHats()' will return 0.
509
+ def getAxisScale(self, axisId):
510
+ """Get the scale factor for a given axis.
511
+
512
+ Parameters
513
+ ----------
514
+ axisId : int
515
+ The axis ID to get the scale factor for.
516
+
517
+ Returns
518
+ -------
519
+ float
520
+ The scale factor for the given axis.
225
521
 
226
522
  """
227
- if backend == 'pyglet':
228
- return len(self.getAllHats())
229
- elif backend == 'glfw':
230
- return 0
231
- else:
232
- return self._device.get_numhats()
523
+ return self._axisScale[axisId]
233
524
 
234
- def getHat(self, hatId=0):
235
- """Get the position of a particular hat.
525
+ def setAxisScale(self, axisId, scale):
526
+ """Set the scale factor for a given axis.
527
+
528
+ Parameters
529
+ ----------
530
+ axisId : int or None
531
+ The axis ID to set the scale factor for. If None, set the scale
532
+ factor for all axes to the given value.
533
+ scale : float
534
+ The scale factor to set. This factor will be multiplied by the
535
+ axis value. If negative, the axis value will be inverted.
536
+
537
+ """
538
+ if not isinstance(scale, (int, float)):
539
+ raise TypeError("Scaling factor must be a numeric type.")
540
+
541
+ if isinstance(axisId, str):
542
+ axisId = self._getIndexFromName('axes', axisId)
236
543
 
237
- The position returned is an (x, y) tuple where x and y
238
- can be -1, 0 or +1
239
- """
240
- if backend == 'pyglet':
241
- if hatId == 0:
242
- return self._device.hat
243
- else:
244
- return self.getAllHats()[hatId]
245
- elif backend == 'glfw':
246
- # does nothing, hats are buttons in GLFW
247
- pass
544
+ if axisId is None:
545
+ self._axisScale = [scale] * len(self._axisScale)
248
546
  else:
249
- return self._device.get_hat(hatId)
547
+ self._axisScale[axisId] = scale
548
+
549
+ def getAxisDeadzone(self, axisId):
550
+ """Get the deadzone for a given axis.
551
+
552
+ Parameters
553
+ ----------
554
+ axisId : int
555
+ The axis ID to get the deadzone for.
556
+
557
+ Returns
558
+ -------
559
+ float
560
+ The deadzone for the given axis.
561
+
562
+ """
563
+ if axisId is None:
564
+ return self._axisDeadzone
565
+
566
+ if isinstance(axisId, str):
567
+ axisId = self._getIndexFromName('axes', axisId)
568
+
569
+ if isinstance(axisId, (list, tuple)):
570
+ return [self.getAxisDeadzone(ax) for ax in axisId]
571
+
572
+ return self._axisDeadzone[axisId]
573
+
574
+ def setAxisDeadzone(self, axisId=None, deadzone=0.1):
575
+ """Set the deadzone for a given axis.
576
+
577
+ Parameters
578
+ ----------
579
+ axisId : int, str, list or None
580
+ The axis ID to set the deadzone for. If None, set the deadzone for
581
+ all axes to the given value. A string can be supplied to set the
582
+ deadzone for an axis by name. A list of axes can also be supplied to
583
+ set the deadzone for multiple axes at once.
584
+ deadzone : float
585
+ The deadzone to set, must be between 0.0 and 1.0.
586
+
587
+ """
588
+ if not isinstance(deadzone, (int, float)):
589
+ raise TypeError("Deadzone must be a numeric type.")
590
+
591
+ deadzone = min(1.0, max(0.0, deadzone))
592
+ if axisId is None:
593
+ self._axisDeadzone = [deadzone] * len(self._axisDeadzone)
594
+ return
595
+
596
+ if isinstance(axisId, str): # name supplied
597
+ axisId = self._getIndexFromName('axes', axisId)
598
+
599
+ if isinstance(axisId, (list, tuple)):
600
+ for ax in axisId:
601
+ self.setAxisDeadzone(ax, deadzone)
602
+ return
603
+
604
+ self._axisDeadzone[axisId] = deadzone
605
+
606
+ # --------------------------------------------------------------------------
607
+ # Axis methods
608
+ #
609
+
610
+ def getAllAxes(self):
611
+ """Get a list of all current axis values (`int`).
612
+ """
613
+ allAxes = self._joy.getAllAxes()
614
+
615
+ # apply scaling and deadzone to axes
616
+ for i, axisVal in enumerate(allAxes):
617
+ allAxes[i] = axisVal * self._axisScale[i] \
618
+ if abs(axisVal) >= self._axisDeadzone[i] else 0.0
619
+
620
+ return allAxes
621
+
622
+ def getNumAxes(self):
623
+ """Get the number of available joystick axes.
624
+
625
+ The first axis usually corresponds to the X axis, the second to the Y
626
+ axis for most joysticks. Additional axes may be present for other
627
+ controls such as addtional thumbsticks or throttle lever.
628
+
629
+ Returns
630
+ -------
631
+ int
632
+ The number of axes found on the joystick.
633
+
634
+ """
635
+ return self._numAxes
636
+
637
+ def getAxis(self, axisId):
638
+ """Get the value of an axis by an integer id.
639
+
640
+ Parameters
641
+ ----------
642
+ axisId : int, str or list
643
+ The axis ID to get the value for. If a string is supplied, the name
644
+ of the axis is used to get the value. If a list of axes indices or
645
+ names is supplied, a list of values is returned.
646
+
647
+ Returns
648
+ -------
649
+ float or list
650
+ The value of the axis. If a list of axes is supplied, a list of
651
+ values is returned.
652
+
653
+ """
654
+ if isinstance(axisId, str): # name supplied
655
+ axisId = self._getIndexFromName('axes', axisId)
656
+
657
+ # is axisId a sequence?
658
+ if isinstance(axisId, (list, tuple)):
659
+ return [self.getAxis(ax) for ax in axisId] # recusively called
660
+
661
+ # get the axis value from `int` axisId
662
+ axisVal = self._joy.getAxis(axisId)
663
+ return axisVal * self._axisScale[axisId] \
664
+ if abs(axisVal) >= self._axisDeadzone[axisId] else 0.0
250
665
 
251
666
  def getX(self):
252
667
  """Return the X axis value (equivalent to joystick.getAxis(0))."""
253
- if backend == 'pyglet':
254
- return self._device.x
255
- elif backend == 'glfw':
256
- return self.getAxis(0)
257
- else:
258
- return self._device.get_axis(0)
668
+ return self.getAxis(JOYSTICK_AXIS_X)
259
669
 
260
670
  def getY(self):
261
671
  """Return the Y axis value (equivalent to joystick.getAxis(1))."""
262
- if backend == 'pyglet':
263
- return self._device.y
264
- elif backend == 'glfw':
265
- return self.getAxis(1)
266
- else:
267
- return self._device.get_axis(1)
672
+ return self.getAxis(JOYSTICK_AXIS_Y)
673
+
674
+ def getXY(self):
675
+ """Return the X and Y axis values as a tuple.
676
+
677
+ Returns
678
+ -------
679
+ tuple
680
+ The X and Y axis values as a tuple.
681
+
682
+ """
683
+ return self.getAxis([JOYSTICK_AXIS_X, JOYSTICK_AXIS_Y])
268
684
 
269
685
  def getZ(self):
270
686
  """Return the Z axis value (equivalent to joystick.getAxis(2))."""
271
- if backend == 'pyglet':
272
- return self._device.z
273
- elif backend == 'glfw':
274
- return self.getAxis(2)
275
- else:
276
- return self._device.get_axis(2)
687
+ return self.getAxis(JOYSTICK_AXIS_Z)
277
688
 
278
- def getAllAxes(self):
279
- """Get a list of all current axis values."""
280
- axes = []
281
- if backend == 'pyglet':
282
- names = ['x', 'y', 'z', 'rx', 'ry', 'rz', ]
283
- for axName in names:
284
- if hasattr(self._device, axName):
285
- axes.append(getattr(self._device, axName))
286
- elif backend == 'glfw':
287
- _axes, count = glfw.get_joystick_axes(self._device)
288
- for i in range(count):
289
- axes.append(_axes[i])
290
- else:
291
- for id in range(self._device.get_numaxes()):
292
- axes.append(self._device.get_axis(id))
293
- return axes
689
+ def getRX(self):
690
+ """Return the RX axis value (equivalent to joystick.getAxis(3))."""
691
+ return self.getAxis(JOYSTICK_AXIS_RX)
294
692
 
295
- def getNumAxes(self):
296
- """Return the number of joystick axes found.
693
+ def getRY(self):
694
+ """Return the RY axis value (equivalent to joystick.getAxis(4))."""
695
+ return self.getAxis(JOYSTICK_AXIS_RY)
696
+
697
+ def getRZ(self):
698
+ """Return the RZ axis value (equivalent to joystick.getAxis(5))."""
699
+ return self.getAxis(JOYSTICK_AXIS_RZ)
700
+
701
+ # --------------------------------------------------------------------------
702
+ # Button methods
703
+ #
704
+
705
+ def getNumButtons(self):
706
+ """Get the number of buttons on the device (`int`).
707
+
708
+ Returns
709
+ -------
710
+ int
711
+ The number of buttons on the joystick.
297
712
 
298
713
  """
299
- if backend == 'pyglet':
300
- return len(self.getAllAxes())
301
- elif backend == 'glfw':
302
- _, count = glfw.get_joystick_axes(self._device)
303
- return count
304
- else:
305
- return self._device.get_numaxes()
714
+ return self._numButtons
306
715
 
307
- def getAxis(self, axisId):
308
- """Get the value of an axis by an integer id.
716
+ def getAllButtons(self):
717
+ """Get the state of all buttons on the devics.
718
+
719
+ Returns
720
+ -------
721
+ list
722
+ A list of button states. Each state is a boolean.
309
723
 
310
- (from 0 to number of axes - 1)
311
724
  """
312
- if backend == 'pyglet':
313
- val = self.getAllAxes()[axisId]
314
- if val is None:
315
- val = 0
316
- return val
317
- elif backend == 'glfw':
318
- val, _ = glfw.get_joystick_axes(self._device)
319
- return val[axisId]
320
- else:
321
- return self._device.get_axis(axisId)
725
+ return self._joy.getAllButtons()
726
+
727
+ def getButton(self, buttonId):
728
+ """Get the state of a given button on the device (`bool`).
729
+
730
+ Parameters
731
+ ----------
732
+ buttonId : int, str or list
733
+ The button ID to get the state for. If a string is supplied, the
734
+ name of the button is used to get the state. If a list of button
735
+ indices or names is supplied, a list of states is returned.
736
+
737
+ Returns
738
+ -------
739
+ bool or list
740
+ The state of the button. If a list of buttons was passed as
741
+ `buttonId`, a list of states is returned where each state is a
742
+ boolean.
743
+
744
+ """
745
+ if isinstance(buttonId, str): # name supplied
746
+ buttonId = self._getIndexFromName('buttons', buttonId)
747
+
748
+ if isinstance(buttonId, (list, tuple)):
749
+ return [self.getButton(b) for b in buttonId]
750
+
751
+ return self._joy.getButton(buttonId)
752
+
753
+ # --------------------------------------------------------------------------
754
+ # Hat methods
755
+ #
756
+ def getNumHats(self):
757
+ """Get the number of hats on this joystick.
758
+
759
+ The GLFW backend makes no distinction between hats and buttons. Calling
760
+ 'getNumHats()' will return 0.
761
+
762
+ """
763
+ return self._numHats
764
+
765
+ def getAllHats(self):
766
+ """Get the current values of all available hats.
767
+
768
+ Returns
769
+ -------
770
+ list
771
+ Each value is a tuple (x, y) where x and y axis states are trinary
772
+ (-1, 0, +1)
773
+
774
+ """
775
+ return self._joy.getAllHats()
776
+
777
+ def getHat(self, hatId=0):
778
+ """Get the position of a particular hat.
779
+
780
+ Parameters
781
+ ----------
782
+ hatId : int or str
783
+ The hat ID to get the position for. If a string is supplied, the
784
+ name of the hat is used to get the position.
785
+
786
+ Returns
787
+ -------
788
+ tuple
789
+ The position returned is an (x, y) tuple where x and y can be -1, 0
790
+ or +1.
791
+
792
+ """
793
+ if isinstance(hatId, str): # name supplied
794
+ hatId = self._getIndexFromName('hats', hatId)
795
+
796
+ if isinstance(hatId, (list, tuple)):
797
+ return [self.getHat(h) for h in hatId]
798
+
799
+ return self._joy.getHat(hatId)
322
800
 
323
801
 
324
802
  class XboxController(Joystick):
@@ -330,8 +808,9 @@ class XboxController(Joystick):
330
808
  y_btn_state = xbctrl.y # get the state of the 'Y' button
331
809
 
332
810
  """
333
- def __init__(self, id, *args, **kwargs):
334
- super(XboxController, self).__init__(id)
811
+ def __init__(self, deviceIndex, **kwargs):
812
+ deviceIndex = kwargs.get('id', deviceIndex) # legacy param
813
+ super(XboxController, self).__init__(deviceIndex)
335
814
 
336
815
  # validate if this is an Xbox controller by its reported name
337
816
  if self.name.find("Xbox 360") == -1:
@@ -595,3 +1074,123 @@ class XboxController(Joystick):
595
1074
  val = 1.0
596
1075
 
597
1076
  return val
1077
+
1078
+
1079
+ # Setter and getter methods for the joystick backend, this allows us to sanity
1080
+ # check the backend value before setting it.
1081
+
1082
+ def getBackend():
1083
+ """Get the joystick backend in use.
1084
+
1085
+ Returns
1086
+ -------
1087
+ str
1088
+ The name of the joystick backend in use.
1089
+
1090
+ """
1091
+ return backend
1092
+
1093
+
1094
+ def setBackend(inputLib):
1095
+ """Set the joystick backend (input library) to use.
1096
+
1097
+ Successive instances of `Joystick` will use the backend set here. If the
1098
+ backend is not available, a `ValueError` is raised.
1099
+
1100
+ Parameters
1101
+ ----------
1102
+ inputLib : str or None
1103
+ The name of the joystick input library to use. If None, the value will
1104
+ be set to match the window backend name. You cannot set the backend to
1105
+ None if there are no open windows.
1106
+
1107
+ Examples
1108
+ --------
1109
+ Set the joystick backend to 'glfw'::
1110
+
1111
+ joystick.setBackend('glfw')
1112
+ joy = joystick.Joystick(0) # uses the GLFW backend
1113
+
1114
+ joy.inputLib == 'glfw' # True
1115
+
1116
+ Use the window backend as the joystick backend::
1117
+
1118
+ win = visual.Window([400, 400], winType='pyglet') # create first!
1119
+ joystick.setBackend(None) # set to window backend
1120
+ print(joystick.getBackend()) # 'pyglet'
1121
+
1122
+ """
1123
+ if inputLib is None:
1124
+ if not visual.openWindows:
1125
+ raise ValueError("Cannot determine the window backend.")
1126
+
1127
+ win = visual.openWindows[0]()
1128
+ inputLib = win.backend.winTypeName # get window backend name
1129
+
1130
+ # get available backends and check if the requested backend is available
1131
+ availableBackends = getJoystickInterfaces()
1132
+ if inputLib not in availableBackends.keys():
1133
+ raise JoystickBackendNotAvailableError(
1134
+ "Joystick backend '{}' is not available.".format(inputLib))
1135
+
1136
+ global backend # set the global backend
1137
+ backend = inputLib
1138
+
1139
+
1140
+ def getJoystickInterfaces():
1141
+ """Get available joystick input interfaces.
1142
+
1143
+ Returns
1144
+ -------
1145
+ dict
1146
+ A mapping of joystick interfaces available where the key is the input
1147
+ library identifier and the value is the joystick interface class.
1148
+ Setting the backend to one of these keys will use the corresponding
1149
+ joystick interface.
1150
+
1151
+ """
1152
+ foundJoystickInterfaces = {}
1153
+
1154
+ # look for subclasses of JoystickInterface in this module's namespace
1155
+ for name in globals():
1156
+ obj = globals()[name]
1157
+ if isinstance(obj, type) and issubclass(obj, BaseJoystickInterface):
1158
+ if obj != BaseJoystickInterface:
1159
+ foundJoystickInterfaces[obj._inputLib] = obj
1160
+
1161
+ return foundJoystickInterfaces.copy()
1162
+
1163
+
1164
+ def getAllJoysticks():
1165
+ """Enumerate all available joysticks and return a dictionary of their
1166
+ information.
1167
+
1168
+ Uses the presently set joystick backend to get the available joysticks.
1169
+
1170
+ Returns
1171
+ -------
1172
+ list
1173
+ A list of dictionaries containing information about each available
1174
+ joystick. Information varies depending on the joystick interface used,
1175
+ however the `'index'` key is always present and contains the index of
1176
+ the joystick. Passing this index to the `Joystick` constructor will
1177
+ create a joystick object for that device.
1178
+
1179
+ Examples
1180
+ --------
1181
+ Get information about all available joysticks::
1182
+
1183
+ joysticks = getAllJoysticks()
1184
+ for joy in joysticks:
1185
+ print(joy)
1186
+
1187
+ Create a `Joystick` object for the first joystick found::
1188
+
1189
+ joy = Joystick(joysticks[0]['index'])
1190
+
1191
+ """
1192
+ return Joystick.getAllJoysticks()
1193
+
1194
+
1195
+ if __name__ == "__main__":
1196
+ pass