]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/ListCtrl.py
Added new MaskedEditControl code from Will Sadkin. The modules are
[wxWidgets.git] / wxPython / demo / ListCtrl.py
CommitLineData
cf694132
RD
1#----------------------------------------------------------------------------
2# Name: ListCtrl.py
3# Purpose: Testing lots of stuff, controls, window types, etc.
4#
5# Author: Robin Dunn & Gary Dumer
6#
7# Created:
8# RCS-ID: $Id$
9# Copyright: (c) 1998 by Total Control Software
10# Licence: wxWindows license
11#----------------------------------------------------------------------------
8fa876ca
RD
12
13import wx
14import wx.lib.mixins.listctrl as listmix
cf694132 15
8fa876ca 16import images
cf694132
RD
17
18#---------------------------------------------------------------------------
19
dcd38683
RD
20musicdata = {
211 : ("Bad English", "The Price Of Love", "Rock"),
222 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
233 : ("George Michael", "Praying For Time", "Rock"),
244 : ("Gloria Estefan", "Here We Are", "Rock"),
255 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
266 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
cd096152
RD
277 : ("Paul Young", "Oh Girl", "Rock"),
288 : ("Paula Abdul", "Opposites Attract", "Rock"),
299 : ("Richard Marx", "Should've Known Better", "Rock"),
3010: ("Rod Stewart", "Forever Young", "Rock"),
3111: ("Roxette", "Dangerous", "Rock"),
3212: ("Sheena Easton", "The Lover In Me", "Rock"),
3313: ("Sinead O'Connor", "Nothing Compares 2 U", "Rock"),
3414: ("Stevie B.", "Because I Love You", "Rock"),
3515: ("Taylor Dayne", "Love Will Lead You Back", "Rock"),
3616: ("The Bangles", "Eternal Flame", "Rock"),
3717: ("Wilson Phillips", "Release Me", "Rock"),
3818: ("Billy Joel", "Blonde Over Blue", "Rock"),
3919: ("Billy Joel", "Famous Last Words", "Rock"),
4020: ("Billy Joel", "Lullabye (Goodnight, My Angel)", "Rock"),
4121: ("Billy Joel", "The River Of Dreams", "Rock"),
4222: ("Billy Joel", "Two Thousand Years", "Rock"),
4323: ("Janet Jackson", "Alright", "Rock"),
4424: ("Janet Jackson", "Black Cat", "Rock"),
4525: ("Janet Jackson", "Come Back To Me", "Rock"),
4626: ("Janet Jackson", "Escapade", "Rock"),
4727: ("Janet Jackson", "Love Will Never Do (Without You)", "Rock"),
4828: ("Janet Jackson", "Miss You Much", "Rock"),
4929: ("Janet Jackson", "Rhythm Nation", "Rock"),
5030: ("Janet Jackson", "State Of The World", "Rock"),
5131: ("Janet Jackson", "The Knowledge", "Rock"),
5232: ("Spyro Gyra", "End of Romanticism", "Jazz"),
5333: ("Spyro Gyra", "Heliopolis", "Jazz"),
5434: ("Spyro Gyra", "Jubilee", "Jazz"),
5535: ("Spyro Gyra", "Little Linda", "Jazz"),
5636: ("Spyro Gyra", "Morning Dance", "Jazz"),
5737: ("Spyro Gyra", "Song for Lorraine", "Jazz"),
5838: ("Yes", "Owner Of A Lonely Heart", "Rock"),
5939: ("Yes", "Rhythm Of Love", "Rock"),
b1cfebd9
RD
6040: ("Cusco", "Dream Catcher", "New Age"),
6141: ("Cusco", "Geronimos Laughter", "New Age"),
6242: ("Cusco", "Ghost Dance", "New Age"),
6343: ("Blue Man Group", "Drumbone", "New Age"),
6444: ("Blue Man Group", "Endless Column", "New Age"),
6545: ("Blue Man Group", "Klein Mandelbrot", "New Age"),
6646: ("Kenny G", "Silhouette", "Jazz"),
6747: ("Sade", "Smooth Operator", "Jazz"),
6848: ("David Arkenstone", "Papillon (On The Wings Of The Butterfly)", "New Age"),
6949: ("David Arkenstone", "Stepping Stars", "New Age"),
7050: ("David Arkenstone", "Carnation Lily Lily Rose", "New Age"),
7151: ("David Lanz", "Behind The Waterfall", "New Age"),
7252: ("David Lanz", "Cristofori's Dream", "New Age"),
7353: ("David Lanz", "Heartsounds", "New Age"),
7454: ("David Lanz", "Leaves on the Seine", "New Age"),
dcd38683
RD
75}
76
8fa876ca 77#---------------------------------------------------------------------------
726fc00a 78
d4b73b1b 79class TestListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
8fa876ca
RD
80 def __init__(self, parent, ID, pos=wx.DefaultPosition,
81 size=wx.DefaultSize, style=0):
82 wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
d4b73b1b 83 listmix.ListCtrlAutoWidthMixin.__init__(self)
726fc00a
RD
84
85
d4b73b1b 86class TestListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
cf694132 87 def __init__(self, parent, log):
8fa876ca 88 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
cf694132
RD
89
90 self.log = log
8fa876ca 91 tID = wx.NewId()
cf694132 92
8fa876ca 93 self.il = wx.ImageList(16, 16)
6d19860f 94
3979290c 95 self.idx1 = self.il.Add(images.getSmilesBitmap())
00887b39
RD
96 self.sm_up = self.il.Add(images.getSmallUpArrowBitmap())
97 self.sm_dn = self.il.Add(images.getSmallDnArrowBitmap())
98
726fc00a 99 self.list = TestListCtrl(self, tID,
8fa876ca 100 style=wx.LC_REPORT
00e4ce13
RD
101 #| wx.BORDER_SUNKEN
102 | wx.BORDER_NONE
8fa876ca 103 | wx.LC_EDIT_LABELS
1e4a197e
RD
104 #| wxLC_NO_HEADER
105 #| wxLC_VRULES | wxLC_HRULES
106 )
8fa876ca
RD
107
108 self.list.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
cf694132 109
3979290c
RD
110 self.PopulateList()
111
112 # Now that the list exists we can init the other base class,
113 # see wxPython/lib/mixins/listctrl.py
114 self.itemDataMap = musicdata
d4b73b1b 115 listmix.ColumnSorterMixin.__init__(self, 3)
1e4a197e 116 #self.SortListItems(0, True)
3979290c 117
8fa876ca
RD
118 self.Bind(wx.EVT_SIZE, self.OnSize)
119
120 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
121 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.list)
122 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
123 self.Bind(wx.EVT_LIST_DELETE_ITEM, self.OnItemDelete, self.list)
124 self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
125 self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColRightClick, self.list)
126 self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.OnColBeginDrag, self.list)
127 self.Bind(wx.EVT_LIST_COL_DRAGGING, self.OnColDragging, self.list)
128 self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnColEndDrag, self.list)
129 self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
130
131 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
132 self.list.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
3979290c
RD
133
134 # for wxMSW
8fa876ca 135 self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightClick)
3979290c
RD
136
137 # for wxGTK
8fa876ca 138 self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightClick)
3979290c 139
cf694132 140
3979290c 141 def PopulateList(self):
6d19860f 142 if 0:
726fc00a 143 # for normal, simple columns, you can add them like this:
6d19860f 144 self.list.InsertColumn(0, "Artist")
8fa876ca 145 self.list.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
6d19860f
RD
146 self.list.InsertColumn(2, "Genre")
147 else:
148 # but since we want images on the column header we have to do it the hard way:
8fa876ca
RD
149 info = wx.ListItem()
150 info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
6d19860f
RD
151 info.m_image = -1
152 info.m_format = 0
153 info.m_text = "Artist"
154 self.list.InsertColumnInfo(0, info)
155
8fa876ca 156 info.m_format = wx.LIST_FORMAT_RIGHT
6d19860f
RD
157 info.m_text = "Title"
158 self.list.InsertColumnInfo(1, info)
159
160 info.m_format = 0
161 info.m_text = "Genre"
162 self.list.InsertColumnInfo(2, info)
163
dcd38683
RD
164 items = musicdata.items()
165 for x in range(len(items)):
166 key, data = items[x]
3979290c 167 self.list.InsertImageStringItem(x, data[0], self.idx1)
dcd38683
RD
168 self.list.SetStringItem(x, 1, data[1])
169 self.list.SetStringItem(x, 2, data[2])
170 self.list.SetItemData(x, key)
cf694132 171
8fa876ca
RD
172 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
173 self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
0e947004 174 self.list.SetColumnWidth(2, 100)
cf694132 175
6d19860f 176 # show how to select an item
8fa876ca 177 self.list.SetItemState(5, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
c368d904 178
6d19860f 179 # show how to change the colour of a couple items
c368d904 180 item = self.list.GetItem(1)
8fa876ca 181 item.SetTextColour(wx.BLUE)
c368d904 182 self.list.SetItem(item)
c7e7022c 183 item = self.list.GetItem(4)
8fa876ca 184 item.SetTextColour(wx.RED)
c7e7022c
RD
185 self.list.SetItem(item)
186
bb0054cd 187 self.currentItem = 0
64be6958 188
a08cbc01 189
d4b73b1b 190 # Used by the ColumnSorterMixin, see wxPython/lib/mixins/listctrl.py
6d19860f
RD
191 def GetListCtrl(self):
192 return self.list
193
d4b73b1b 194 # Used by the ColumnSorterMixin, see wxPython/lib/mixins/listctrl.py
6d19860f
RD
195 def GetSortImages(self):
196 return (self.sm_dn, self.sm_up)
197
198
a08cbc01
RD
199 def OnRightDown(self, event):
200 self.x = event.GetX()
64be6958
RD
201 self.y = event.GetY()
202 self.log.WriteText("x, y = %s\n" % str((self.x, self.y)))
1e4a197e 203 item, flags = self.list.HitTest((self.x, self.y))
8fa876ca
RD
204
205 if flags & wx.LIST_HITTEST_ONITEM:
1e4a197e 206 self.list.Select(item)
8fa876ca 207
a08cbc01 208 event.Skip()
bb0054cd 209
185d7c3e
RD
210
211 def getColumnText(self, index, col):
212 item = self.list.GetItem(index, col)
213 return item.GetText()
214
215
bb0054cd 216 def OnItemSelected(self, event):
1e4a197e 217 ##print event.GetItem().GetTextColour()
bb0054cd 218 self.currentItem = event.m_itemIndex
9416aa89
RD
219 self.log.WriteText("OnItemSelected: %s, %s, %s, %s\n" %
220 (self.currentItem,
221 self.list.GetItemText(self.currentItem),
185d7c3e
RD
222 self.getColumnText(self.currentItem, 1),
223 self.getColumnText(self.currentItem, 2)))
8fa876ca 224
9416aa89
RD
225 if self.currentItem == 10:
226 self.log.WriteText("OnItemSelected: Veto'd selection\n")
227 #event.Veto() # doesn't work
228 # this does
8fa876ca
RD
229 self.list.SetItemState(10, 0, wx.LIST_STATE_SELECTED)
230
1fded56b
RD
231 event.Skip()
232
185d7c3e 233
9c2abc2c
RD
234 def OnItemDeselected(self, evt):
235 item = evt.GetItem()
1e4a197e
RD
236 self.log.WriteText("OnItemDeselected: %d" % evt.m_itemIndex)
237
238 # Show how to reselect something we don't want deselected
9c2abc2c 239 if evt.m_itemIndex == 11:
8fa876ca 240 wx.CallAfter(self.list.SetItemState, 11, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
9c2abc2c
RD
241
242
c368d904
RD
243 def OnItemActivated(self, event):
244 self.currentItem = event.m_itemIndex
1e4a197e
RD
245 self.log.WriteText("OnItemActivated: %s\nTopItem: %s" %
246 (self.list.GetItemText(self.currentItem), self.list.GetTopItem()))
c368d904 247
1fded56b
RD
248 def OnBeginEdit(self, event):
249 self.log.WriteText("OnBeginEdit")
250 event.Allow()
251
8bf5d46e
RD
252 def OnItemDelete(self, event):
253 self.log.WriteText("OnItemDelete\n")
254
dcd38683 255 def OnColClick(self, event):
6d19860f 256 self.log.WriteText("OnColClick: %d\n" % event.GetColumn())
b881fc78 257 event.Skip()
6d19860f
RD
258
259 def OnColRightClick(self, event):
3115ef3e
RD
260 item = self.list.GetColumn(event.GetColumn())
261 self.log.WriteText("OnColRightClick: %d %s\n" %
262 (event.GetColumn(), (item.GetText(), item.GetAlign(),
263 item.GetWidth(), item.GetImage())))
dcd38683 264
6d19860f
RD
265 def OnColBeginDrag(self, event):
266 self.log.WriteText("OnColBeginDrag\n")
1192dbd4
RD
267 ## Show how to not allow a column to be resized
268 #if event.GetColumn() == 0:
269 # event.Veto()
270
c7e7022c 271
6d19860f
RD
272 def OnColDragging(self, event):
273 self.log.WriteText("OnColDragging\n")
dcd38683 274
6d19860f
RD
275 def OnColEndDrag(self, event):
276 self.log.WriteText("OnColEndDrag\n")
8bf5d46e 277
bb0054cd
RD
278 def OnDoubleClick(self, event):
279 self.log.WriteText("OnDoubleClick item %s\n" % self.list.GetItemText(self.currentItem))
185d7c3e 280 event.Skip()
a08cbc01 281
bb0054cd
RD
282 def OnRightClick(self, event):
283 self.log.WriteText("OnRightClick %s\n" % self.list.GetItemText(self.currentItem))
cce80896 284
1fded56b 285 # only do this part the first time so the events are only bound once
1e4a197e 286 if not hasattr(self, "popupID1"):
8fa876ca
RD
287 self.popupID1 = wx.NewId()
288 self.popupID2 = wx.NewId()
289 self.popupID3 = wx.NewId()
290 self.popupID4 = wx.NewId()
291 self.popupID5 = wx.NewId()
292 self.popupID6 = wx.NewId()
293
294 self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)
295 self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2)
296 self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3)
297 self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4)
298 self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5)
299 self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6)
1e4a197e
RD
300
301 # make a menu
8fa876ca 302 menu = wx.Menu()
1fded56b
RD
303 # add some items
304 menu.Append(self.popupID1, "FindItem tests")
62fc0020 305 menu.Append(self.popupID2, "Iterate Selected")
1e4a197e
RD
306 menu.Append(self.popupID3, "ClearAll and repopulate")
307 menu.Append(self.popupID4, "DeleteAllItems")
308 menu.Append(self.popupID5, "GetItem")
1fded56b 309 menu.Append(self.popupID6, "Edit")
1e4a197e
RD
310
311 # Popup the menu. If an item is selected then its handler
312 # will be called before PopupMenu returns.
8fa876ca 313 self.PopupMenu(menu, (self.x, self.y))
e166644c 314 menu.Destroy()
1e4a197e 315
a08cbc01
RD
316
317 def OnPopupOne(self, event):
318 self.log.WriteText("Popup one\n")
3b5ccda1
RD
319 print "FindItem:", self.list.FindItem(-1, "Roxette")
320 print "FindItemData:", self.list.FindItemData(-1, 11)
a08cbc01
RD
321
322 def OnPopupTwo(self, event):
62fc0020
RD
323 self.log.WriteText("Selected items:\n")
324 index = self.list.GetFirstSelected()
8fa876ca 325
62fc0020
RD
326 while index != -1:
327 self.log.WriteText(" %s: %s\n" % (self.list.GetItemText(index), self.getColumnText(index, 1)))
328 index = self.list.GetNextSelected(index)
329
a08cbc01
RD
330 def OnPopupThree(self, event):
331 self.log.WriteText("Popup three\n")
3979290c 332 self.list.ClearAll()
8fa876ca 333 wx.CallAfter(self.PopulateList)
cf694132 334
8bf5d46e
RD
335 def OnPopupFour(self, event):
336 self.list.DeleteAllItems()
337
e166644c
RD
338 def OnPopupFive(self, event):
339 item = self.list.GetItem(self.currentItem)
340 print item.m_text, item.m_itemId, self.list.GetItemData(self.currentItem)
341
1fded56b
RD
342 def OnPopupSix(self, event):
343 self.list.EditLabel(self.currentItem)
344
345
cf694132
RD
346 def OnSize(self, event):
347 w,h = self.GetClientSizeTuple()
348 self.list.SetDimensions(0, 0, w, h)
349
350
351
cf694132
RD
352#---------------------------------------------------------------------------
353
354def runTest(frame, nb, log):
355 win = TestListCtrlPanel(nb, log)
356 return win
357
358#---------------------------------------------------------------------------
359
360
8fa876ca
RD
361overview = """\
362<html>
363<body>
364A list control presents lists in a number of formats: list view, report view,
365icon view and small icon view. In any case, elements are numbered from zero.
366For all these modes (but not for virtual list controls), the items are stored
367in the control and must be added to it using InsertItem method.
cf694132 368
8fa876ca
RD
369<p>To intercept events from a list control, use the event table macros described in
370<code>wxListEvent.</code>
cf694132 371
8fa876ca
RD
372<h3>Mix-ins</h3>
373This example demonstrates how to use mixins. The following mixins are available.
cf694132 374
8fa876ca
RD
375<h4>ColumnSorterMixin</h4>
376
377<code><b>ColumnSorterMixin(numColumns)</b></code>
378
379<p>A mixin class that handles sorting of a wxListCtrl in REPORT mode when the column
380header is clicked on.
381
382<p>There are a few requirments needed in order for this to work genericly:
383<p><ol>
384 <li>The combined class must have a <code>GetListCtrl</code> method that returns
385 the ListCtrl to be sorted, and the list control must exist at the time the
386 <code>ColumnSorterMixin.__init__()</code>method is called because it uses
387 <code>GetListCtrl</code>.
388
389 <li>Items in the list control must have a unique data value set with
390 <code>list.SetItemData</code>.
391
392 <li>The combined class must have an attribute named <code>itemDataMap</code>
393 that is a dictionary mapping the data values to a sequence of objects
394 representing the values in each column. These valuesare compared in
395 the column sorter to determine sort order.
396</ol>
397
398<p>Interesting methods to override are <code>GetColumnSorter</code>,
399<code>GetSecondarySortValues</code>, and <code>GetSortImages</code>.
cf694132 400
8fa876ca
RD
401<h5>Methods</h5>
402<dl>
403<dt><code>SetColumnCount(newNumColumns)</code>
404<dd>Informs the mixin as to the number of columns in the control. When it is
405set, it also sets up an event handler for <code>EVT_LIST_COL_CLICK</code> events.
cf694132 406
8fa876ca
RD
407<dt><code>SortListItems(col=-1, ascending=1)</code>
408<dd>Sort the list on demand. Can also be used to set the sort column and order.
cf694132 409
8fa876ca
RD
410<dt><code>GetColumnWidths()</code>
411<dd>Returns a list of column widths. Can be used to help restore the current
412view later.
413
414<dt><code>GetSortImages()</code>
415<dd>Returns a tuple of image list indexes the indexes in the image list for an
416image to be put on the column header when sorting in descending order
417
418<dt><code>GetColumnSorter()</code>
419<dd>Returns a callable object to be used for comparing column values when sorting.
420
421<dt><code>GetSecondarySortValues(col, key1, key2)</code>
422<dd>Returns a tuple of 2 values to use for secondary sort values when the
423items in the selected column match equal. The default just returns the
424item data values.
425
426</dl>
427
428<h4>ListCtrlAutoWidthMixin</h4>
429
d4b73b1b 430<code><b>ListCtrlAutoWidthMixin()</b></code>
8fa876ca
RD
431
432<p>A mix-in class that automatically resizes the last column to take up the
433remaining width of the ListCtrl.
434
435<p>This causes the ListCtrl to automatically take up the full width of the list,
436without either a horizontal scroll bar (unless absolutely necessary) or empty
437space to the right of the last column.
438
439<p><b>NOTE:</b> This only works for report-style lists.
440
441<p><b>WARNING:</b> If you override the <code>EVT_SIZE</code> event in your ListCtrl,
442make sure you call event.Skip() to ensure that the mixin's _OnResize method is
443called.
444
445<p>This mix-in class was written by <a href='mailto:ewestra@wave.co.nz'>Erik Westra </a>
446
447<h5>Methods</h5>
448<dl>
449
450<dt><code>resizeLastColumn(minWidth)</code>
451<dd>Resize the last column appropriately. If the list's columns are too wide to
452fit within the window, we use a horizontal scrollbar. Otherwise, we expand the
453right-most column to take up the remaining free space in the list. This method is
454called automatically when the ListCtrl is resized; you can also call it yourself
455whenever you want the last column to be resized appropriately (eg, when adding,
456removing or resizing columns). 'minWidth' is the preferred minimum width for
457the last column.
458
459</dl>
460
461
462<h4>ListCtrlSelectionManagerMix</h4>
463
464<code><b>ListCtrlSelectionManagerMix()</b></code>
465
466<p>Mixin that defines a platform independent selection policy
467
468<p>As selection single and multi-select list return the item index or a
469list of item indexes respectively.
470
471<h5>Methods</h5>
472<dl>
473
474<dt><code>getPopupMenu()</code>
475<dd>Override to implement dynamic menus (create)
476
477<dt><code>setPopupMenu(menu)</code>
478<dd>Must be set for default behaviour.
479
480<dt><code>afterPopupMenu()</code>
481<dd>Override to implement dynamic menus (destroy).
482
483<dt><code>getSelection()</code>
484<dd>Returns the current selection (or selections as a Python list if extended
485selection is enabled)
486
487
488</body>
489</html>
490"""
cf694132
RD
491
492
3115ef3e
RD
493if __name__ == '__main__':
494 import sys,os
495 import run
8eca4fef 496 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
cf694132 497