]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/ListCtrl.py
fixes for unicode build, return values from event handlers (via
[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
RD
100 style=wx.LC_REPORT
101 | wx.SUNKEN_BORDER
102 | wx.LC_EDIT_LABELS
1e4a197e
RD
103 #| wxLC_NO_HEADER
104 #| wxLC_VRULES | wxLC_HRULES
105 )
8fa876ca
RD
106
107 self.list.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
cf694132 108
3979290c
RD
109 self.PopulateList()
110
111 # Now that the list exists we can init the other base class,
112 # see wxPython/lib/mixins/listctrl.py
113 self.itemDataMap = musicdata
d4b73b1b 114 listmix.ColumnSorterMixin.__init__(self, 3)
1e4a197e 115 #self.SortListItems(0, True)
3979290c 116
8fa876ca
RD
117 self.Bind(wx.EVT_SIZE, self.OnSize)
118
119 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
120 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.list)
121 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
122 self.Bind(wx.EVT_LIST_DELETE_ITEM, self.OnItemDelete, self.list)
123 self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
124 self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColRightClick, self.list)
125 self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.OnColBeginDrag, self.list)
126 self.Bind(wx.EVT_LIST_COL_DRAGGING, self.OnColDragging, self.list)
127 self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnColEndDrag, self.list)
128 self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
129
130 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
131 self.list.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
3979290c
RD
132
133 # for wxMSW
8fa876ca 134 self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightClick)
3979290c
RD
135
136 # for wxGTK
8fa876ca 137 self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightClick)
3979290c 138
cf694132 139
3979290c 140 def PopulateList(self):
6d19860f 141 if 0:
726fc00a 142 # for normal, simple columns, you can add them like this:
6d19860f 143 self.list.InsertColumn(0, "Artist")
8fa876ca 144 self.list.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
6d19860f
RD
145 self.list.InsertColumn(2, "Genre")
146 else:
147 # but since we want images on the column header we have to do it the hard way:
8fa876ca
RD
148 info = wx.ListItem()
149 info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
6d19860f
RD
150 info.m_image = -1
151 info.m_format = 0
152 info.m_text = "Artist"
153 self.list.InsertColumnInfo(0, info)
154
8fa876ca 155 info.m_format = wx.LIST_FORMAT_RIGHT
6d19860f
RD
156 info.m_text = "Title"
157 self.list.InsertColumnInfo(1, info)
158
159 info.m_format = 0
160 info.m_text = "Genre"
161 self.list.InsertColumnInfo(2, info)
162
dcd38683
RD
163 items = musicdata.items()
164 for x in range(len(items)):
165 key, data = items[x]
3979290c 166 self.list.InsertImageStringItem(x, data[0], self.idx1)
dcd38683
RD
167 self.list.SetStringItem(x, 1, data[1])
168 self.list.SetStringItem(x, 2, data[2])
169 self.list.SetItemData(x, key)
cf694132 170
8fa876ca
RD
171 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
172 self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
0e947004 173 self.list.SetColumnWidth(2, 100)
cf694132 174
6d19860f 175 # show how to select an item
8fa876ca 176 self.list.SetItemState(5, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
c368d904 177
6d19860f 178 # show how to change the colour of a couple items
c368d904 179 item = self.list.GetItem(1)
8fa876ca 180 item.SetTextColour(wx.BLUE)
c368d904 181 self.list.SetItem(item)
c7e7022c 182 item = self.list.GetItem(4)
8fa876ca 183 item.SetTextColour(wx.RED)
c7e7022c
RD
184 self.list.SetItem(item)
185
bb0054cd 186 self.currentItem = 0
64be6958 187
a08cbc01 188
d4b73b1b 189 # Used by the ColumnSorterMixin, see wxPython/lib/mixins/listctrl.py
6d19860f
RD
190 def GetListCtrl(self):
191 return self.list
192
d4b73b1b 193 # Used by the ColumnSorterMixin, see wxPython/lib/mixins/listctrl.py
6d19860f
RD
194 def GetSortImages(self):
195 return (self.sm_dn, self.sm_up)
196
197
a08cbc01
RD
198 def OnRightDown(self, event):
199 self.x = event.GetX()
64be6958
RD
200 self.y = event.GetY()
201 self.log.WriteText("x, y = %s\n" % str((self.x, self.y)))
1e4a197e 202 item, flags = self.list.HitTest((self.x, self.y))
8fa876ca
RD
203
204 if flags & wx.LIST_HITTEST_ONITEM:
1e4a197e 205 self.list.Select(item)
8fa876ca 206
a08cbc01 207 event.Skip()
bb0054cd 208
185d7c3e
RD
209
210 def getColumnText(self, index, col):
211 item = self.list.GetItem(index, col)
212 return item.GetText()
213
214
bb0054cd 215 def OnItemSelected(self, event):
1e4a197e 216 ##print event.GetItem().GetTextColour()
bb0054cd 217 self.currentItem = event.m_itemIndex
9416aa89
RD
218 self.log.WriteText("OnItemSelected: %s, %s, %s, %s\n" %
219 (self.currentItem,
220 self.list.GetItemText(self.currentItem),
185d7c3e
RD
221 self.getColumnText(self.currentItem, 1),
222 self.getColumnText(self.currentItem, 2)))
8fa876ca 223
9416aa89
RD
224 if self.currentItem == 10:
225 self.log.WriteText("OnItemSelected: Veto'd selection\n")
226 #event.Veto() # doesn't work
227 # this does
8fa876ca
RD
228 self.list.SetItemState(10, 0, wx.LIST_STATE_SELECTED)
229
1fded56b
RD
230 event.Skip()
231
185d7c3e 232
9c2abc2c
RD
233 def OnItemDeselected(self, evt):
234 item = evt.GetItem()
1e4a197e
RD
235 self.log.WriteText("OnItemDeselected: %d" % evt.m_itemIndex)
236
237 # Show how to reselect something we don't want deselected
9c2abc2c 238 if evt.m_itemIndex == 11:
8fa876ca 239 wx.CallAfter(self.list.SetItemState, 11, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
9c2abc2c
RD
240
241
c368d904
RD
242 def OnItemActivated(self, event):
243 self.currentItem = event.m_itemIndex
1e4a197e
RD
244 self.log.WriteText("OnItemActivated: %s\nTopItem: %s" %
245 (self.list.GetItemText(self.currentItem), self.list.GetTopItem()))
c368d904 246
1fded56b
RD
247 def OnBeginEdit(self, event):
248 self.log.WriteText("OnBeginEdit")
249 event.Allow()
250
8bf5d46e
RD
251 def OnItemDelete(self, event):
252 self.log.WriteText("OnItemDelete\n")
253
dcd38683 254 def OnColClick(self, event):
6d19860f 255 self.log.WriteText("OnColClick: %d\n" % event.GetColumn())
b881fc78 256 event.Skip()
6d19860f
RD
257
258 def OnColRightClick(self, event):
3115ef3e
RD
259 item = self.list.GetColumn(event.GetColumn())
260 self.log.WriteText("OnColRightClick: %d %s\n" %
261 (event.GetColumn(), (item.GetText(), item.GetAlign(),
262 item.GetWidth(), item.GetImage())))
dcd38683 263
6d19860f
RD
264 def OnColBeginDrag(self, event):
265 self.log.WriteText("OnColBeginDrag\n")
1192dbd4
RD
266 ## Show how to not allow a column to be resized
267 #if event.GetColumn() == 0:
268 # event.Veto()
269
c7e7022c 270
6d19860f
RD
271 def OnColDragging(self, event):
272 self.log.WriteText("OnColDragging\n")
dcd38683 273
6d19860f
RD
274 def OnColEndDrag(self, event):
275 self.log.WriteText("OnColEndDrag\n")
8bf5d46e 276
bb0054cd
RD
277 def OnDoubleClick(self, event):
278 self.log.WriteText("OnDoubleClick item %s\n" % self.list.GetItemText(self.currentItem))
185d7c3e 279 event.Skip()
a08cbc01 280
bb0054cd
RD
281 def OnRightClick(self, event):
282 self.log.WriteText("OnRightClick %s\n" % self.list.GetItemText(self.currentItem))
cce80896 283
1fded56b 284 # only do this part the first time so the events are only bound once
1e4a197e 285 if not hasattr(self, "popupID1"):
8fa876ca
RD
286 self.popupID1 = wx.NewId()
287 self.popupID2 = wx.NewId()
288 self.popupID3 = wx.NewId()
289 self.popupID4 = wx.NewId()
290 self.popupID5 = wx.NewId()
291 self.popupID6 = wx.NewId()
292
293 self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)
294 self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2)
295 self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3)
296 self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4)
297 self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5)
298 self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6)
1e4a197e
RD
299
300 # make a menu
8fa876ca 301 menu = wx.Menu()
1fded56b
RD
302 # add some items
303 menu.Append(self.popupID1, "FindItem tests")
62fc0020 304 menu.Append(self.popupID2, "Iterate Selected")
1e4a197e
RD
305 menu.Append(self.popupID3, "ClearAll and repopulate")
306 menu.Append(self.popupID4, "DeleteAllItems")
307 menu.Append(self.popupID5, "GetItem")
1fded56b 308 menu.Append(self.popupID6, "Edit")
1e4a197e
RD
309
310 # Popup the menu. If an item is selected then its handler
311 # will be called before PopupMenu returns.
8fa876ca 312 self.PopupMenu(menu, (self.x, self.y))
e166644c 313 menu.Destroy()
1e4a197e 314
a08cbc01
RD
315
316 def OnPopupOne(self, event):
317 self.log.WriteText("Popup one\n")
3b5ccda1
RD
318 print "FindItem:", self.list.FindItem(-1, "Roxette")
319 print "FindItemData:", self.list.FindItemData(-1, 11)
a08cbc01
RD
320
321 def OnPopupTwo(self, event):
62fc0020
RD
322 self.log.WriteText("Selected items:\n")
323 index = self.list.GetFirstSelected()
8fa876ca 324
62fc0020
RD
325 while index != -1:
326 self.log.WriteText(" %s: %s\n" % (self.list.GetItemText(index), self.getColumnText(index, 1)))
327 index = self.list.GetNextSelected(index)
328
a08cbc01
RD
329 def OnPopupThree(self, event):
330 self.log.WriteText("Popup three\n")
3979290c 331 self.list.ClearAll()
8fa876ca 332 wx.CallAfter(self.PopulateList)
cf694132 333
8bf5d46e
RD
334 def OnPopupFour(self, event):
335 self.list.DeleteAllItems()
336
e166644c
RD
337 def OnPopupFive(self, event):
338 item = self.list.GetItem(self.currentItem)
339 print item.m_text, item.m_itemId, self.list.GetItemData(self.currentItem)
340
1fded56b
RD
341 def OnPopupSix(self, event):
342 self.list.EditLabel(self.currentItem)
343
344
cf694132
RD
345 def OnSize(self, event):
346 w,h = self.GetClientSizeTuple()
347 self.list.SetDimensions(0, 0, w, h)
348
349
350
cf694132
RD
351#---------------------------------------------------------------------------
352
353def runTest(frame, nb, log):
354 win = TestListCtrlPanel(nb, log)
355 return win
356
357#---------------------------------------------------------------------------
358
359
8fa876ca
RD
360overview = """\
361<html>
362<body>
363A list control presents lists in a number of formats: list view, report view,
364icon view and small icon view. In any case, elements are numbered from zero.
365For all these modes (but not for virtual list controls), the items are stored
366in the control and must be added to it using InsertItem method.
cf694132 367
8fa876ca
RD
368<p>To intercept events from a list control, use the event table macros described in
369<code>wxListEvent.</code>
cf694132 370
8fa876ca
RD
371<h3>Mix-ins</h3>
372This example demonstrates how to use mixins. The following mixins are available.
cf694132 373
8fa876ca
RD
374<h4>ColumnSorterMixin</h4>
375
376<code><b>ColumnSorterMixin(numColumns)</b></code>
377
378<p>A mixin class that handles sorting of a wxListCtrl in REPORT mode when the column
379header is clicked on.
380
381<p>There are a few requirments needed in order for this to work genericly:
382<p><ol>
383 <li>The combined class must have a <code>GetListCtrl</code> method that returns
384 the ListCtrl to be sorted, and the list control must exist at the time the
385 <code>ColumnSorterMixin.__init__()</code>method is called because it uses
386 <code>GetListCtrl</code>.
387
388 <li>Items in the list control must have a unique data value set with
389 <code>list.SetItemData</code>.
390
391 <li>The combined class must have an attribute named <code>itemDataMap</code>
392 that is a dictionary mapping the data values to a sequence of objects
393 representing the values in each column. These valuesare compared in
394 the column sorter to determine sort order.
395</ol>
396
397<p>Interesting methods to override are <code>GetColumnSorter</code>,
398<code>GetSecondarySortValues</code>, and <code>GetSortImages</code>.
cf694132 399
8fa876ca
RD
400<h5>Methods</h5>
401<dl>
402<dt><code>SetColumnCount(newNumColumns)</code>
403<dd>Informs the mixin as to the number of columns in the control. When it is
404set, it also sets up an event handler for <code>EVT_LIST_COL_CLICK</code> events.
cf694132 405
8fa876ca
RD
406<dt><code>SortListItems(col=-1, ascending=1)</code>
407<dd>Sort the list on demand. Can also be used to set the sort column and order.
cf694132 408
8fa876ca
RD
409<dt><code>GetColumnWidths()</code>
410<dd>Returns a list of column widths. Can be used to help restore the current
411view later.
412
413<dt><code>GetSortImages()</code>
414<dd>Returns a tuple of image list indexes the indexes in the image list for an
415image to be put on the column header when sorting in descending order
416
417<dt><code>GetColumnSorter()</code>
418<dd>Returns a callable object to be used for comparing column values when sorting.
419
420<dt><code>GetSecondarySortValues(col, key1, key2)</code>
421<dd>Returns a tuple of 2 values to use for secondary sort values when the
422items in the selected column match equal. The default just returns the
423item data values.
424
425</dl>
426
427<h4>ListCtrlAutoWidthMixin</h4>
428
d4b73b1b 429<code><b>ListCtrlAutoWidthMixin()</b></code>
8fa876ca
RD
430
431<p>A mix-in class that automatically resizes the last column to take up the
432remaining width of the ListCtrl.
433
434<p>This causes the ListCtrl to automatically take up the full width of the list,
435without either a horizontal scroll bar (unless absolutely necessary) or empty
436space to the right of the last column.
437
438<p><b>NOTE:</b> This only works for report-style lists.
439
440<p><b>WARNING:</b> If you override the <code>EVT_SIZE</code> event in your ListCtrl,
441make sure you call event.Skip() to ensure that the mixin's _OnResize method is
442called.
443
444<p>This mix-in class was written by <a href='mailto:ewestra@wave.co.nz'>Erik Westra </a>
445
446<h5>Methods</h5>
447<dl>
448
449<dt><code>resizeLastColumn(minWidth)</code>
450<dd>Resize the last column appropriately. If the list's columns are too wide to
451fit within the window, we use a horizontal scrollbar. Otherwise, we expand the
452right-most column to take up the remaining free space in the list. This method is
453called automatically when the ListCtrl is resized; you can also call it yourself
454whenever you want the last column to be resized appropriately (eg, when adding,
455removing or resizing columns). 'minWidth' is the preferred minimum width for
456the last column.
457
458</dl>
459
460
461<h4>ListCtrlSelectionManagerMix</h4>
462
463<code><b>ListCtrlSelectionManagerMix()</b></code>
464
465<p>Mixin that defines a platform independent selection policy
466
467<p>As selection single and multi-select list return the item index or a
468list of item indexes respectively.
469
470<h5>Methods</h5>
471<dl>
472
473<dt><code>getPopupMenu()</code>
474<dd>Override to implement dynamic menus (create)
475
476<dt><code>setPopupMenu(menu)</code>
477<dd>Must be set for default behaviour.
478
479<dt><code>afterPopupMenu()</code>
480<dd>Override to implement dynamic menus (destroy).
481
482<dt><code>getSelection()</code>
483<dd>Returns the current selection (or selections as a Python list if extended
484selection is enabled)
485
486
487</body>
488</html>
489"""
cf694132
RD
490
491
3115ef3e
RD
492if __name__ == '__main__':
493 import sys,os
494 import run
8eca4fef 495 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
cf694132 496