psychopy 2025.1.1__py3-none-any.whl → 2025.2.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 (220) hide show
  1. psychopy/VERSION +1 -1
  2. psychopy/alerts/alertsCatalogue/4810.yaml +19 -0
  3. psychopy/alerts/alertsCatalogue/alertCategories.yaml +4 -0
  4. psychopy/alerts/alertsCatalogue/alertmsg.py +15 -1
  5. psychopy/alerts/alertsCatalogue/generateAlertmsg.py +2 -2
  6. psychopy/app/Resources/classic/add_many.png +0 -0
  7. psychopy/app/Resources/classic/add_many@2x.png +0 -0
  8. psychopy/app/Resources/classic/devices.png +0 -0
  9. psychopy/app/Resources/classic/devices@2x.png +0 -0
  10. psychopy/app/Resources/classic/photometer.png +0 -0
  11. psychopy/app/Resources/classic/photometer@2x.png +0 -0
  12. psychopy/app/Resources/dark/add_many.png +0 -0
  13. psychopy/app/Resources/dark/add_many@2x.png +0 -0
  14. psychopy/app/Resources/dark/devices.png +0 -0
  15. psychopy/app/Resources/dark/devices@2x.png +0 -0
  16. psychopy/app/Resources/dark/photometer.png +0 -0
  17. psychopy/app/Resources/dark/photometer@2x.png +0 -0
  18. psychopy/app/Resources/light/add_many.png +0 -0
  19. psychopy/app/Resources/light/add_many@2x.png +0 -0
  20. psychopy/app/Resources/light/devices.png +0 -0
  21. psychopy/app/Resources/light/devices@2x.png +0 -0
  22. psychopy/app/Resources/light/photometer.png +0 -0
  23. psychopy/app/Resources/light/photometer@2x.png +0 -0
  24. psychopy/app/_psychopyApp.py +35 -13
  25. psychopy/app/builder/builder.py +88 -35
  26. psychopy/app/builder/dialogs/__init__.py +69 -220
  27. psychopy/app/builder/dialogs/dlgsCode.py +29 -8
  28. psychopy/app/builder/dialogs/paramCtrls.py +1468 -904
  29. psychopy/app/builder/validators.py +25 -17
  30. psychopy/app/coder/coder.py +12 -1
  31. psychopy/app/coder/repl.py +5 -2
  32. psychopy/app/colorpicker/__init__.py +1 -1
  33. psychopy/app/deviceManager/__init__.py +1 -0
  34. psychopy/app/deviceManager/addDialog.py +218 -0
  35. psychopy/app/deviceManager/dialog.py +185 -0
  36. psychopy/app/deviceManager/panel.py +191 -0
  37. psychopy/app/deviceManager/utils.py +60 -0
  38. psychopy/app/idle.py +7 -0
  39. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  40. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +12695 -10592
  41. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  42. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +10199 -24
  43. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  44. psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +10199 -24
  45. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  46. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +11221 -9712
  47. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  48. psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +10200 -25
  49. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  50. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +10200 -25
  51. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  52. psychopy/app/locale/en_US/LC_MESSAGE/messages.po +10195 -18
  53. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  54. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +11917 -9101
  55. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  56. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +11924 -9103
  57. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  58. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +11917 -9101
  59. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  60. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +11084 -9569
  61. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  62. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +11590 -5806
  63. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  64. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +10199 -24
  65. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  66. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +11091 -9577
  67. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  68. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +11072 -9549
  69. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  70. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +11071 -9559
  71. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  72. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +10200 -25
  73. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  74. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +11072 -9560
  75. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  76. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +1485 -1137
  77. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  78. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +10199 -24
  79. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  80. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +11463 -8757
  81. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  82. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +10200 -25
  83. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  84. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +10200 -25
  85. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  86. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +10200 -25
  87. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  88. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +11288 -9434
  89. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  90. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +10200 -25
  91. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  92. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +10199 -24
  93. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  94. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +11441 -8747
  95. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  96. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +11069 -9545
  97. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  98. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +12085 -8268
  99. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  100. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +11929 -8022
  101. psychopy/app/plugin_manager/dialog.py +12 -3
  102. psychopy/app/plugin_manager/packageIndex.py +303 -0
  103. psychopy/app/plugin_manager/packages.py +203 -63
  104. psychopy/app/plugin_manager/plugins.py +120 -240
  105. psychopy/app/preferencesDlg.py +6 -1
  106. psychopy/app/psychopyApp.py +16 -4
  107. psychopy/app/runner/runner.py +10 -2
  108. psychopy/app/runner/scriptProcess.py +8 -3
  109. psychopy/app/stdout/stdOutRich.py +11 -4
  110. psychopy/app/themes/icons.py +3 -0
  111. psychopy/app/utils.py +61 -0
  112. psychopy/data/experiment.py +133 -23
  113. psychopy/data/routine.py +12 -0
  114. psychopy/data/staircase.py +42 -20
  115. psychopy/data/trial.py +20 -12
  116. psychopy/data/utils.py +42 -2
  117. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +22 -5
  118. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  119. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +2 -12
  120. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -8
  121. psychopy/demos/builder/Feature Demos/movies/movie.psyexp +220 -0
  122. psychopy/demos/builder/Feature Demos/movies/readme.md +3 -0
  123. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +1 -2
  124. psychopy/demos/builder/Hardware/camera/camera.psyexp +3 -16
  125. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +3 -16
  126. psychopy/demos/coder/hardware/hdf5_extract.py +133 -0
  127. psychopy/event.py +20 -15
  128. psychopy/experiment/_experiment.py +86 -10
  129. psychopy/experiment/components/__init__.py +3 -10
  130. psychopy/experiment/components/_base.py +9 -20
  131. psychopy/experiment/components/button/__init__.py +1 -1
  132. psychopy/experiment/components/buttonBox/__init__.py +50 -54
  133. psychopy/experiment/components/camera/__init__.py +137 -359
  134. psychopy/experiment/components/keyboard/__init__.py +17 -24
  135. psychopy/experiment/components/microphone/__init__.py +61 -110
  136. psychopy/experiment/components/movie/__init__.py +2 -3
  137. psychopy/experiment/components/serialOut/__init__.py +192 -93
  138. psychopy/experiment/components/settings/__init__.py +45 -27
  139. psychopy/experiment/components/sound/__init__.py +82 -73
  140. psychopy/experiment/components/soundsensor/__init__.py +43 -80
  141. psychopy/experiment/devices.py +303 -0
  142. psychopy/experiment/exports.py +20 -18
  143. psychopy/experiment/flow.py +7 -0
  144. psychopy/experiment/loops.py +47 -29
  145. psychopy/experiment/monitor.py +74 -0
  146. psychopy/experiment/params.py +48 -10
  147. psychopy/experiment/plugins.py +28 -108
  148. psychopy/experiment/py2js_transpiler.py +1 -1
  149. psychopy/experiment/routines/__init__.py +1 -1
  150. psychopy/experiment/routines/_base.py +59 -24
  151. psychopy/experiment/routines/audioValidator/__init__.py +19 -155
  152. psychopy/experiment/routines/visualValidator/__init__.py +25 -25
  153. psychopy/hardware/__init__.py +20 -57
  154. psychopy/hardware/button.py +15 -2
  155. psychopy/hardware/camera/__init__.py +2237 -1394
  156. psychopy/hardware/joystick/__init__.py +1 -1
  157. psychopy/hardware/keyboard.py +5 -8
  158. psychopy/hardware/listener.py +4 -1
  159. psychopy/hardware/manager.py +75 -35
  160. psychopy/hardware/microphone.py +52 -6
  161. psychopy/hardware/monitor.py +144 -0
  162. psychopy/hardware/photometer/__init__.py +156 -117
  163. psychopy/hardware/serialdevice.py +16 -2
  164. psychopy/hardware/soundsensor.py +4 -1
  165. psychopy/iohub/devices/deviceConfigValidation.py +2 -1
  166. psychopy/iohub/devices/keyboard/darwin.py +8 -5
  167. psychopy/iohub/util/__init__.py +7 -8
  168. psychopy/localization/generateTranslationTemplate.py +208 -116
  169. psychopy/localization/messages.pot +4305 -3502
  170. psychopy/monitors/MonitorCenter.py +174 -74
  171. psychopy/plugins/__init__.py +6 -4
  172. psychopy/preferences/devices.py +80 -0
  173. psychopy/preferences/generateHints.py +2 -1
  174. psychopy/preferences/preferences.py +35 -11
  175. psychopy/scripts/psychopy-pkgutil.py +969 -0
  176. psychopy/scripts/psyexpCompile.py +1 -1
  177. psychopy/session.py +34 -38
  178. psychopy/sound/__init__.py +6 -260
  179. psychopy/sound/audioclip.py +164 -0
  180. psychopy/sound/backend_ptb.py +8 -0
  181. psychopy/sound/backend_pygame.py +10 -0
  182. psychopy/sound/backend_pysound.py +9 -0
  183. psychopy/sound/backends/__init__.py +0 -0
  184. psychopy/sound/microphone.py +3 -0
  185. psychopy/sound/sound.py +58 -0
  186. psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +1 -1
  187. psychopy/tests/data/duplicateHeaders.csv +2 -0
  188. psychopy/tests/test_app/test_builder/test_BuilderFrame.py +22 -7
  189. psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +0 -2
  190. psychopy/tests/test_data/test_utils.py +5 -1
  191. psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py +22 -2
  192. psychopy/tests/test_hardware/test_ports.py +0 -12
  193. psychopy/tests/test_tools/test_stringtools.py +1 -1
  194. psychopy/tools/attributetools.py +12 -5
  195. psychopy/tools/fontmanager.py +17 -14
  196. psychopy/tools/movietools.py +43 -2
  197. psychopy/tools/stringtools.py +33 -8
  198. psychopy/tools/versionchooser.py +1 -1
  199. psychopy/validation/audio.py +5 -1
  200. psychopy/validation/visual.py +5 -1
  201. psychopy/visual/basevisual.py +8 -7
  202. psychopy/visual/circle.py +2 -2
  203. psychopy/visual/image.py +29 -109
  204. psychopy/visual/movies/__init__.py +1800 -313
  205. psychopy/visual/polygon.py +4 -0
  206. psychopy/visual/shape.py +2 -2
  207. psychopy/visual/window.py +34 -11
  208. psychopy/voicekey/__init__.py +41 -669
  209. psychopy/voicekey/labjack_vks.py +7 -48
  210. psychopy/voicekey/parallel_vks.py +7 -42
  211. psychopy/voicekey/vk_tools.py +114 -263
  212. {psychopy-2025.1.1.dist-info → psychopy-2025.2.1.dist-info}/METADATA +17 -11
  213. {psychopy-2025.1.1.dist-info → psychopy-2025.2.1.dist-info}/RECORD +216 -184
  214. {psychopy-2025.1.1.dist-info → psychopy-2025.2.1.dist-info}/WHEEL +1 -1
  215. psychopy/visual/movies/players/__init__.py +0 -62
  216. psychopy/visual/movies/players/ffpyplayer_player.py +0 -1401
  217. psychopy/voicekey/demo_vks.py +0 -12
  218. psychopy/voicekey/signal.py +0 -42
  219. {psychopy-2025.1.1.dist-info → psychopy-2025.2.1.dist-info}/entry_points.txt +0 -0
  220. {psychopy-2025.1.1.dist-info → psychopy-2025.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Part of the PsychoPy library
4
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
5
+ # Distributed under the terms of the GNU General Public License (GPL).
6
+
1
7
  import webbrowser
2
8
 
3
9
  import wx
10
+ import wx.dataview
4
11
  import os
5
12
  import sys
6
13
  import subprocess as sp
@@ -8,13 +15,22 @@ import subprocess as sp
8
15
  from psychopy.app import utils
9
16
  from psychopy.app.themes import handlers, icons
10
17
  from psychopy.localization import _translate
11
- from psychopy.tools.pkgtools import (
12
- getInstalledPackages, getPackageMetadata, getPypiInfo, isInstalled,
13
- _isUserPackage, getInstallState
14
- )
18
+ from psychopy.tools.pkgtools import (getPackageMetadata, getPypiInfo)
19
+ from psychopy.app.plugin_manager.packageIndex import (
20
+ getInstalledPackages,
21
+ getRemotePackages,
22
+ isUserPackageInstalled,
23
+ isSystemPackageInstalled)
15
24
  from psychopy.tools.versionchooser import parseVersionSafely
16
25
 
17
26
 
27
+ # data flags for package status
28
+ PACKAGE_STATUS_INVALID = -1
29
+ PACKAGE_STATUS_NOT_INSTALLED = 0
30
+ PACKAGE_STATUS_INSTALLED_SYSTEM = 1
31
+ PACKAGE_STATUS_INSTALLED_USER = 2 # installed and user package
32
+
33
+
18
34
  class PackageManagerPanel(wx.Panel):
19
35
  def __init__(self, parent, dlg):
20
36
  wx.Panel.__init__(self, parent)
@@ -24,21 +40,18 @@ class PackageManagerPanel(wx.Panel):
24
40
  self.SetSizer(self.border)
25
41
  self.sizer = wx.BoxSizer(wx.HORIZONTAL)
26
42
  self.border.Add(self.sizer, proportion=1, border=6, flag=wx.ALL | wx.EXPAND)
27
- # Add package list
43
+ # self.packageList.Bind(wx.dataview.EVT_DATAVIEW_ITEM_ACTIVATED, self.onSelectItem)
44
+ self.detailsPanel = PackageDetailsPanel(self, dlg=self.dlg)
28
45
  self.packageList = PackageListCtrl(self, dlg=self.dlg)
29
- self.packageList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onSelectItem)
30
46
  self.sizer.Add(self.packageList, flag=wx.EXPAND | wx.ALL)
31
- # Seperator
32
47
  self.sizer.Add(wx.StaticLine(self, style=wx.LI_VERTICAL), border=6, flag=wx.EXPAND | wx.ALL)
33
- # Add details panel
34
- self.detailsPanel = PackageDetailsPanel(self, dlg=self.dlg)
35
48
  self.sizer.Add(self.detailsPanel, proportion=1, flag=wx.EXPAND | wx.ALL)
36
49
 
37
- def onSelectItem(self, evt=None):
38
- # Get package name
39
- pipname = evt.GetText()
40
- # Set pip details from name
41
- self.detailsPanel.package = pipname
50
+ def refresh(self, evt=None):
51
+ # Refresh package list
52
+ self.packageList.searchCtrl.SetValue("")
53
+ self.packageList.refresh()
54
+ self.detailsPanel.package = None
42
55
 
43
56
 
44
57
  class PIPTerminalPanel(wx.Panel):
@@ -147,15 +160,47 @@ class PackageListCtrl(wx.Panel):
147
160
  self.sizer = wx.BoxSizer(wx.VERTICAL)
148
161
  self.border.Add(self.sizer, proportion=1, border=12, flag=wx.ALL | wx.EXPAND)
149
162
 
163
+ # parent package list control
164
+ self.parent = parent
165
+ self.packageDetailsPanel = self.parent.detailsPanel
166
+
150
167
  # Search bar
151
- self.searchCtrl = wx.SearchCtrl(self)
168
+ self.searchCtrl = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER)
152
169
  self.searchCtrl.Bind(wx.EVT_SEARCH, self.refresh)
170
+ self.searchCtrl.Bind(wx.EVT_SEARCH_CANCEL, self.onSearchCancel)
171
+ self.searchCtrl.Bind(wx.EVT_TEXT, self.onSearchText)
153
172
  self.sizer.Add(self.searchCtrl, border=6, flag=wx.ALL | wx.EXPAND)
173
+
174
+ self.searchCtrl.ShowSearchButton(True)
175
+
154
176
  # Create list ctrl
155
- self.ctrl = utils.ListCtrl(self, style=wx.LC_REPORT | wx.LC_SINGLE_SEL)
156
- self.ctrl.setResizeColumn(0)
157
- self.ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)
158
- self.ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.onRightClick)
177
+ # self.ctrl = utils.ListCtrl(self, style=wx.LC_REPORT | wx.LC_SINGLE_SEL)
178
+ # self.ctrl.setResizeColumn(0)
179
+ # self.ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)
180
+ # self.ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.onRightClick)
181
+ # self.sizer.Add(self.ctrl, proportion=1, border=6, flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
182
+
183
+ self.ctrl = wx.dataview.TreeListCtrl(self)
184
+ self.ctrl.SetMinSize((120, 400))
185
+ self.ctrl.AppendColumn(_translate("Package"))
186
+ self.ctrl.AppendColumn(_translate("Installed"))
187
+
188
+ self.rootUserPackages = self.ctrl.AppendItem(self.ctrl.GetRootItem(), _translate("User Packages"))
189
+ self.rootSystemPackages = self.ctrl.AppendItem(self.ctrl.GetRootItem(), _translate("System Packages"))
190
+ self.rootRemotePackages = self.ctrl.AppendItem(self.ctrl.GetRootItem(), _translate("Available Packages"))
191
+
192
+ self.ctrl.SetItemText(self.rootUserPackages, _translate("User Packages"))
193
+ self.ctrl.SetItemText(self.rootSystemPackages, _translate("System Packages"))
194
+ self.ctrl.SetItemText(self.rootRemotePackages, _translate("Available Packages"))
195
+ self.ctrl.SetItemData(self.rootUserPackages, PACKAGE_STATUS_INVALID)
196
+ self.ctrl.SetItemData(self.rootSystemPackages, PACKAGE_STATUS_INVALID)
197
+ self.ctrl.SetItemData(self.rootRemotePackages, PACKAGE_STATUS_INVALID)
198
+ self.ctrl.SetColumnWidth(0, 200)
199
+ self.ctrl.SetColumnWidth(1, 100)
200
+
201
+ # self.ctrl.Bind(wx.dataview.EVT_TREELIST_ITEM_ACTIVATED, self.onItemSelected)
202
+ self.ctrl.Bind(wx.dataview.EVT_TREELIST_SELECTION_CHANGED, self.onItemSelected)
203
+ # self.ctrl.Bind(wx.dataview.EVT_DATAVIEW_ITEM_RIGHT_CLICK, self.onRightClick)
159
204
  self.sizer.Add(self.ctrl, proportion=1, border=6, flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
160
205
 
161
206
  # "Or..." label
@@ -180,6 +225,30 @@ class PackageListCtrl(wx.Panel):
180
225
 
181
226
  self.Layout()
182
227
 
228
+ def onSearchText(self, evt=None):
229
+ """On text change in search box. Used to refresh the package list if
230
+ the search term is empty since the box doesn't trigger a search
231
+ when the text is cleared.
232
+ """
233
+ # Get search term
234
+ searchTerm = self.searchCtrl.GetValue()
235
+
236
+ if not searchTerm:
237
+ # If empty, refresh
238
+ self.refresh()
239
+ return
240
+
241
+ if evt is not None:
242
+ evt.Skip()
243
+
244
+ def onSearchCancel(self, evt=None):
245
+ """On search cancel button pressed. Used to refresh the package list
246
+ if the search term is empty since the box doesn't trigger a search
247
+ when the text is cleared.
248
+ """
249
+ self.searchCtrl.SetValue("")
250
+ self.refresh()
251
+
183
252
  def onOpenPipTerminal(self, evt=None):
184
253
  # Make dialog
185
254
  dlg = wx.Dialog(self, title="PIP Terminal", size=(480, 480), style=wx.RESIZE_BORDER | wx.CAPTION | wx.CLOSE_BOX)
@@ -196,9 +265,18 @@ class PackageListCtrl(wx.Panel):
196
265
 
197
266
  def onItemSelected(self, evt=None):
198
267
  # Post event so it can be caught by parent
199
- evt.SetEventObject(self)
200
- wx.PostEvent(self, evt)
201
-
268
+ # evt.SetEventObject(self)
269
+ # wx.PostEvent(self, evt)
270
+
271
+ selection = evt.GetItem()
272
+ pipname = None
273
+ if selection.IsOk():
274
+ # check if valid
275
+ if self.ctrl.GetItemData(selection) != PACKAGE_STATUS_INVALID:
276
+ pipname = self.ctrl.GetItemText(selection)
277
+
278
+ self.packageDetailsPanel.package = pipname
279
+
202
280
  def onRightClick(self, evt=None):
203
281
  # Get package name
204
282
  package = evt.GetText()
@@ -206,11 +284,11 @@ class PackageListCtrl(wx.Panel):
206
284
  menu = wx.Menu()
207
285
  # Map menu functions
208
286
  menu.functions = {}
209
- if isInstalled(package) and _isUserPackage(package):
287
+ if isUserPackageInstalled(package):
210
288
  # Add uninstall if installed to user
211
289
  uninstallOpt = menu.Append(wx.ID_ANY, item=_translate("Uninstall"))
212
290
  menu.functions[uninstallOpt.GetId()] = self.onUninstall
213
- elif isInstalled(package):
291
+ elif isSystemPackageInstalled(package):
214
292
  # Add nothing if installed to protected system folder
215
293
  pass
216
294
  else:
@@ -257,35 +335,84 @@ class PackageListCtrl(wx.Panel):
257
335
  self.refresh()
258
336
 
259
337
  def refresh(self, evt=None):
338
+ # get states of items
339
+ rootRemoteWasExpanded = self.ctrl.IsExpanded(self.rootRemotePackages)
340
+ rootUserWasExpanded = self.ctrl.IsExpanded(self.rootUserPackages)
341
+ rootSystemWasExpanded = self.ctrl.IsExpanded(self.rootSystemPackages)
342
+
260
343
  # Get search term
261
344
  searchTerm = self.searchCtrl.GetValue()
262
- # Clear
263
- self.ctrl.ClearAll()
264
- self.ctrl.AppendColumn(_translate("Package"))
265
- self.ctrl.AppendColumn(_translate("Installed"))
266
-
267
- # Get installed packages
345
+ self.ctrl.DeleteAllItems()
346
+
347
+ self.rootUserPackages = self.ctrl.AppendItem(
348
+ self.ctrl.GetRootItem(), _translate("User Packages"))
349
+ self.ctrl.SetItemData(self.rootUserPackages, PACKAGE_STATUS_INVALID)
350
+ self.rootSystemPackages = self.ctrl.AppendItem(
351
+ self.ctrl.GetRootItem(), _translate("System Packages"))
352
+ self.ctrl.SetItemData(self.rootSystemPackages, PACKAGE_STATUS_INVALID)
353
+ self.rootRemotePackages = self.ctrl.AppendItem(
354
+ self.ctrl.GetRootItem(), _translate("Available Packages"))
355
+ self.ctrl.SetItemData(
356
+ self.rootRemotePackages, PACKAGE_STATUS_INVALID)
357
+
358
+ # get packages
268
359
  installedPackages = dict(getInstalledPackages())
269
- # If there's no search term, show all installed and return
270
- if searchTerm in (None, ""):
271
- for pkg, version in installedPackages.items():
272
- item = self.ctrl.Append((pkg, version))
273
- self.ctrl.SetItemFont(item, font=wx.Font().Bold())
274
- return
275
- # Add column for latest version if we're actually searching
276
- self.ctrl.AppendColumn(_translate("Latest"))
277
- # Get packages from search
278
- # Populate
279
- for pkg, version in installedPackages.items():
280
- if pkg is None:
281
- continue
282
- font = wx.Font()
283
- if searchTerm in pkg:
284
- # If installed, add row with value for installed version
285
- item = self.ctrl.Append((pkg, version))
286
- font = font.Bold()
287
- # Style new row according to install status
288
- self.ctrl.SetItemFont(item, font)
360
+
361
+ systemPackages = installedPackages['system']['packages']
362
+ userPackages = installedPackages['user']['packages']
363
+ availablePackages = getRemotePackages()['packages']
364
+
365
+ # filter on search
366
+ if searchTerm not in (None, ""):
367
+ self.searchCtrl.ShowCancelButton(True)
368
+ systemPackages = {k: v for k, v in systemPackages.items() if searchTerm in k}
369
+ userPackages = {k: v for k, v in userPackages.items() if searchTerm in k}
370
+ availablePackages = [v for v in availablePackages if searchTerm in v]
371
+ else:
372
+ self.searchCtrl.ShowCancelButton(False)
373
+
374
+ for pkg, version in systemPackages.items():
375
+ item = self.ctrl.AppendItem(self.rootSystemPackages, pkg)
376
+ self.ctrl.SetItemText(item, 1, version)
377
+ self.ctrl.SetItemData(item, PACKAGE_STATUS_INSTALLED_SYSTEM)
378
+ # self.ctrl.SetItemFont(item, font=wx.Font().Bold())
379
+
380
+ self.ctrl.SetItemText(
381
+ self.rootSystemPackages,
382
+ _translate("System Packages") + " (%d)" % len(systemPackages))
383
+
384
+ for pkg, version in userPackages.items():
385
+ item = self.ctrl.AppendItem(self.rootUserPackages, pkg)
386
+ self.ctrl.SetItemText(item, 1, version)
387
+ self.ctrl.SetItemData(item, PACKAGE_STATUS_INSTALLED_USER)
388
+ # self.ctrl.SetItemFont(item, font=wx.Font().Bold())
389
+
390
+ self.ctrl.SetItemText(
391
+ self.rootUserPackages,
392
+ _translate("User Packages") + " (%d)" % len(userPackages))
393
+
394
+ if len(availablePackages) > 1000:
395
+ availablePackages = availablePackages[:1000]
396
+ self.ctrl.SetItemText(
397
+ self.rootRemotePackages,
398
+ _translate("Available Packages") + " (1000+)")
399
+ else:
400
+ totalSearchMatches = len(availablePackages)
401
+ self.ctrl.SetItemText(
402
+ self.rootRemotePackages,
403
+ _translate("Available Packages") + " (%d)" % totalSearchMatches)
404
+
405
+ for pkg in availablePackages:
406
+ item = self.ctrl.AppendItem(self.rootRemotePackages, pkg)
407
+ self.ctrl.SetItemData(item, PACKAGE_STATUS_NOT_INSTALLED)
408
+
409
+ # Expand all roots
410
+ if rootRemoteWasExpanded:
411
+ self.ctrl.Expand(self.rootRemotePackages)
412
+ if rootUserWasExpanded:
413
+ self.ctrl.Expand(self.rootUserPackages)
414
+ if rootSystemWasExpanded:
415
+ self.ctrl.Expand(self.rootSystemPackages)
289
416
 
290
417
  def onAddFromFile(self, evt=None):
291
418
  # Create dialog to get package file location
@@ -342,8 +469,8 @@ class PackageDetailsPanel(wx.Panel):
342
469
  self.installBtn.Bind(wx.EVT_BUTTON, self.onInstall)
343
470
  # Version chooser
344
471
  self.versionCtrl = wx.Choice(self)
472
+ # self.versionCtrl.Bind(wx.EVT_CHOICE, self.onVersionSelection)
345
473
  self.headBtnSzr.Add(self.versionCtrl, border=3, flag=wx.RIGHT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER)
346
- self.versionCtrl.Bind(wx.EVT_CHOICE, self.onVersion)
347
474
  # Uninstall button
348
475
  self.uninstallBtn = wx.Button(self, label=_translate("Uninstall"))
349
476
  self.headBtnSzr.AddStretchSpacer(1)
@@ -385,6 +512,7 @@ class PackageDetailsPanel(wx.Panel):
385
512
  metadata = getPackageMetadata(self._package)
386
513
  else:
387
514
  metadata = {}
515
+
388
516
  # Get best data available, prioritising local metadata
389
517
  self.params = {
390
518
  'name': metadata.get('Name', pypiData.get('Name', pipname)),
@@ -413,7 +541,7 @@ class PackageDetailsPanel(wx.Panel):
413
541
  self.versionCtrl.Clear()
414
542
  self.versionCtrl.AppendItems(self.params['releases'])
415
543
  if self.params['version'] is None:
416
- self.versionCtrl.SetSelection(0)
544
+ self.versionCtrl.SetSelection(self.versionCtrl.GetCount() - 1)
417
545
  else:
418
546
  if self.params['version'] not in self.versionCtrl.GetStrings():
419
547
  self.versionCtrl.Append(self.params['version'])
@@ -423,31 +551,43 @@ class PackageDetailsPanel(wx.Panel):
423
551
  self.Layout()
424
552
  self._applyAppTheme()
425
553
 
554
+ # def onVersionSelection(self, evt=None):
555
+ # # if the version matches the installed version, disable the install button
556
+ # self.refresh()
557
+
426
558
  def refresh(self, evt=None):
427
- state, version = getInstallState(self.package)
559
+ # check if the package is installed
560
+ installedPackages = dict(getInstalledPackages())
561
+ systemPackages = installedPackages['system']['packages']
562
+ userPackages = installedPackages['user']['packages']
563
+ availablePackages = getRemotePackages()['packages']
428
564
 
429
- if state == "u":
565
+ pkgName = self.package
566
+ state = False
567
+ if pkgName in userPackages:
430
568
  # If installed to the user space, can be uninstalled or changed
431
569
  self.uninstallBtn.Enable()
432
570
  self.versionCtrl.Enable()
571
+ self.versionCtrl.SetStringSelection(userPackages[pkgName])
433
572
  self.installBtn.Enable(
434
- self.versionCtrl.GetStringSelection() != version
573
+ self.versionCtrl.GetStringSelection() != userPackages[pkgName]
435
574
  )
436
- elif state == "s":
437
- # If installed to the system, can't be uninstalled or changed
438
- self.uninstallBtn.Disable()
439
- self.versionCtrl.Disable()
440
- self.installBtn.Disable()
441
- elif state == "n":
442
- # If uninstalled, can only be installed
575
+ state = True
576
+ elif pkgName in availablePackages:
577
+ # If available but not installed, can be installed
443
578
  self.uninstallBtn.Disable()
444
579
  self.versionCtrl.Enable()
445
580
  self.installBtn.Enable()
581
+ self.versionCtrl.SetSelection(self.versionCtrl.GetCount() - 1) # newest
582
+ state = True
446
583
  else:
447
- # If None, disable everything
584
+ # If installed to the system, can't be uninstalled or changed
585
+ if pkgName in systemPackages:
586
+ self.versionCtrl.SetStringSelection(systemPackages[pkgName])
448
587
  self.uninstallBtn.Disable()
449
588
  self.versionCtrl.Disable()
450
589
  self.installBtn.Disable()
590
+
451
591
  # Disable all controls if we have None
452
592
  self.homeBtn.Enable(state is not None)
453
593
  self.nameCtrl.Enable(state is not None)