1 #---------------------------------------------------------------------------- 
   3 # Purpose:      Testing lots of stuff, controls, window types, etc. 
   5 # Author:       Robin Dunn & Gary Dumer 
   9 # Copyright:    (c) 1998 by Total Control Software 
  10 # Licence:      wxWindows license 
  11 #---------------------------------------------------------------------------- 
  15 import  wx
.lib
.mixins
.listctrl  
as  listmix
 
  19 #--------------------------------------------------------------------------- 
  22 1 : ("Bad English", "The Price Of Love", "Rock"), 
  23 2 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"), 
  24 3 : ("George Michael", "Praying For Time", "Rock"), 
  25 4 : ("Gloria Estefan", "Here We Are", "Rock"), 
  26 5 : ("Linda Ronstadt", "Don't Know Much", "Rock"), 
  27 6 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"), 
  28 7 : ("Paul Young", "Oh Girl", "Rock"), 
  29 8 : ("Paula Abdul", "Opposites Attract", "Rock"), 
  30 9 : ("Richard Marx", "Should've Known Better", "Rock"), 
  31 10: ("Rod Stewart", "Forever Young", "Rock"), 
  32 11: ("Roxette", "Dangerous", "Rock"), 
  33 12: ("Sheena Easton", "The Lover In Me", "Rock"), 
  34 13: ("Sinead O'Connor", "Nothing Compares 2 U", "Rock"), 
  35 14: ("Stevie B.", "Because I Love You", "Rock"), 
  36 15: ("Taylor Dayne", "Love Will Lead You Back", "Rock"), 
  37 16: ("The Bangles", "Eternal Flame", "Rock"), 
  38 17: ("Wilson Phillips", "Release Me", "Rock"), 
  39 18: ("Billy Joel", "Blonde Over Blue", "Rock"), 
  40 19: ("Billy Joel", "Famous Last Words", "Rock"), 
  41 20: ("Billy Joel", "Lullabye (Goodnight, My Angel)", "Rock"), 
  42 21: ("Billy Joel", "The River Of Dreams", "Rock"), 
  43 22: ("Billy Joel", "Two Thousand Years", "Rock"), 
  44 23: ("Janet Jackson", "Alright", "Rock"), 
  45 24: ("Janet Jackson", "Black Cat", "Rock"), 
  46 25: ("Janet Jackson", "Come Back To Me", "Rock"), 
  47 26: ("Janet Jackson", "Escapade", "Rock"), 
  48 27: ("Janet Jackson", "Love Will Never Do (Without You)", "Rock"), 
  49 28: ("Janet Jackson", "Miss You Much", "Rock"), 
  50 29: ("Janet Jackson", "Rhythm Nation", "Rock"), 
  51 30: ("Janet Jackson", "State Of The World", "Rock"), 
  52 31: ("Janet Jackson", "The Knowledge", "Rock"), 
  53 32: ("Spyro Gyra", "End of Romanticism", "Jazz"), 
  54 33: ("Spyro Gyra", "Heliopolis", "Jazz"), 
  55 34: ("Spyro Gyra", "Jubilee", "Jazz"), 
  56 35: ("Spyro Gyra", "Little Linda", "Jazz"), 
  57 36: ("Spyro Gyra", "Morning Dance", "Jazz"), 
  58 37: ("Spyro Gyra", "Song for Lorraine", "Jazz"), 
  59 38: ("Yes", "Owner Of A Lonely Heart", "Rock"), 
  60 39: ("Yes", "Rhythm Of Love", "Rock"), 
  61 40: ("Cusco", "Dream Catcher", "New Age"), 
  62 41: ("Cusco", "Geronimos Laughter", "New Age"), 
  63 42: ("Cusco", "Ghost Dance", "New Age"), 
  64 43: ("Blue Man Group", "Drumbone", "New Age"), 
  65 44: ("Blue Man Group", "Endless Column", "New Age"), 
  66 45: ("Blue Man Group", "Klein Mandelbrot", "New Age"), 
  67 46: ("Kenny G", "Silhouette", "Jazz"), 
  68 47: ("Sade", "Smooth Operator", "Jazz"), 
  69 48: ("David Arkenstone", "Papillon (On The Wings Of The Butterfly)", "New Age"), 
  70 49: ("David Arkenstone", "Stepping Stars", "New Age"), 
  71 50: ("David Arkenstone", "Carnation Lily Lily Rose", "New Age"), 
  72 51: ("David Lanz", "Behind The Waterfall", "New Age"), 
  73 52: ("David Lanz", "Cristofori's Dream", "New Age"), 
  74 53: ("David Lanz", "Heartsounds", "New Age"), 
  75 54: ("David Lanz", "Leaves on the Seine", "New Age"), 
  78 #--------------------------------------------------------------------------- 
  80 class TestListCtrl(wx
.ListCtrl
, listmix
.ListCtrlAutoWidthMixin
): 
  81     def __init__(self
, parent
, ID
, pos
=wx
.DefaultPosition
, 
  82                  size
=wx
.DefaultSize
, style
=0): 
  83         wx
.ListCtrl
.__init
__(self
, parent
, ID
, pos
, size
, style
) 
  84         listmix
.ListCtrlAutoWidthMixin
.__init
__(self
) 
  87 class TestListCtrlPanel(wx
.Panel
, listmix
.ColumnSorterMixin
): 
  88     def __init__(self
, parent
, log
): 
  89         wx
.Panel
.__init
__(self
, parent
, -1, style
=wx
.WANTS_CHARS
) 
  94         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
  96         if wx
.Platform 
== "__WXMAC__" and \
 
  97                hasattr(wx
.GetApp().GetTopWindow(), "LoadDemo"): 
  98             self
.useNative 
= wx
.CheckBox(self
, -1, "Use native listctrl") 
  99             self
.useNative
.SetValue(  
 100                 not wx
.SystemOptions
.GetOptionInt("mac.listctrl.always_use_generic") ) 
 101             self
.Bind(wx
.EVT_CHECKBOX
, self
.OnUseNative
, self
.useNative
) 
 102             sizer
.Add(self
.useNative
, 0, wx
.ALL | wx
.ALIGN_RIGHT
, 4) 
 104         self
.il 
= wx
.ImageList(16, 16) 
 106         self
.idx1 
= self
.il
.Add(images
.getSmilesBitmap()) 
 107         self
.sm_up 
= self
.il
.Add(images
.getSmallUpArrowBitmap()) 
 108         self
.sm_dn 
= self
.il
.Add(images
.getSmallDnArrowBitmap()) 
 110         self
.list = TestListCtrl(self
, tID
, 
 115                                  | wx
.LC_SORT_ASCENDING
 
 122         self
.list.SetImageList(self
.il
, wx
.IMAGE_LIST_SMALL
) 
 123         sizer
.Add(self
.list, 1, wx
.EXPAND
) 
 127         # Now that the list exists we can init the other base class, 
 128         # see wx/lib/mixins/listctrl.py 
 129         self
.itemDataMap 
= musicdata
 
 130         listmix
.ColumnSorterMixin
.__init
__(self
, 3) 
 131         #self.SortListItems(0, True) 
 134         self
.SetAutoLayout(True) 
 136         self
.Bind(wx
.EVT_LIST_ITEM_SELECTED
, self
.OnItemSelected
, self
.list) 
 137         self
.Bind(wx
.EVT_LIST_ITEM_DESELECTED
, self
.OnItemDeselected
, self
.list) 
 138         self
.Bind(wx
.EVT_LIST_ITEM_ACTIVATED
, self
.OnItemActivated
, self
.list) 
 139         self
.Bind(wx
.EVT_LIST_DELETE_ITEM
, self
.OnItemDelete
, self
.list) 
 140         self
.Bind(wx
.EVT_LIST_COL_CLICK
, self
.OnColClick
, self
.list) 
 141         self
.Bind(wx
.EVT_LIST_COL_RIGHT_CLICK
, self
.OnColRightClick
, self
.list) 
 142         self
.Bind(wx
.EVT_LIST_COL_BEGIN_DRAG
, self
.OnColBeginDrag
, self
.list) 
 143         self
.Bind(wx
.EVT_LIST_COL_DRAGGING
, self
.OnColDragging
, self
.list) 
 144         self
.Bind(wx
.EVT_LIST_COL_END_DRAG
, self
.OnColEndDrag
, self
.list) 
 145         self
.Bind(wx
.EVT_LIST_BEGIN_LABEL_EDIT
, self
.OnBeginEdit
, self
.list) 
 147         self
.list.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnDoubleClick
) 
 148         self
.list.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnRightDown
) 
 151         self
.list.Bind(wx
.EVT_COMMAND_RIGHT_CLICK
, self
.OnRightClick
) 
 154         self
.list.Bind(wx
.EVT_RIGHT_UP
, self
.OnRightClick
) 
 157     def OnUseNative(self
, event
): 
 158         wx
.SystemOptions
.SetOptionInt("mac.listctrl.always_use_generic", not event
.IsChecked()) 
 159         wx
.GetApp().GetTopWindow().LoadDemo("ListCtrl") 
 161     def PopulateList(self
): 
 163             # for normal, simple columns, you can add them like this: 
 164             self
.list.InsertColumn(0, "Artist") 
 165             self
.list.InsertColumn(1, "Title", wx
.LIST_FORMAT_RIGHT
) 
 166             self
.list.InsertColumn(2, "Genre") 
 168             # but since we want images on the column header we have to do it the hard way: 
 170             info
.m_mask 
= wx
.LIST_MASK_TEXT | wx
.LIST_MASK_IMAGE | wx
.LIST_MASK_FORMAT
 
 173             info
.m_text 
= "Artist" 
 174             self
.list.InsertColumnInfo(0, info
) 
 176             info
.m_format 
= wx
.LIST_FORMAT_RIGHT
 
 177             info
.m_text 
= "Title" 
 178             self
.list.InsertColumnInfo(1, info
) 
 181             info
.m_text 
= "Genre" 
 182             self
.list.InsertColumnInfo(2, info
) 
 184         items 
= musicdata
.items() 
 185         for key
, data 
in items
: 
 186             index 
= self
.list.InsertImageStringItem(sys
.maxint
, data
[0], self
.idx1
) 
 187             self
.list.SetStringItem(index
, 1, data
[1]) 
 188             self
.list.SetStringItem(index
, 2, data
[2]) 
 189             self
.list.SetItemData(index
, key
) 
 191         self
.list.SetColumnWidth(0, wx
.LIST_AUTOSIZE
) 
 192         self
.list.SetColumnWidth(1, wx
.LIST_AUTOSIZE
) 
 193         self
.list.SetColumnWidth(2, 100) 
 195         # show how to select an item 
 196         self
.list.SetItemState(5, wx
.LIST_STATE_SELECTED
, wx
.LIST_STATE_SELECTED
) 
 198         # show how to change the colour of a couple items 
 199         item 
= self
.list.GetItem(1) 
 200         item
.SetTextColour(wx
.BLUE
) 
 201         self
.list.SetItem(item
) 
 202         item 
= self
.list.GetItem(4) 
 203         item
.SetTextColour(wx
.RED
) 
 204         self
.list.SetItem(item
) 
 209     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py 
 210     def GetListCtrl(self
): 
 213     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py 
 214     def GetSortImages(self
): 
 215         return (self
.sm_dn
, self
.sm_up
) 
 218     def OnRightDown(self
, event
): 
 221         self
.log
.WriteText("x, y = %s\n" % str((x
, y
))) 
 222         item
, flags 
= self
.list.HitTest((x
, y
)) 
 224         if item 
!= wx
.NOT_FOUND 
and flags 
& wx
.LIST_HITTEST_ONITEM
: 
 225             self
.list.Select(item
) 
 230     def getColumnText(self
, index
, col
): 
 231         item 
= self
.list.GetItem(index
, col
) 
 232         return item
.GetText() 
 235     def OnItemSelected(self
, event
): 
 236         ##print event.GetItem().GetTextColour() 
 237         self
.currentItem 
= event
.m_itemIndex
 
 238         self
.log
.WriteText("OnItemSelected: %s, %s, %s, %s\n" % 
 240                             self
.list.GetItemText(self
.currentItem
), 
 241                             self
.getColumnText(self
.currentItem
, 1), 
 242                             self
.getColumnText(self
.currentItem
, 2))) 
 244         if self
.currentItem 
== 10: 
 245             self
.log
.WriteText("OnItemSelected: Veto'd selection\n") 
 246             #event.Veto()  # doesn't work 
 248             self
.list.SetItemState(10, 0, wx
.LIST_STATE_SELECTED
) 
 253     def OnItemDeselected(self
, evt
): 
 255         self
.log
.WriteText("OnItemDeselected: %d" % evt
.m_itemIndex
) 
 257         # Show how to reselect something we don't want deselected 
 258         if evt
.m_itemIndex 
== 11: 
 259             wx
.CallAfter(self
.list.SetItemState
, 11, wx
.LIST_STATE_SELECTED
, wx
.LIST_STATE_SELECTED
) 
 262     def OnItemActivated(self
, event
): 
 263         self
.currentItem 
= event
.m_itemIndex
 
 264         self
.log
.WriteText("OnItemActivated: %s\nTopItem: %s" % 
 265                            (self
.list.GetItemText(self
.currentItem
), self
.list.GetTopItem())) 
 267     def OnBeginEdit(self
, event
): 
 268         self
.log
.WriteText("OnBeginEdit") 
 271     def OnItemDelete(self
, event
): 
 272         self
.log
.WriteText("OnItemDelete\n") 
 274     def OnColClick(self
, event
): 
 275         self
.log
.WriteText("OnColClick: %d\n" % event
.GetColumn()) 
 278     def OnColRightClick(self
, event
): 
 279         item 
= self
.list.GetColumn(event
.GetColumn()) 
 280         self
.log
.WriteText("OnColRightClick: %d %s\n" % 
 281                            (event
.GetColumn(), (item
.GetText(), item
.GetAlign(), 
 282                                                 item
.GetWidth(), item
.GetImage()))) 
 284     def OnColBeginDrag(self
, event
): 
 285         self
.log
.WriteText("OnColBeginDrag\n") 
 286         ## Show how to not allow a column to be resized 
 287         #if event.GetColumn() == 0: 
 291     def OnColDragging(self
, event
): 
 292         self
.log
.WriteText("OnColDragging\n") 
 294     def OnColEndDrag(self
, event
): 
 295         self
.log
.WriteText("OnColEndDrag\n") 
 297     def OnDoubleClick(self
, event
): 
 298         self
.log
.WriteText("OnDoubleClick item %s\n" % self
.list.GetItemText(self
.currentItem
)) 
 301     def OnRightClick(self
, event
): 
 302         self
.log
.WriteText("OnRightClick %s\n" % self
.list.GetItemText(self
.currentItem
)) 
 304         # only do this part the first time so the events are only bound once 
 305         if not hasattr(self
, "popupID1"): 
 306             self
.popupID1 
= wx
.NewId() 
 307             self
.popupID2 
= wx
.NewId() 
 308             self
.popupID3 
= wx
.NewId() 
 309             self
.popupID4 
= wx
.NewId() 
 310             self
.popupID5 
= wx
.NewId() 
 311             self
.popupID6 
= wx
.NewId() 
 313             self
.Bind(wx
.EVT_MENU
, self
.OnPopupOne
, id=self
.popupID1
) 
 314             self
.Bind(wx
.EVT_MENU
, self
.OnPopupTwo
, id=self
.popupID2
) 
 315             self
.Bind(wx
.EVT_MENU
, self
.OnPopupThree
, id=self
.popupID3
) 
 316             self
.Bind(wx
.EVT_MENU
, self
.OnPopupFour
, id=self
.popupID4
) 
 317             self
.Bind(wx
.EVT_MENU
, self
.OnPopupFive
, id=self
.popupID5
) 
 318             self
.Bind(wx
.EVT_MENU
, self
.OnPopupSix
, id=self
.popupID6
) 
 323         menu
.Append(self
.popupID1
, "FindItem tests") 
 324         menu
.Append(self
.popupID2
, "Iterate Selected") 
 325         menu
.Append(self
.popupID3
, "ClearAll and repopulate") 
 326         menu
.Append(self
.popupID4
, "DeleteAllItems") 
 327         menu
.Append(self
.popupID5
, "GetItem") 
 328         menu
.Append(self
.popupID6
, "Edit") 
 330         # Popup the menu.  If an item is selected then its handler 
 331         # will be called before PopupMenu returns. 
 336     def OnPopupOne(self
, event
): 
 337         self
.log
.WriteText("Popup one\n") 
 338         print "FindItem:", self
.list.FindItem(-1, "Roxette") 
 339         print "FindItemData:", self
.list.FindItemData(-1, 11) 
 341     def OnPopupTwo(self
, event
): 
 342         self
.log
.WriteText("Selected items:\n") 
 343         index 
= self
.list.GetFirstSelected() 
 346             self
.log
.WriteText("      %s: %s\n" % (self
.list.GetItemText(index
), self
.getColumnText(index
, 1))) 
 347             index 
= self
.list.GetNextSelected(index
) 
 349     def OnPopupThree(self
, event
): 
 350         self
.log
.WriteText("Popup three\n") 
 352         wx
.CallAfter(self
.PopulateList
) 
 354     def OnPopupFour(self
, event
): 
 355         self
.list.DeleteAllItems() 
 357     def OnPopupFive(self
, event
): 
 358         item 
= self
.list.GetItem(self
.currentItem
) 
 359         print item
.m_text
, item
.m_itemId
, self
.list.GetItemData(self
.currentItem
) 
 361     def OnPopupSix(self
, event
): 
 362         self
.list.EditLabel(self
.currentItem
) 
 365 #--------------------------------------------------------------------------- 
 367 def runTest(frame
, nb
, log
): 
 368     win 
= TestListCtrlPanel(nb
, log
) 
 371 #--------------------------------------------------------------------------- 
 377 A list control presents lists in a number of formats: list view, report view,  
 378 icon view and small icon view. In any case, elements are numbered from zero.  
 379 For all these modes (but not for virtual list controls), the items are stored  
 380 in the control and must be added to it using InsertItem method. 
 382 <p>To intercept events from a list control, use the event table macros described in  
 383 <code>wxListEvent.</code> 
 386 This example demonstrates how to use mixins. The following mixins are available. 
 388 <h4>ColumnSorterMixin</h4> 
 390 <code><b>ColumnSorterMixin(numColumns)</b></code> 
 392 <p>A mixin class that handles sorting of a wxListCtrl in REPORT mode when the column  
 393 header is clicked on. 
 395 <p>There are a few requirments needed in order for this to work genericly: 
 397     <li>The combined class must have a <code>GetListCtrl</code> method that returns  
 398     the ListCtrl to be sorted, and the list control must exist at the time the  
 399     <code>ColumnSorterMixin.__init__()</code>method is called because it uses  
 400     <code>GetListCtrl</code>. 
 402     <li>Items in the list control must have a unique data value set with  
 403     <code>list.SetItemData</code>. 
 405     <li>The combined class must have an attribute named <code>itemDataMap</code> 
 406     that is a dictionary mapping the data values to a sequence of objects  
 407     representing the values in each column.  These valuesare compared in  
 408     the column sorter to determine sort order. 
 411 <p>Interesting methods to override are <code>GetColumnSorter</code>, 
 412 <code>GetSecondarySortValues</code>, and <code>GetSortImages</code>. 
 416 <dt><code>SetColumnCount(newNumColumns)</code> 
 417 <dd>Informs the mixin as to the number of columns in the control. When it is 
 418 set, it also sets up an event handler for <code>EVT_LIST_COL_CLICK</code> events. 
 420 <dt><code>SortListItems(col=-1, ascending=1)</code> 
 421 <dd>Sort the list on demand.  Can also be used to set the sort column and order. 
 423 <dt><code>GetColumnWidths()</code> 
 424 <dd>Returns a list of column widths.  Can be used to help restore the current  
 427 <dt><code>GetSortImages()</code> 
 428 <dd>Returns a tuple of image list indexes the indexes in the image list for an  
 429 image to be put on the column header when sorting in descending order 
 431 <dt><code>GetColumnSorter()</code> 
 432 <dd>Returns a callable object to be used for comparing column values when sorting. 
 434 <dt><code>GetSecondarySortValues(col, key1, key2)</code> 
 435 <dd>Returns a tuple of 2 values to use for secondary sort values when the 
 436 items in the selected column match equal.  The default just returns the  
 441 <h4>ListCtrlAutoWidthMixin</h4> 
 443 <code><b>ListCtrlAutoWidthMixin()</b></code> 
 445 <p>A mix-in class that automatically resizes the last column to take up the  
 446 remaining width of the ListCtrl. 
 448 <p>This causes the ListCtrl to automatically take up the full width of the list,  
 449 without either a horizontal scroll bar (unless absolutely necessary) or empty  
 450 space to the right of the last column. 
 452 <p><b>NOTE:</b> This only works for report-style lists. 
 454 <p><b>WARNING:</b> If you override the <code>EVT_SIZE</code> event in your ListCtrl,  
 455 make sure you call event.Skip() to ensure that the mixin's _OnResize method is  
 458 <p>This mix-in class was written by <a href='mailto:ewestra@wave.co.nz'>Erik Westra </a> 
 463 <dt><code>resizeLastColumn(minWidth)</code> 
 464 <dd>Resize the last column appropriately. If the list's columns are too wide to  
 465 fit within the window, we use a horizontal scrollbar.  Otherwise, we expand the  
 466 right-most column to take up the remaining free space in the list. This method is  
 467 called automatically when the ListCtrl is resized; you can also call it yourself  
 468 whenever you want the last column to be resized appropriately (eg, when adding,  
 469 removing or resizing columns).  'minWidth' is the preferred minimum width for  
 475 <h4>ListCtrlSelectionManagerMix</h4> 
 477 <code><b>ListCtrlSelectionManagerMix()</b></code> 
 479 <p>Mixin that defines a platform independent selection policy 
 481 <p>As selection single and multi-select list return the item index or a  
 482 list of item indexes respectively. 
 487 <dt><code>getPopupMenu()</code> 
 488 <dd>Override to implement dynamic menus (create) 
 490 <dt><code>setPopupMenu(menu)</code> 
 491 <dd>Must be set for default behaviour. 
 493 <dt><code>afterPopupMenu()</code> 
 494 <dd>Override to implement dynamic menus (destroy). 
 496 <dt><code>getSelection()</code> 
 497 <dd>Returns the current selection (or selections  as a Python list if extended  
 498 selection is enabled) 
 506 if __name__ 
== '__main__': 
 509     run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])