psychopy 2024.2.1__py3-none-any.whl → 2024.2.5__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 (252) hide show
  1. psychopy/.DS_Store +0 -0
  2. psychopy/CHANGELOG.txt +204 -0
  3. psychopy/GIT_SHA +1 -1
  4. psychopy/VERSION +1 -1
  5. psychopy/__init__.py +10 -1
  6. psychopy/__init__.py.orig +65 -0
  7. psychopy/app/.DS_Store +0 -0
  8. psychopy/app/Resources/.DS_Store +0 -0
  9. psychopy/app/_psychopyApp.py +11 -3
  10. psychopy/app/appData.spec +1 -1
  11. psychopy/app/builder/builder.py +1 -1
  12. psychopy/app/builder/builder.py.orig +3932 -0
  13. psychopy/app/builder/dialogs/__init__.py.orig +1679 -0
  14. psychopy/app/builder/dialogs/paramCtrls.py +1 -1
  15. psychopy/app/builder/dialogs/paramCtrls.py.orig +713 -0
  16. psychopy/app/colorpicker/__init__.py.orig +411 -0
  17. psychopy/app/cortex.log +0 -0
  18. psychopy/app/jobs.py +8 -1
  19. psychopy/app/locale/ar_001/.DS_Store +0 -0
  20. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +792 -1816
  21. psychopy/app/plugin_manager/dialog.py +9 -7
  22. psychopy/app/ribbon.py +2 -1
  23. psychopy/app/runner/runner.py +25 -13
  24. psychopy/clock.py +8 -4
  25. psychopy/core.py.orig +169 -0
  26. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +330 -0
  27. psychopy/demos/builder/Tools/.DS_Store +0 -0
  28. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  29. psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
  30. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +38 -0
  31. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +3418 -0
  32. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  33. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +2 -0
  34. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +15 -0
  35. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  36. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +562 -0
  37. psychopy/demos/coder/.DS_Store +0 -0
  38. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  39. psychopy/demos/coder/iohub/.iohpid +1 -0
  40. psychopy/demos/coder/iohub/eyetracking/.iohpid +1 -0
  41. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  42. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  43. psychopy/experiment/_experiment.py +32 -7
  44. psychopy/experiment/_experiment.py.orig +1032 -0
  45. psychopy/experiment/components/.DS_Store +0 -0
  46. psychopy/experiment/components/_base.py +13 -4
  47. psychopy/experiment/components/_base.py.orig +823 -0
  48. psychopy/experiment/components/form/.DS_Store +0 -0
  49. psychopy/experiment/components/microphone/__init__.py +10 -1
  50. psychopy/experiment/components/microphone/__init__.py.orig +490 -0
  51. psychopy/experiment/components/polygon/__init__.py +21 -22
  52. psychopy/experiment/components/settings/__init__.py +13 -14
  53. psychopy/experiment/components/settings/__init__.py.orig +1337 -0
  54. psychopy/experiment/components/textbox/__init__.py.orig +310 -0
  55. psychopy/experiment/components/webcam/.DS_Store +0 -0
  56. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  57. psychopy/experiment/flow.py +10 -8
  58. psychopy/experiment/loops.py.orig +829 -0
  59. psychopy/experiment/params.py +8 -3
  60. psychopy/experiment/params.py.orig +408 -0
  61. psychopy/experiment/routine.py.orig +503 -0
  62. psychopy/experiment/routines/_base.py +15 -6
  63. psychopy/experiment/routines/counterbalance/__init__.py +1 -0
  64. psychopy/gui/qtgui.py +14 -7
  65. psychopy/gui/util.py +10 -14
  66. psychopy/gui/wxgui.py +10 -4
  67. psychopy/hardware/.DS_Store +0 -0
  68. psychopy/hardware/brainproducts.py.orig +680 -0
  69. psychopy/hardware/iolab.py.orig +238 -0
  70. psychopy/hardware/manager.py +1 -1
  71. psychopy/hardware/photodiode.py +59 -27
  72. psychopy/hardware/speaker.py +4 -4
  73. psychopy/iohub/client/__init__.py +17 -0
  74. psychopy/iohub/client/keyboard.py +5 -0
  75. psychopy/iohub/datastore/__init__.py.orig +443 -0
  76. psychopy/iohub/datastore/util.py.orig +692 -0
  77. psychopy/iohub/devices/mouse/darwin.py.orig +427 -0
  78. psychopy/iohub/devices/mouse/linux2.py +4 -0
  79. psychopy/iohub/devices/mouse/linux2.py.orig +198 -0
  80. psychopy/iohub/devices/mouse/win32.py +5 -0
  81. psychopy/preferences/.DS_Store +0 -0
  82. psychopy/projects/pavlovia.py +10 -3
  83. psychopy/projects/pavlovia.py.orig +1295 -0
  84. psychopy/sound/backend_ptb.py +22 -5
  85. psychopy/sound/transcribe.py +24 -4
  86. psychopy/tests/.DS_Store +0 -0
  87. psychopy/tests/data/.DS_Store +0 -0
  88. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  89. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  90. psychopy/tests/data/aperture1_norm_local.png +0 -0
  91. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  92. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  93. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  94. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  95. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  96. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  97. psychopy/tests/data/blend_add_height_local.png +0 -0
  98. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  99. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  100. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  101. psychopy/tests/data/blend_add_norm_local.png +0 -0
  102. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  103. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  104. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  105. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  106. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  107. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  108. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  109. psychopy/tests/data/circleHex_height_local.png +0 -0
  110. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  111. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  112. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  113. psychopy/tests/data/circleHex_norm_local.png +0 -0
  114. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  115. psychopy/tests/data/color_comparison_local.png +0 -0
  116. psychopy/tests/data/correctScript/.DS_Store +0 -0
  117. psychopy/tests/data/dots_height_local.png +0 -0
  118. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  119. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  120. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  121. psychopy/tests/data/dots_norm_local.png +0 -0
  122. psychopy/tests/data/dots_stencil_local.png +0 -0
  123. psychopy/tests/data/elarray1_height_local.png +0 -0
  124. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  125. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  126. psychopy/tests/data/elarray1_norm_local.png +0 -0
  127. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  128. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  129. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  130. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  131. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  132. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  133. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  134. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  135. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  136. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  137. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  138. psychopy/tests/data/gabor1_height_local.png +0 -0
  139. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  140. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  141. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  142. psychopy/tests/data/gabor1_norm_local.png +0 -0
  143. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  144. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  145. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  146. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  147. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  148. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  149. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  150. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  151. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  152. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  153. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  154. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  155. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  156. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  157. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  158. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  159. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  160. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  161. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  162. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  163. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  164. psychopy/tests/data/numpyImage_height_local.png +0 -0
  165. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  166. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  167. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  168. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  169. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  170. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  171. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  172. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  173. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  174. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  175. psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
  176. psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
  177. psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
  178. psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
  179. psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
  180. psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
  181. psychopy/tests/data/text1_height_local.png +0 -0
  182. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  183. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  184. psychopy/tests/data/text1_norm_local.png +0 -0
  185. psychopy/tests/data/text1_stencil_local.png +0 -0
  186. psychopy/tests/data/wedge1_height_local.png +0 -0
  187. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  188. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  189. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  190. psychopy/tests/data/wedge1_norm_local.png +0 -0
  191. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  192. psychopy/tests/test_app/.DS_Store +0 -0
  193. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  194. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +177 -0
  195. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  196. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  197. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +168 -0
  198. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  199. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  200. psychopy/tests/test_data/.DS_Store +0 -0
  201. psychopy/tests/test_experiment/test_experiment.py +96 -0
  202. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +68 -0
  203. psychopy/tests/test_tools/test_arraytools.py +112 -0
  204. psychopy/tests/test_visual/test_image.py.orig +219 -0
  205. psychopy/tools/arraytools.py +47 -0
  206. psychopy/tools/versionchooser.py +1 -1
  207. psychopy/visual/backends/pygletbackend.py +26 -8
  208. psychopy/visual/basevisual.py.orig +1723 -0
  209. psychopy/visual/form.py.orig +1181 -0
  210. psychopy/visual/text.py.orig +752 -0
  211. psychopy/visual/textbox2/textbox2.py.orig +1315 -0
  212. psychopy/visual/window.py +13 -5
  213. psychopy/visual/windowwarp.py.orig +463 -0
  214. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/METADATA +9 -9
  215. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/RECORD +220 -84
  216. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/WHEEL +1 -1
  217. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/entry_points.txt +2 -0
  218. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  219. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  220. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  221. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  222. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  223. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  224. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  225. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  226. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  227. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  228. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  229. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  230. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  231. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  232. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  233. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  234. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  235. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  236. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  237. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  238. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  239. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  240. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  241. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  242. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  243. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  244. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  245. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  246. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  247. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  248. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  249. psychopy-2024.2.1.dist-info/licenses/AUTHORS.md +0 -138
  250. /psychopy/{app/locale/es_ES/LC_MESSAGE → demos/builder}/.DS_Store +0 -0
  251. /psychopy/{visual → demos/builder/Experiments}/.DS_Store +0 -0
  252. {psychopy-2024.2.1.dist-info → psychopy-2024.2.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,238 @@
1
+
2
+ """The `ioLab python library
3
+ <http://github.com/ioLab/python-ioLabs>`_ is now defunct and the company
4
+ no longer trades
5
+ """
6
+ <<<<<<< Updated upstream
7
+ # This file can't be named ioLabs.py, otherwise "import ioLabs" doesn't work.
8
+ # And iolabs.py (lowercase) did not solve it either, something is case
9
+ # insensitive somewhere
10
+
11
+ from numpy import ubyte
12
+ from psychopy import core, event, logging
13
+
14
+ try:
15
+ import ioLabs
16
+ from ioLabs import USBBox, REPORT, COMMAND
17
+ except ImportError:
18
+ err = """Failed to import the ioLabs library. If you're using your own
19
+ copy of python (not the Standalone distribution of PsychoPy) then
20
+ try installing it with:
21
+ > pip install ioLabs""".replace(' ', '')
22
+ logging.error(err)
23
+
24
+ from psychopy.constants import PRESSED, RELEASED
25
+ btn2str = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7',
26
+ 64: 'voice'}
27
+
28
+ # hack to fake a USBBox on ubuntu during documentation
29
+ import sys
30
+ if 'sphinx' in sys.modules:
31
+ USBBox = object
32
+
33
+ class ButtonBox(USBBox):
34
+ """PsychoPy's interface to ioLabs.USBBox. Voice key completely untested.
35
+
36
+ Original author: Jonathan Roberts
37
+ PsychoPy rewrite: Jeremy Gray, 2013
38
+ """
39
+
40
+ def __init__(self):
41
+ """Class to detect and report
42
+ `ioLab button box <http://www.iolab.co.uk>`_.
43
+
44
+ The ioLabs library needs to be installed. It is included in the
45
+ *Standalone* distributions of PsychoPy as of version 1.62.01.
46
+ Otherwise try "pip install ioLabs"
47
+
48
+ Usage::
49
+
50
+ from psychopy.hardware import iolab
51
+ bbox = iolab.ButtonBox()
52
+
53
+ For examples see the demos menu of the PsychoPy Coder or go to the
54
+ URL above.
55
+
56
+ All times are reported in units of seconds.
57
+ """
58
+ ioLabs.USBBox.__init__(self)
59
+ logging.debug('init iolabs bbox')
60
+ self.events = []
61
+ self.status = None # helps Builder
62
+ self._lastReset = 0.0 # time on baseclock when bbox clock was reset
63
+ self._baseclock = core.Clock() # for basetime, not RT time
64
+ self.resetClock(log=True) # internal clock on the bbox
65
+ msg = 'button box resetClock(log=True) took %.4fs'
66
+ logging.exp(msg % self._baseclock.getTime())
67
+
68
+ self.commands.add_callback(REPORT.KEYDN, self._onKeyDown)
69
+ self.commands.add_callback(REPORT.KEYUP, self._onKeyUp)
70
+ self.commands.add_callback(REPORT.RTCREP, self._onRtcRep)
71
+
72
+ # set up callbacks for key events ("key" = button and or voice key):
73
+ def _onKey(self, report):
74
+ report.rt = report.rtc / 1000.
75
+ report.btn = report.key_code # int
76
+ report.key = btn2str[report.key_code] # str
77
+ self.events.append(report)
78
+
79
+ def _onKeyDown(self, report):
80
+ report.direction = PRESSED
81
+ self._onKey(report)
82
+
83
+ def _onKeyUp(self, report):
84
+ report.direction = RELEASED
85
+ self._onKey(report)
86
+
87
+ def _onRtcRep(self, report):
88
+ # read internal clock without needing a button-press; not working
89
+ report.rt = self.commands.rtcget()['rtc'] / 1000.
90
+
91
+ def __del__(self):
92
+ # does not seem to ever get called
93
+ self.standby()
94
+ for rep in [REPORT.KEYDN, REPORT.KEYUP, REPORT.RTCREP]:
95
+ self.remove_callback(rep)
96
+ ioLabs.USBBox.__del__(self)
97
+
98
+ def standby(self):
99
+ """Disable all buttons and lights.
100
+ """
101
+ self.buttons.enabled = 0x00 # 8 bit pattern 0=disabled 1=enabled
102
+ self.leds.state = 0xFF # leds == port2 == lights, 8 bits 0=on 1=off
103
+ return self
104
+
105
+ def resetClock(self, log=True):
106
+ """Reset the clock on the bbox internal clock, e.g., at the start
107
+ of a trial.
108
+
109
+ ~1ms for me; logging is much faster than the reset
110
+ """
111
+ # better / faster than self.reset_clock() (no wait for report):
112
+ self.commands.resrtc()
113
+ self._lastReset = self._baseclock.getTime()
114
+ if log:
115
+ msg = 'reset bbox internal clock at basetime = %.3f'
116
+ logging.exp(msg % self._lastReset)
117
+
118
+ def _getTime(self, log=False):
119
+ """Return the time on the bbox internal clock, relative to last reset.
120
+
121
+ Status: rtcget() not working
122
+
123
+ `log=True` will log the bbox time and elapsed CPU (python) time.
124
+ """
125
+ bboxTime = self.commands.rtcget()['rtc'] / 1000.
126
+ logging.debug('bbox rtc: %.3f' % bboxTime)
127
+ if log:
128
+ cpuTime = self._baseclock.getTime() - self._lastReset
129
+ logging.debug('cpu time: %.3f' % cpuTime)
130
+
131
+ return bboxTime
132
+
133
+ def getBaseTime(self):
134
+ """Return the time since init (using the CPU clock, not ioLab bbox).
135
+
136
+ Aim is to provide a similar API as for a Cedrus box.
137
+ Could let both clocks run for a long time to assess relative drift.
138
+ """
139
+ return self._baseclock.getTime()
140
+
141
+ def setEnabled(self, buttonList=(0, 1, 2, 3, 4, 5, 6, 7), voice=False):
142
+ """Set a filter to suppress events from non-enabled buttons.
143
+
144
+ The ioLabs bbox filters buttons in hardware; here we just tell it
145
+ what we want:
146
+ None - disable all buttons
147
+ an integer (0..7) - enable a single button
148
+ a list of integers (0..7) - enable all buttons in the list
149
+
150
+ Set voice=True to enable the voiceKey - gets reported as button 64
151
+ """
152
+ allInRange = all([b in range(8) for b in buttonList])
153
+ if not (buttonList is None or allInRange):
154
+ raise ValueError('buttonList needs to be a list of 0..7, or None')
155
+ self.buttons.enabled = _list2bits(buttonList)
156
+ self.int0.enabled = int(voice)
157
+
158
+ def getEnabled(self):
159
+ """Return a list of the buttons that are currently enabled.
160
+ """
161
+ return _bits2list(self.buttons.enabled)
162
+
163
+ def setLights(self, lightList=(0, 1, 2, 3, 4, 5, 6, 7)):
164
+ """Turn on the specified LEDs (None, 0..7, list of 0..7)
165
+ """
166
+ self.leds.state = ~_list2bits(lightList)
167
+
168
+ def waitEvents(self, downOnly=True, timeout=0, escape='escape',
169
+ wait=0.002):
170
+ """Wait for and return the first button press event.
171
+
172
+ Always calls `clearEvents()` first (like PsychoPy keyboard waitKeys).
173
+
174
+ Use `downOnly=False` to include button-release events.
175
+
176
+ `escape` is a list/tuple of keyboard events that, if pressed, will
177
+ interrupt the bbox wait; `waitKeys` will return `None` in that case.
178
+
179
+ `timeout` is the max time to wait in seconds before returning `None`.
180
+ `timeout` of 0 means no time-out (= default).
181
+ """
182
+ self.clearEvents() # e.g., removes UP from previous DOWN
183
+ if timeout > 0:
184
+ c = core.Clock()
185
+ if escape and not type(escape) in [list, tuple]:
186
+ escape = [escape]
187
+ while True:
188
+ if wait:
189
+ core.wait(wait, 0) # throttle CPU; event RTs come from bbox
190
+ evt = self.getEvents(downOnly=downOnly)
191
+ if evt:
192
+ evt = evt[0]
193
+ break
194
+ if escape and event.getKeys(escape) or 0 < timeout < c.getTime():
195
+ return
196
+ return evt
197
+
198
+ def getEvents(self, downOnly=True):
199
+ """Detect and return a list of all events (likely just one); no block.
200
+
201
+ Use `downOnly=False` to include button-release events.
202
+ """
203
+ if downOnly is False:
204
+ raise NotImplementedError()
205
+ self.process_received_reports()
206
+ evts = []
207
+ for evt in self.events:
208
+ if evt.direction == PRESSED or not downOnly:
209
+ evts.append(evt)
210
+ return evts
211
+
212
+ def clearEvents(self):
213
+ """Discard all button / voice key events.
214
+ """
215
+ self.events[:] = []
216
+ self.commands.clear_received_reports()
217
+ logging.debug('bbox clear events')
218
+
219
+
220
+ pow2 = [2**i for i in range(8)]
221
+
222
+
223
+ def _list2bits(arg):
224
+ # return a numpy.ubyte with bits set based on integers 0..7 in arg
225
+ if type(arg) == int and 0 <= arg < 8:
226
+ return ubyte(pow2[arg])
227
+ elif hasattr(arg, '__iter__'):
228
+ return ubyte(sum([pow2[btn] for btn in arg]))
229
+ else: # None
230
+ return ubyte(0)
231
+
232
+ =======
233
+ >>>>>>> Stashed changes
234
+
235
+ raise DeprecationWarning("The ioLab button box is no longer being sold and the library is no"
236
+ "longer supported. This library has been removed from PsychoPy as of"
237
+ "version 2022.1. We would recommend you use an alternative button box"
238
+ "such as the LabHackers MilliKey <https://www.labhackers.com/millikey.html>")
@@ -891,7 +891,7 @@ class DeviceManager:
891
891
  win, text=str(n + 1),
892
892
  size=1, pos=0,
893
893
  alignment="center", anchor="center",
894
- letterHeight=0.5, bold=True,
894
+ letterHeight=0.25, bold=True,
895
895
  fillColor=None, color="white"
896
896
  )
897
897
  lbls.append(lbl)
@@ -155,7 +155,7 @@ class BasePhotodiodeGroup(base.BaseResponseDevice):
155
155
 
156
156
  return channels
157
157
 
158
- def findPhotodiode(self, win, channel=None):
158
+ def findPhotodiode(self, win, channel=None, retryLimit=5):
159
159
  """
160
160
  Draws rectangles on the screen and records photodiode responses to recursively find the location of the diode.
161
161
 
@@ -271,32 +271,46 @@ class BasePhotodiodeGroup(base.BaseResponseDevice):
271
271
  # if none of these have returned, rect is too small to cover the whole photodiode, so return
272
272
  return responsive
273
273
 
274
- # reset state
275
- self.state = [None] * self.channels
276
- self.dispatchMessages()
277
- self.clearResponses()
278
- # recursively shrink rect around the photodiode
279
- responsive = scanQuadrants()
280
- # if cancelled, warn and continue
281
- if responsive is None:
282
- logging.warn(
283
- "`findPhotodiode` procedure cancelled by user."
284
- )
285
- return (
286
- layout.Position(self.pos, units="norm", win=win),
287
- layout.Position(self.size, units="norm", win=win),
288
- )
289
- # if we didn't get any responses at all, prompt to try again
290
- if not responsive:
274
+ def handleNonResponse(label, rect, timeout=5):
275
+ # skip if retry limit hit
276
+ if retryLimit <= 0:
277
+ return None
278
+ # start a countdown
279
+ timer = core.CountdownTimer(start=timeout)
291
280
  # set label text to alert user
292
- label.text = (
281
+ msg = (
293
282
  "Received no responses from photodiode during `findPhotodiode`. Photodiode may not "
294
283
  "be connected or may be configured incorrectly.\n"
295
284
  "\n"
296
- "To continue, use the arrow keys to move the photodiode patch and use the "
297
- "plus/minus keys to resize it.\n"
298
- "\n"
299
- "Press ENTER when finished."
285
+ "To manually specify the photodiode's position, press ENTER. To quit, press "
286
+ "ESCAPE. Otherwise, will retry in {:.0f}s\n"
287
+ )
288
+ label.foreColor = "red"
289
+ # start a frame loop until they press enter
290
+ keys = []
291
+ while timer.getTime() > 0 and not keys:
292
+ # get keys
293
+ keys = kb.getKeys()
294
+ # skip if escape pressed
295
+ if "escape" in keys:
296
+ return None
297
+ # specify manually if return pressed
298
+ if "return" in keys:
299
+ return specifyManually(label=label, rect=rect)
300
+ # format label
301
+ label.text = msg.format(timer.getTime())
302
+ # show label and square
303
+ label.draw()
304
+ # flip
305
+ win.flip()
306
+ # if we timed out, retry whole find procedure
307
+ return self.findPhotodiode(win, channel=channel, retryLimit=retryLimit-1)
308
+
309
+ def specifyManually(label, rect):
310
+ # set label text to alert user
311
+ label.text = (
312
+ "Use the arrow keys to move the photodiode patch and use the plus/minus keys to "
313
+ "resize it. Press ENTER when finished, or press ESCAPE to quit.\n"
300
314
  )
301
315
  label.foreColor = "red"
302
316
  # revert to defaults
@@ -306,12 +320,18 @@ class BasePhotodiodeGroup(base.BaseResponseDevice):
306
320
  # start a frame loop until they press enter
307
321
  keys = []
308
322
  res = 0.05
309
- while "return" not in keys:
323
+ while "return" not in keys and "escape" not in keys:
310
324
  # get keys
311
325
  keys = kb.getKeys()
312
326
  # skip if escape pressed
313
327
  if "escape" in keys:
314
328
  return None
329
+ # finish if return pressed
330
+ if "return" in keys:
331
+ return (
332
+ layout.Position(self.pos, units="norm", win=win),
333
+ layout.Position(self.size, units="norm", win=win),
334
+ )
315
335
  # move rect according to arrow keys
316
336
  pos = list(rect.pos)
317
337
  if "left" in keys:
@@ -335,13 +355,25 @@ class BasePhotodiodeGroup(base.BaseResponseDevice):
335
355
  rect.draw()
336
356
  # flip
337
357
  win.flip()
338
- # wait for a keypress
339
- kb.waitKeys()
340
- # return defaults
358
+
359
+ # reset state
360
+ self.state = [None] * self.channels
361
+ self.dispatchMessages()
362
+ self.clearResponses()
363
+ # recursively shrink rect around the photodiode
364
+ responsive = scanQuadrants()
365
+ # if cancelled, warn and continue
366
+ if responsive is None:
367
+ logging.warn(
368
+ "`findPhotodiode` procedure cancelled by user."
369
+ )
341
370
  return (
342
371
  layout.Position(self.pos, units="norm", win=win),
343
372
  layout.Position(self.size, units="norm", win=win),
344
373
  )
374
+ # if we didn't get any responses at all, prompt to try again
375
+ if not responsive:
376
+ handleNonResponse(label=label, rect=rect)
345
377
  # clear all the events created by this process
346
378
  self.state = [None] * self.channels
347
379
  self.dispatchMessages()
@@ -8,7 +8,7 @@ class SpeakerDevice(BaseDevice):
8
8
  profiles = self.getAvailableDevices()
9
9
 
10
10
  # if index is default (-1), setup a default device index
11
- if not isinstance(index, (int, float)) or index < 0:
11
+ if not isinstance(index, (int, float)):
12
12
  index = profiles[0]['index'] # initialize as the first device
13
13
 
14
14
  # check if a default device is already set and update index
@@ -23,8 +23,8 @@ class SpeakerDevice(BaseDevice):
23
23
  if profile['deviceName'] == defaultDevice:
24
24
  index = profile['index']
25
25
 
26
- available_index = [profile['index'] for profile in profiles]
27
- if index < 0 or index not in available_index:
26
+ available_index = [profile['index'] for profile in profiles] + [-1]
27
+ if index not in available_index:
28
28
  logging.error("No speaker device found with index %d" % index)
29
29
 
30
30
  # store index
@@ -86,4 +86,4 @@ class SpeakerDevice(BaseDevice):
86
86
  }
87
87
  devices.append(device)
88
88
 
89
- return devices
89
+ return devices
@@ -10,6 +10,7 @@ import time
10
10
  import subprocess
11
11
  import json
12
12
  import signal
13
+ import atexit
13
14
  from weakref import proxy
14
15
 
15
16
  import psutil
@@ -31,6 +32,8 @@ from psychopy import constants
31
32
 
32
33
  getTime = Computer.getTime
33
34
 
35
+ SHUTDOWN_FUNCS = []
36
+
34
37
  _currentSessionInfo = None
35
38
 
36
39
  def windowInfoDict(win):
@@ -1000,6 +1003,9 @@ class ioHubConnection():
1000
1003
  Computer.iohub_process_id = self._server_process.pid
1001
1004
  Computer.iohub_process = psutil.Process(self._server_process.pid)
1002
1005
 
1006
+ global SHUTDOWN_FUNCS
1007
+ SHUTDOWN_FUNCS.append(self._shutDownServer)
1008
+
1003
1009
  # >>>>> Create open UDP port to ioHub Server
1004
1010
  server_udp_port = self._iohub_server_config.get('udp_port', 9000)
1005
1011
  from ..net import UDPClientConnection
@@ -1451,6 +1457,17 @@ class ioEvent():
1451
1457
  self.type,
1452
1458
  self.id)
1453
1459
 
1460
+
1461
+ def shutdownActiveConnections():
1462
+ """Shutdown any active ioHub connections that are currently running.
1463
+ """
1464
+ activeConnection = ioHubConnection.getActiveConnection()
1465
+ if activeConnection is not None and hasattr(activeConnection, 'shutdown'):
1466
+ activeConnection.shutdown()
1467
+
1468
+
1469
+ atexit.register(shutdownActiveConnections)
1470
+
1454
1471
  _lazyImports = """
1455
1472
  from psychopy.iohub.client.connect import launchHubServer
1456
1473
  from psychopy.iohub.client import keyboard
@@ -229,6 +229,11 @@ class Keyboard(ioHubDeviceView):
229
229
  """
230
230
  kb_state = self.getCurrentDeviceState()
231
231
 
232
+ # catch anything that is not a dictionary, usually indicates an error
233
+ if type(kb_state) is not dict:
234
+ raise RuntimeError(
235
+ 'Keyboard device state request failed, got: %s' % kb_state)
236
+
232
237
  events = {int(k): v for k, v in list(kb_state.get('events').items())}
233
238
  pressed_keys = {int(k): v for k, v in list(kb_state.get('pressed_keys', {}).items())}
234
239