psychopy 2024.1.3__py3-none-any.whl → 2024.2.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (331) hide show
  1. psychopy/.DS_Store +0 -0
  2. psychopy/CHANGELOG.txt +206 -0
  3. psychopy/GIT_SHA +1 -0
  4. psychopy/VERSION +1 -0
  5. psychopy/__init__.py +77 -15
  6. psychopy/app/Resources/classic/plugin16.png +0 -0
  7. psychopy/app/Resources/classic/plugin16@2x.png +0 -0
  8. psychopy/app/Resources/dark/plugin16.png +0 -0
  9. psychopy/app/Resources/dark/plugin16@2x.png +0 -0
  10. psychopy/app/Resources/light/plugin16.png +0 -0
  11. psychopy/app/Resources/light/plugin16@2x.png +0 -0
  12. psychopy/app/__init__.py +76 -2
  13. psychopy/app/_psychopyApp.py +126 -101
  14. psychopy/app/builder/builder.py +14 -10
  15. psychopy/app/builder/dialogs/__init__.py +8 -8
  16. psychopy/app/builder/dialogs/dlgsConditions.py +12 -13
  17. psychopy/app/builder/dialogs/paramCtrls.py +24 -57
  18. psychopy/app/builder/localizedStrings.py +11 -9
  19. psychopy/app/builder/validators.py +2 -2
  20. psychopy/app/coder/codeEditorBase.py +8 -8
  21. psychopy/app/coder/coder.py +4 -4
  22. psychopy/app/connections/sendusage.py +2 -2
  23. psychopy/app/connections/updates.py +9 -9
  24. psychopy/app/dialogs.py +34 -2
  25. psychopy/app/idle.py +31 -0
  26. psychopy/app/jobs.py +21 -3
  27. psychopy/app/linuxconfig/__init__.py +9 -0
  28. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  29. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +4602 -2540
  30. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  31. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +56 -54
  32. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +53 -43
  33. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  34. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +56 -54
  35. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  36. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1258 -1176
  37. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +9415 -5
  38. psychopy/app/pavlovia_ui/_base.py +33 -3
  39. psychopy/app/pavlovia_ui/search.py +0 -1
  40. psychopy/app/plugin_manager/dialog.py +104 -51
  41. psychopy/app/plugin_manager/packages.py +5 -0
  42. psychopy/app/plugin_manager/plugins.py +152 -67
  43. psychopy/app/preferencesDlg.py +8 -8
  44. psychopy/app/psychopyApp.py +11 -5
  45. psychopy/app/ribbon.py +124 -14
  46. psychopy/app/runner/runner.py +6 -1
  47. psychopy/app/stdout/stdOutRich.py +27 -11
  48. psychopy/app/themes/icons.py +52 -2
  49. psychopy/assets/__init__.py +0 -0
  50. psychopy/assets/click.png +0 -0
  51. psychopy/assets/clicknext.png +0 -0
  52. psychopy/assets/next.png +0 -0
  53. psychopy/assets/psychopy.ico +0 -0
  54. psychopy/assets/psychopy.png +0 -0
  55. psychopy/assets/templates/__init__.py +0 -0
  56. psychopy/assets/touch.png +0 -0
  57. psychopy/assets/touchnext.png +0 -0
  58. psychopy/assets/window.ico +0 -0
  59. psychopy/changes/2023.1.0.md +9 -0
  60. psychopy/changes/2024.1.0.md +16 -0
  61. psychopy/changes/__init__.py +0 -0
  62. psychopy/clock.py +2 -2
  63. psychopy/colors.py +2 -1
  64. psychopy/compatibility.py +53 -1
  65. psychopy/contrib/.DS_Store +0 -0
  66. psychopy/contrib/configobj/__init__.py +10 -8
  67. psychopy/data/__init__.py +3 -2
  68. psychopy/data/base.py +5 -5
  69. psychopy/data/experiment.py +130 -4
  70. psychopy/data/routine.py +56 -0
  71. psychopy/data/staircase.py +2 -2
  72. psychopy/data/trial.py +559 -97
  73. psychopy/data/utils.py +56 -21
  74. psychopy/demos/.DS_Store +0 -0
  75. psychopy/demos/builder/.DS_Store +0 -0
  76. psychopy/demos/builder/Design Templates/.DS_Store +0 -0
  77. psychopy/demos/builder/Experiments/.DS_Store +0 -0
  78. psychopy/demos/builder/Feature Demos/.DS_Store +0 -0
  79. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +375 -0
  80. psychopy/demos/builder/Feature Demos/buttonBox/readme.md +5 -0
  81. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +433 -0
  82. psychopy/demos/builder/Feature Demos/pilotMode/readme.md +7 -0
  83. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +4 -4
  84. psychopy/demos/builder/Hardware/.DS_Store +0 -0
  85. psychopy/demos/builder/Helper Tools/.DS_Store +0 -0
  86. psychopy/demos/coder/.DS_Store +0 -0
  87. psychopy/demos/coder/hardware/testSoundLatency.py +2 -2
  88. psychopy/demos/coder/iohub/.DS_Store +0 -0
  89. psychopy/demos/coder/misc/hdf5_2_csv +33 -0
  90. psychopy/event.py +30 -29
  91. psychopy/experiment/.DS_Store +0 -0
  92. psychopy/experiment/_experiment.py +6 -6
  93. psychopy/experiment/components/.DS_Store +0 -0
  94. psychopy/experiment/components/__init__.py +6 -3
  95. psychopy/experiment/components/_base.py +286 -131
  96. psychopy/experiment/components/aperture/.DS_Store +0 -0
  97. psychopy/experiment/components/brush/.DS_Store +0 -0
  98. psychopy/experiment/components/button/.DS_Store +0 -0
  99. psychopy/experiment/components/button/__init__.py +5 -1
  100. psychopy/experiment/components/buttonBox/.DS_Store +0 -0
  101. psychopy/experiment/components/buttonBox/__init__.py +21 -12
  102. psychopy/experiment/components/camera/.DS_Store +0 -0
  103. psychopy/experiment/components/code/.DS_Store +0 -0
  104. psychopy/experiment/components/dots/.DS_Store +0 -0
  105. psychopy/experiment/components/eyetracker_record/.DS_Store +0 -0
  106. psychopy/experiment/components/eyetracker_record/__init__.py +92 -30
  107. psychopy/experiment/components/form/.DS_Store +0 -0
  108. psychopy/experiment/components/form/__init__.py +6 -2
  109. psychopy/experiment/components/grating/.DS_Store +0 -0
  110. psychopy/experiment/components/grating/__init__.py +14 -3
  111. psychopy/experiment/components/image/.DS_Store +0 -0
  112. psychopy/experiment/components/image/__init__.py +14 -3
  113. psychopy/experiment/components/joyButtons/.DS_Store +0 -0
  114. psychopy/experiment/components/joystick/.DS_Store +0 -0
  115. psychopy/experiment/components/keyboard/.DS_Store +0 -0
  116. psychopy/experiment/components/keyboard/__init__.py +22 -10
  117. psychopy/experiment/components/microphone/.DS_Store +0 -0
  118. psychopy/experiment/components/microphone/__init__.py +59 -39
  119. psychopy/experiment/components/mouse/.DS_Store +0 -0
  120. psychopy/experiment/components/mouse/__init__.py +44 -29
  121. psychopy/experiment/components/movie/.DS_Store +0 -0
  122. psychopy/experiment/components/movie/__init__.py +1 -1
  123. psychopy/experiment/components/panorama/.DS_Store +0 -0
  124. psychopy/experiment/components/parallelOut/.DS_Store +0 -0
  125. psychopy/experiment/components/patch/.DS_Store +0 -0
  126. psychopy/experiment/components/polygon/.DS_Store +0 -0
  127. psychopy/experiment/components/polygon/__init__.py +26 -6
  128. psychopy/experiment/components/progress/.DS_Store +0 -0
  129. psychopy/experiment/components/progress/__init__.py +1 -1
  130. psychopy/experiment/components/ratingScale/.DS_Store +0 -0
  131. psychopy/experiment/components/resourceManager/.DS_Store +0 -0
  132. psychopy/experiment/components/roi/.DS_Store +0 -0
  133. psychopy/experiment/components/roi/__init__.py +5 -0
  134. psychopy/experiment/components/routineSettings/.DS_Store +0 -0
  135. psychopy/experiment/components/routineSettings/__init__.py +57 -10
  136. psychopy/experiment/components/serialOut/.DS_Store +0 -0
  137. psychopy/experiment/components/settings/.DS_Store +0 -0
  138. psychopy/experiment/components/settings/__init__.py +117 -42
  139. psychopy/experiment/components/slider/.DS_Store +0 -0
  140. psychopy/experiment/components/sound/.DS_Store +0 -0
  141. psychopy/experiment/components/sound/__init__.py +54 -19
  142. psychopy/experiment/components/static/.DS_Store +0 -0
  143. psychopy/experiment/components/static/__init__.py +1 -1
  144. psychopy/experiment/components/text/.DS_Store +0 -0
  145. psychopy/experiment/components/text/__init__.py +28 -3
  146. psychopy/experiment/components/textbox/.DS_Store +0 -0
  147. psychopy/experiment/components/textbox/__init__.py +12 -2
  148. psychopy/experiment/components/unknown/.DS_Store +0 -0
  149. psychopy/experiment/components/unknown/__init__.py +1 -2
  150. psychopy/experiment/components/unknownPlugin/.DS_Store +0 -0
  151. psychopy/experiment/components/unknownPlugin/__init__.py +2 -2
  152. psychopy/experiment/components/variable/.DS_Store +0 -0
  153. psychopy/experiment/flow.py +11 -4
  154. psychopy/experiment/loops.py +85 -37
  155. psychopy/experiment/params.py +74 -32
  156. psychopy/experiment/py2js_transpiler.py +8 -1
  157. psychopy/experiment/routines/.DS_Store +0 -0
  158. psychopy/experiment/routines/_base.py +102 -22
  159. psychopy/experiment/routines/counterbalance/.DS_Store +0 -0
  160. psychopy/experiment/routines/counterbalance/__init__.py +5 -1
  161. psychopy/experiment/routines/eyetracker_calibrate/.DS_Store +0 -0
  162. psychopy/experiment/routines/eyetracker_validate/.DS_Store +0 -0
  163. psychopy/experiment/routines/pavlovia_survey/.DS_Store +0 -0
  164. psychopy/experiment/routines/photodiodeValidator/.DS_Store +0 -0
  165. psychopy/experiment/routines/photodiodeValidator/__init__.py +7 -6
  166. psychopy/experiment/routines/unknown/.DS_Store +0 -0
  167. psychopy/gui/wxgui.py +4 -4
  168. psychopy/hardware/.DS_Store +0 -0
  169. psychopy/hardware/__init__.py +1 -1
  170. psychopy/hardware/base.py +12 -0
  171. psychopy/hardware/camera/__init__.py +1 -15
  172. psychopy/hardware/cedrus.py +10 -11
  173. psychopy/hardware/crs/colorcal.py +13 -22
  174. psychopy/hardware/crs/optical.py +10 -20
  175. psychopy/hardware/emulator.py +17 -14
  176. psychopy/hardware/eyetracker.py +42 -118
  177. psychopy/hardware/gammasci.py +4 -15
  178. psychopy/hardware/keyboard.py +102 -11
  179. psychopy/hardware/listener.py +3 -0
  180. psychopy/hardware/microphone.py +148 -18
  181. psychopy/hardware/minolta.py +8 -15
  182. psychopy/hardware/photodiode.py +191 -16
  183. psychopy/hardware/photometer/__init__.py +11 -19
  184. psychopy/hardware/pr.py +8 -15
  185. psychopy/hardware/speaker.py +39 -4
  186. psychopy/info.py +0 -71
  187. psychopy/iohub/.DS_Store +0 -0
  188. psychopy/iohub/__init__.py +1 -1
  189. psychopy/iohub/client/__init__.py +30 -20
  190. psychopy/iohub/client/keyboard.py +24 -24
  191. psychopy/iohub/datastore/__init__.py +2 -2
  192. psychopy/iohub/datastore/util.py +2 -2
  193. psychopy/iohub/default_config.yaml +1 -1
  194. psychopy/iohub/devices/.DS_Store +0 -0
  195. psychopy/iohub/devices/__init__.py +112 -25
  196. psychopy/iohub/devices/deviceConfigValidation.py +2 -1
  197. psychopy/iohub/devices/experiment/default_experiment.yaml +12 -1
  198. psychopy/iohub/devices/experiment/supported_config_settings.yaml +5 -1
  199. psychopy/iohub/devices/eyetracker/.DS_Store +0 -0
  200. psychopy/iohub/devices/eyetracker/__init__.py +46 -0
  201. psychopy/iohub/devices/eyetracker/calibration/procedure.py +2 -2
  202. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +14 -2
  203. psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +3 -4
  204. psychopy/iohub/server.py +2 -2
  205. psychopy/iohub/start_iohub_process.py +3 -0
  206. psychopy/iohub/util/__init__.py +62 -70
  207. psychopy/layout.py +5 -5
  208. psychopy/logging.py +8 -1
  209. psychopy/microphone.py +10 -37
  210. psychopy/platform_specific/__init__.py +0 -2
  211. psychopy/platform_specific/darwin.py +1 -3
  212. psychopy/platform_specific/linux.py +31 -33
  213. psychopy/platform_specific/win32.py +38 -13
  214. psychopy/plugins/__init__.py +148 -116
  215. psychopy/plugins/util.py +39 -0
  216. psychopy/preferences/Darwin.spec +4 -2
  217. psychopy/preferences/FreeBSD.spec +4 -2
  218. psychopy/preferences/Linux.spec +4 -2
  219. psychopy/preferences/Windows.spec +4 -2
  220. psychopy/preferences/baseNoArch.spec +4 -2
  221. psychopy/preferences/preferences.py +47 -24
  222. psychopy/projects/pavlovia.py +47 -4
  223. psychopy/scripts/psyexpCompile.py +0 -4
  224. psychopy/session.py +153 -21
  225. psychopy/sound/__init__.py +31 -21
  226. psychopy/sound/_base.py +20 -3
  227. psychopy/sound/audioclip.py +320 -33
  228. psychopy/sound/backend_ptb.py +47 -58
  229. psychopy/sound/backend_pygame.py +1 -1
  230. psychopy/sound/backend_pysound.py +6 -15
  231. psychopy/sound/transcribe.py +53 -0
  232. psychopy/tests/.DS_Store +0 -0
  233. psychopy/tests/data/.DS_Store +0 -0
  234. psychopy/tests/data/TestUnknownPluginComponent_load_resave.psyexp +135 -0
  235. psychopy/tests/data/Test_textbox/test_ori_0_bottom right.png +0 -0
  236. psychopy/tests/data/Test_textbox/test_ori_0_center.png +0 -0
  237. psychopy/tests/data/Test_textbox/test_ori_0_top left.png +0 -0
  238. psychopy/tests/data/Test_textbox/test_ori_120_bottom right.png +0 -0
  239. psychopy/tests/data/Test_textbox/test_ori_120_center.png +0 -0
  240. psychopy/tests/data/Test_textbox/test_ori_120_top left.png +0 -0
  241. psychopy/tests/data/Test_textbox/test_ori_180_bottom right.png +0 -0
  242. psychopy/tests/data/Test_textbox/test_ori_180_center.png +0 -0
  243. psychopy/tests/data/Test_textbox/test_ori_180_top left.png +0 -0
  244. psychopy/tests/data/Test_textbox/test_ori_240_bottom right.png +0 -0
  245. psychopy/tests/data/Test_textbox/test_ori_240_center.png +0 -0
  246. psychopy/tests/data/Test_textbox/test_ori_240_top left.png +0 -0
  247. psychopy/tests/data/correctScript/.DS_Store +0 -0
  248. psychopy/tests/data/test_components/testClearKeyboard/testClearKeyboard.psyexp +200 -0
  249. psychopy/tests/data/test_session/.DS_Store +0 -0
  250. psychopy/tests/data/test_session/root/testFutureTrials/testFutureTrials.psyexp +155 -0
  251. psychopy/tests/data/test_session/root/testTrialNav/trialNav.psyexp +158 -0
  252. psychopy/tests/test_app/.DS_Store +0 -0
  253. psychopy/tests/test_app/conftest.py +2 -2
  254. psychopy/tests/test_app/test_speed.py +4 -1
  255. psychopy/tests/test_data/test_TrialHandler2.py +146 -1
  256. psychopy/tests/test_experiment/.DS_Store +0 -0
  257. psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +3 -3
  258. psychopy/tests/test_experiment/needs_wx/test_components.py +2 -2
  259. psychopy/tests/test_experiment/test_components/test_KeyboardComponent.py +28 -0
  260. psychopy/tests/test_experiment/test_components/test_UnknownPluginComponent.py +27 -0
  261. psychopy/tests/test_experiment/test_components/test_base_components.py +58 -0
  262. psychopy/tests/test_experiment/test_py2js.py +1 -1
  263. psychopy/tests/test_hardware/test_keyboard.py +184 -16
  264. psychopy/tests/test_hardware/test_ports.py +1 -11
  265. psychopy/tests/test_liaison/test_Liaison.py +47 -0
  266. psychopy/tests/test_misc/test_core.py +5 -0
  267. psychopy/tests/test_session/test_Session.py +5 -1
  268. psychopy/tests/test_tools/test_versionchooser.py +39 -8
  269. psychopy/tests/test_visual/test_all_stimuli.py +0 -97
  270. psychopy/tests/test_visual/test_image.py +6 -5
  271. psychopy/tests/test_visual/test_textbox.py +36 -0
  272. psychopy/tests/utils.py +4 -0
  273. psychopy/tools/filetools.py +1 -1
  274. psychopy/tools/pkgtools.py +160 -137
  275. psychopy/tools/versionchooser.py +10 -10
  276. psychopy/tools/wizard.py +3 -3
  277. psychopy/visual/.DS_Store +0 -0
  278. psychopy/visual/backends/pygletbackend.py +24 -13
  279. psychopy/visual/basevisual.py +5 -11
  280. psychopy/visual/button.py +2 -14
  281. psychopy/visual/helpers.py +5 -5
  282. psychopy/visual/line.py +1 -2
  283. psychopy/visual/movie2.py +7 -816
  284. psychopy/visual/movie3.py +7 -589
  285. psychopy/visual/movies/__init__.py +8 -11
  286. psychopy/visual/movies/frame.py +5 -2
  287. psychopy/visual/movies/players/ffpyplayer_player.py +5 -2
  288. psychopy/visual/noise.py +8 -7
  289. psychopy/visual/patch.py +7 -16
  290. psychopy/visual/progress.py +1 -1
  291. psychopy/visual/radial.py +9 -7
  292. psychopy/visual/ratingscale.py +8 -1415
  293. psychopy/visual/secondorder.py +10 -9
  294. psychopy/visual/shape.py +7 -2
  295. psychopy/visual/text.py +1 -1
  296. psychopy/visual/textbox2/textbox2.py +28 -5
  297. psychopy/web.py +5 -2
  298. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/METADATA +8 -13
  299. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/RECORD +313 -219
  300. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/WHEEL +1 -1
  301. psychopy/app/Resources/click.png +0 -0
  302. psychopy/app/Resources/next.png +0 -0
  303. psychopy/experiment/components/patch/__init__.py +0 -121
  304. psychopy/experiment/components/patch/classic/patch.png +0 -0
  305. psychopy/experiment/components/patch/dark/patch.png +0 -0
  306. psychopy/experiment/components/patch/dark/patch@2x.png +0 -0
  307. psychopy/experiment/components/patch/light/patch.png +0 -0
  308. psychopy/experiment/components/patch/light/patch@2x.png +0 -0
  309. psychopy/experiment/components/ratingScale/__init__.py +0 -337
  310. psychopy/experiment/components/ratingScale/classic/ratingscale.png +0 -0
  311. psychopy/experiment/components/ratingScale/classic/ratingscale@2x.png +0 -0
  312. psychopy/experiment/components/ratingScale/dark/ratingScale@2x.png +0 -0
  313. psychopy/experiment/components/ratingScale/dark/ratingscale.png +0 -0
  314. psychopy/experiment/components/ratingScale/light/ratingScale@2x.png +0 -0
  315. psychopy/experiment/components/ratingScale/light/ratingscale.png +0 -0
  316. psychopy/platform_specific/posix.py +0 -16
  317. psychopy/tests/test_sound/test_microphone.py +0 -217
  318. psychopy/tests/test_visual/test_ratingScale.py +0 -299
  319. /psychopy/{app/Resources → assets}/Psychopy Window Favicon@16w.png +0 -0
  320. /psychopy/{app/Resources → assets}/Psychopy Window Favicon@32w.png +0 -0
  321. /psychopy/{app/Resources → assets}/USB-C.png +0 -0
  322. /psychopy/{app/Resources → assets}/USB.png +0 -0
  323. /psychopy/{app/Resources → assets}/creditCard.png +0 -0
  324. /psychopy/{app/Resources → assets}/default.mp3 +0 -0
  325. /psychopy/{app/Resources → assets}/default.mp4 +0 -0
  326. /psychopy/{app/Resources → assets}/default.png +0 -0
  327. /psychopy/{app/Resources → assets/templates}/instruct1.png +0 -0
  328. /psychopy/{app/Resources → assets/templates}/instruct2.png +0 -0
  329. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/entry_points.txt +0 -0
  330. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/AUTHORS.md +0 -0
  331. {psychopy-2024.1.3.dist-info → psychopy-2024.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -8,6 +8,7 @@ import sys
8
8
 
9
9
  import wx
10
10
  import wx.html2
11
+ import requests
11
12
 
12
13
  from psychopy.localization import _translate
13
14
  from psychopy.projects import pavlovia
@@ -90,8 +91,9 @@ class PavloviaMiniBrowser(wx.Dialog):
90
91
  self._loggingIn = True
91
92
  authURL, state = pavlovia.getAuthURL()
92
93
  self.browser.Bind(wx.html2.EVT_WEBVIEW_ERROR, self.onConnectionErr)
93
- self.browser.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.checkForLoginURL)
94
+ self.browser.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.getAccessTokenFromURL)
94
95
  self.browser.LoadURL(authURL)
96
+ self.Close()
95
97
 
96
98
  def setURL(self, url):
97
99
  self.browser.LoadURL(url)
@@ -113,10 +115,38 @@ class PavloviaMiniBrowser(wx.Dialog):
113
115
  if 'INET_E_DOWNLOAD_FAILURE' in event.GetString():
114
116
  self.EndModal(wx.ID_EXIT)
115
117
  raise Exception("{}: No internet connection available.".format(event.GetString()))
118
+
119
+ def getAccessTokenFromURL(self, event):
120
+ """
121
+ Parse the redirect url from a login request for the parameter `code`, this is
122
+ the "Auth code" which is used later to get an access token.
116
123
 
117
- def checkForLoginURL(self, event):
124
+ Parameters
125
+ ----------
126
+ event : wx.html2.EVT_WEBVIEW_LOADED
127
+ Load event from the browser window.
128
+ """
129
+ # get URL
118
130
  url = event.GetURL()
119
- if 'access_token=' in url:
131
+ # get auth code from URL
132
+ if "code=" in url:
133
+ # get state from redirect url
134
+ self.tokenInfo['state'] = self.getParamFromURL('state', url)
135
+ # if returned an auth code, use it to get a token
136
+ resp = requests.post(
137
+ "https://gitlab.pavlovia.org/oauth/token",
138
+ params={
139
+ 'client_id': pavlovia.client_id,
140
+ 'code': self.getParamFromURL("code", url),
141
+ 'grant_type': "authorization_code",
142
+ 'redirect_uri': pavlovia.redirect_url,
143
+ 'code_verifier': pavlovia.code_verifier
144
+ }
145
+ ).json()
146
+ # use the json response from that http request to get access remaining token info
147
+ self.tokenInfo['token'] = resp['access_token']
148
+ self.tokenInfo['tokenType'] = resp['token_type']
149
+ elif "access_token=" in url:
120
150
  self.tokenInfo['token'] = self.getParamFromURL(
121
151
  'access_token', url)
122
152
  self.tokenInfo['tokenType'] = self.getParamFromURL(
@@ -14,7 +14,6 @@ from psychopy.projects import pavlovia
14
14
 
15
15
  import copy
16
16
  import wx
17
- from pkg_resources import parse_version
18
17
  from wx.lib import scrolledpanel as scrlpanel
19
18
  import wx.lib.mixins.listctrl as listmixin
20
19
 
@@ -8,6 +8,7 @@ from psychopy.app import getAppInstance
8
8
  from psychopy.app.plugin_manager import PluginManagerPanel, PackageManagerPanel, InstallStdoutPanel
9
9
  from psychopy.experiment import getAllElements
10
10
  from psychopy.localization import _translate
11
+ import psychopy.logging as logging
11
12
  import psychopy.tools.pkgtools as pkgtools
12
13
  import psychopy.app.jobs as jobs
13
14
  import sys
@@ -18,6 +19,10 @@ import psychopy.plugins as plugins
18
19
  pkgtools.refreshPackages() # build initial package cache
19
20
 
20
21
 
22
+ # flag to indicate if PsychoPy needs to be restarted after installing a package
23
+ NEEDS_RESTART = False
24
+
25
+
21
26
  class EnvironmentManagerDlg(wx.Dialog):
22
27
  def __init__(self, parent):
23
28
  wx.Dialog.__init__(
@@ -47,7 +52,17 @@ class EnvironmentManagerDlg(wx.Dialog):
47
52
  self.notebook.InsertPage(1, self.packageMgr, text=_translate("Packages"))
48
53
  # Buttons
49
54
  self.btns = self.CreateStdDialogButtonSizer(flags=wx.HELP | wx.CLOSE)
55
+ self.Bind(wx.EVT_CLOSE, self.onClose)
50
56
  self.border.Add(self.btns, border=12, flag=wx.EXPAND | wx.ALL)
57
+ # store button handles
58
+ self.closeBtn = self.helpBtn = None
59
+ for btn in self.btns.Children:
60
+ if not btn.Window:
61
+ continue
62
+ if btn.Window.GetId() == wx.ID_CLOSE:
63
+ self.closeBtn = btn.Window
64
+ if btn.Window.GetId() == wx.ID_HELP:
65
+ self.helpBtn = btn.Window
51
66
 
52
67
  self.pipProcess = None # handle to the current Job
53
68
 
@@ -75,12 +90,14 @@ class EnvironmentManagerDlg(wx.Dialog):
75
90
  """
76
91
  cmd = [sys.executable, "-m", "pip", "index", "versions", packageName,
77
92
  '--no-input', '--no-color']
93
+ env = os.environ.copy()
78
94
  # run command in subprocess
79
95
  output = sp.Popen(
80
96
  cmd,
81
97
  stdout=sp.PIPE,
82
98
  stderr=sp.PIPE,
83
99
  shell=False,
100
+ env=env,
84
101
  universal_newlines=True)
85
102
  stdout, stderr = output.communicate() # blocks until process exits
86
103
  nullVersion = {'All': [], 'Installed': '', 'Latest': ''}
@@ -166,6 +183,7 @@ class EnvironmentManagerDlg(wx.Dialog):
166
183
 
167
184
  # interpreter path
168
185
  pyExec = sys.executable
186
+ env = os.environ.copy()
169
187
 
170
188
  # build the shell command to run the script
171
189
  command = [pyExec, '-m', 'pip', 'uninstall', packageName, '--yes']
@@ -179,9 +197,12 @@ class EnvironmentManagerDlg(wx.Dialog):
179
197
  # flags=execFlags,
180
198
  inputCallback=self.output.writeStdOut, # both treated the same
181
199
  errorCallback=self.output.writeStdErr,
182
- terminateCallback=self.output.writeTerminus
200
+ terminateCallback=self.onUninstallExit
183
201
  )
184
- self.pipProcess.start()
202
+ self.pipProcess.start(env=env)
203
+
204
+ global NEEDS_RESTART # flag as needing a restart
205
+ NEEDS_RESTART = True
185
206
 
186
207
  def installPackage(self, packageName, version=None, extra=None):
187
208
  """Install a package.
@@ -222,25 +243,8 @@ class EnvironmentManagerDlg(wx.Dialog):
222
243
 
223
244
  # interpreter path
224
245
  pyExec = sys.executable
225
-
226
- # we need to use bundles on MacOS
227
- if sys.platform == "darwin":
228
- # determine installation path for bundle, create it if needed
229
- bundlePath = plugins.getBundleInstallTarget(packageName)
230
- if not os.path.exists(bundlePath):
231
- self.output.writeStdOut(
232
- "Creating bundle path `{}` for package `{}`.".format(
233
- bundlePath, packageName))
234
- os.mkdir(bundlePath) # make the directory
235
- else:
236
- self.output.writeStdOut(
237
- "Using existing bundle path `{}` for package `{}`.".format(
238
- bundlePath, packageName))
239
-
240
- # add the bundle to path, refresh makes it discoverable after install
241
- if bundlePath not in sys.path:
242
- sys.path.insert(0, bundlePath)
243
-
246
+ # environment
247
+ env = os.environ.copy()
244
248
  # if given a pyproject.toml file, do editable install of parent folder
245
249
  if str(packageName).endswith("pyproject.toml"):
246
250
  if sys.platform != "darwin":
@@ -250,7 +254,8 @@ class EnvironmentManagerDlg(wx.Dialog):
250
254
  # on Mac, build a wheel
251
255
  subprocess.call(
252
256
  [pyExec, '-m', 'build'],
253
- cwd=Path(packageName).parent
257
+ cwd=Path(packageName).parent,
258
+ env=env
254
259
  )
255
260
  # get wheel path
256
261
  packageName = [
@@ -259,16 +264,23 @@ class EnvironmentManagerDlg(wx.Dialog):
259
264
  # On MacOS, we need to install to target instead of user since py2app
260
265
  # doesn't support user installs correctly, this is a workaround for that
261
266
  env = os.environ.copy()
262
- if sys.platform != "darwin":
263
- # build the shell command to run the script
264
- command = [pyExec, '-m', 'pip', 'install', str(packageName),
265
- '--user', '--prefer-binary']
266
- # set the environment variable
267
- env['PYTHONUSERBASE'] = prefs.paths['packages']
267
+
268
+ # build the shell command to run the script
269
+ command = [pyExec, '-m', 'pip', 'install', str(packageName)]
270
+
271
+ # check if we are inside a venv, don't use --user if we are
272
+ if hasattr(sys, 'real_prefix') or (
273
+ hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
274
+ # we are in a venv
275
+ logging.warning(
276
+ "You are installing a package inside a virtual environment. "
277
+ "The package will be installed in the user site-packages directory."
278
+ )
268
279
  else:
269
- # use --target on MacOS with a bundle
270
- command = [pyExec, '-m', 'pip', 'install', packageName, '--target',
271
- bundlePath, '--prefer-binary']
280
+ command.append('--user')
281
+
282
+ # add other options to the command
283
+ command += ['--prefer-binary', '--no-input', '--no-color']
272
284
 
273
285
  # write command to output panel
274
286
  self.output.writeCmd(" ".join(command))
@@ -311,6 +323,7 @@ class EnvironmentManagerDlg(wx.Dialog):
311
323
  will be installed.
312
324
 
313
325
  """
326
+ # do install
314
327
  self.installPackage(
315
328
  packageName=pluginInfo.pipname,
316
329
  version=version,
@@ -319,6 +332,21 @@ class EnvironmentManagerDlg(wx.Dialog):
319
332
  }
320
333
  )
321
334
 
335
+ def uninstallPlugin(self, pluginInfo):
336
+ """Uninstall a plugin.
337
+
338
+ This deletes any bundles in the user's package directory, or uninstalls
339
+ packages from `site-packages`.
340
+
341
+ Parameters
342
+ ----------
343
+ pluginInfo : psychopy.app.plugin_manager.plugins.PluginInfo
344
+ Info object of the plugin to uninstall.
345
+
346
+ """
347
+ # do uninstall
348
+ self.uninstallPackage(pluginInfo.pipname)
349
+
322
350
  def onInstallExit(self, pid, exitCode):
323
351
  """
324
352
  Callback function to handle a pip process exiting. Prints a termination statement
@@ -328,7 +356,6 @@ class EnvironmentManagerDlg(wx.Dialog):
328
356
  if self.pipProcess is None:
329
357
  # if pip process is None, this has been called by mistake, do nothing
330
358
  return
331
-
332
359
  # write installation termination statement
333
360
  msg = "Installation complete"
334
361
  if 'pipname' in self.pipProcess.extra:
@@ -344,12 +371,17 @@ class EnvironmentManagerDlg(wx.Dialog):
344
371
  # enable plugin
345
372
  try:
346
373
  pluginInfo.activate()
347
- plugins.loadPlugin(pluginInfo.pipname)
374
+ # plugins.loadPlugin(pluginInfo.pipname)
348
375
  except RuntimeError:
349
376
  prefs.general['startUpPlugins'].append(pluginInfo.pipname)
350
377
  self.output.writeStdErr(_translate(
351
378
  "[Warning] Could not activate plugin. PsychoPy may need to restart for plugin to take effect."
352
379
  ))
380
+
381
+ global NEEDS_RESTART # flag as needing a restart
382
+ NEEDS_RESTART = True
383
+ showNeedsRestartDialog()
384
+
353
385
  # show list of components/routines now available
354
386
  emts = []
355
387
  for name, emt in getAllElements().items():
@@ -376,25 +408,46 @@ class EnvironmentManagerDlg(wx.Dialog):
376
408
 
377
409
  # clear pip process
378
410
  self.pipProcess = None
411
+ # refresh view
412
+ pkgtools.refreshPackages()
413
+ self.pluginMgr.updateInfo()
414
+
415
+ def onUninstallExit(self, pid, exitCode):
416
+ # write installation termination statement
417
+ msg = "Uninstall complete"
418
+ if 'pipname' in self.pipProcess.extra:
419
+ msg = f"Finished uninstalling %(pipname)s" % self.pipProcess.extra
420
+ self.output.writeTerminus(msg)
421
+ # clear pip process
422
+ self.pipProcess = None
379
423
 
380
424
  def onClose(self, evt=None):
381
- # Get changes to plugin states
382
- pluginChanges = self.pluginMgr.pluginList.getChanges()
383
-
384
- # If any plugins have been uninstalled, prompt user to restart
385
- if any(["uninstalled" in changes for changes in pluginChanges.values()]):
386
- msg = _translate(
387
- "It looks like you've uninstalled some plugins. In order for this to take effect, you will need to "
388
- "restart the PsychoPy app."
389
- )
425
+ if self.isBusy:
426
+ # if closing during an install, prompt user to reconsider
390
427
  dlg = wx.MessageDialog(
391
- None, msg,
392
- style=wx.ICON_WARNING | wx.OK
428
+ self,
429
+ _translate(
430
+ "There is currently an installation/uninstallation in progress, are you sure "
431
+ "you want to close?"
432
+ ),
433
+ style=wx.YES | wx.NO
393
434
  )
394
- dlg.ShowModal()
395
-
396
- # Repopulate component panels
397
- for frame in self.app.getAllFrames():
398
- if hasattr(frame, "componentButtons") and hasattr(frame.componentButtons, "populate"):
399
- frame.componentButtons.populate()
400
-
435
+ # if they change their mind, cancel closing
436
+ if dlg.ShowModal() == wx.ID_NO:
437
+ return
438
+
439
+ if evt is not None:
440
+ evt.Skip()
441
+
442
+
443
+ def showNeedsRestartDialog():
444
+ """Show a dialog asking the user if they would like to restart PsychoPy.
445
+ """
446
+ msg = _translate("Please restart PsychoPy to apply changes.")
447
+
448
+ # show a simple dialog that asks the user to restart PsychoPy
449
+ dlg = wx.MessageDialog(
450
+ None, msg, "Restart Required",
451
+ style=wx.ICON_INFORMATION | wx.OK
452
+ )
453
+ dlg.ShowModal()
@@ -1,6 +1,7 @@
1
1
  import webbrowser
2
2
 
3
3
  import wx
4
+ import os
4
5
  import sys
5
6
  import subprocess as sp
6
7
  from pypi_search import search as pypi
@@ -101,11 +102,15 @@ class PIPTerminalPanel(wx.Panel):
101
102
 
102
103
  def runCommand(self, cmd):
103
104
  """Run the command."""
105
+
106
+ env = os.environ.copy()
107
+
104
108
  emts = [self.preface, cmd]
105
109
  output = sp.Popen(' '.join(emts),
106
110
  stdout=sp.PIPE,
107
111
  stderr=sp.PIPE,
108
112
  shell=True,
113
+ env=env,
109
114
  universal_newlines=True)
110
115
  stdout, stderr = output.communicate()
111
116
  sys.stdout.write(stdout)