1 #---------------------------------------------------------------------------- 
   2 # Name:        wxPython.lib.mixins.listctrl 
   3 # Purpose:     Helpful mix-in classes for wxListCtrl 
   9 # Copyright:   (c) 2001 by Total Control Software 
  10 # Licence:     wxWindows license 
  11 #---------------------------------------------------------------------------- 
  12 # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  14 # o 2.5 compatability update. 
  15 # o ListCtrlSelectionManagerMix untested. 
  17 # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  19 # o wxColumnSorterMixin -> ColumnSorterMixin  
  20 # o wxListCtrlAutoWidthMixin -> ListCtrlAutoWidthMixin 
  22 # 13/10/2004 - Pim Van Heuven (pim@think-wize.com) 
  23 # o wxTextEditMixin: Support Horizontal scrolling when TAB is pressed on long 
  24 #       ListCtrls, support for WXK_DOWN, WXK_UP, performance improvements on 
  25 #       very long ListCtrls, Support for virtual ListCtrls 
  27 # 15-Oct-2004 - Robin Dunn 
  28 # o wxTextEditMixin: Added Shift-TAB support 
  34 #---------------------------------------------------------------------------- 
  36 class ColumnSorterMixin
: 
  38     A mixin class that handles sorting of a wx.ListCtrl in REPORT mode when 
  39     the column header is clicked on. 
  41     There are a few requirments needed in order for this to work genericly: 
  43       1. The combined class must have a GetListCtrl method that 
  44          returns the wx.ListCtrl to be sorted, and the list control 
  45          must exist at the time the wx.ColumnSorterMixin.__init__ 
  46          method is called because it uses GetListCtrl. 
  48       2. Items in the list control must have a unique data value set 
  49          with list.SetItemData. 
  51       3. The combined class must have an attribute named itemDataMap 
  52          that is a dictionary mapping the data values to a sequence of 
  53          objects representing the values in each column.  These values 
  54          are compared in the column sorter to determine sort order. 
  56     Interesting methods to override are GetColumnSorter, 
  57     GetSecondarySortValues, and GetSortImages.  See below for details. 
  60     def __init__(self
, numColumns
): 
  61         self
.SetColumnCount(numColumns
) 
  62         list = self
.GetListCtrl() 
  64             raise ValueError, "No wx.ListCtrl available" 
  65         list.Bind(wx
.EVT_LIST_COL_CLICK
, self
.__OnColClick
, list) 
  68     def SetColumnCount(self
, newNumColumns
): 
  69         self
._colSortFlag 
= [0] * newNumColumns
 
  73     def SortListItems(self
, col
=-1, ascending
=1): 
  74         """Sort the list on demand.  Can also be used to set the sort column and order.""" 
  78             self
._colSortFlag
[col
] = ascending
 
  79         self
.GetListCtrl().SortItems(self
.GetColumnSorter()) 
  80         self
.__updateImages
(oldCol
) 
  83     def GetColumnWidths(self
): 
  85         Returns a list of column widths.  Can be used to help restore the current 
  88         list = self
.GetListCtrl() 
  90         for x 
in range(len(self
._colSortFlag
)): 
  91             rv
.append(list.GetColumnWidth(x
)) 
  95     def GetSortImages(self
): 
  97         Returns a tuple of image list indexesthe indexes in the image list for an image to be put on the column 
  98         header when sorting in descending order. 
 100         return (-1, -1)  # (decending, ascending) image IDs 
 103     def GetColumnSorter(self
): 
 104         """Returns a callable object to be used for comparing column values when sorting.""" 
 105         return self
.__ColumnSorter
 
 108     def GetSecondarySortValues(self
, col
, key1
, key2
): 
 109         """Returns a tuple of 2 values to use for secondary sort values when the 
 110            items in the selected column match equal.  The default just returns the 
 115     def __OnColClick(self
, evt
): 
 117         self
._col 
= col 
= evt
.GetColumn() 
 118         self
._colSortFlag
[col
] = int(not self
._colSortFlag
[col
]) 
 119         self
.GetListCtrl().SortItems(self
.GetColumnSorter()) 
 120         if wx
.Platform 
!= "__WXMAC__" or wx
.SystemOptions
.GetOptionInt("mac.listctrl.always_use_generic") == 1: 
 121             self
.__updateImages
(oldCol
) 
 123         self
.OnSortOrderChanged() 
 126     def OnSortOrderChanged(self
): 
 128         Callback called after sort order has changed (whenever user 
 129         clicked column header). 
 134     def __ColumnSorter(self
, key1
, key2
): 
 136         ascending 
= self
._colSortFlag
[col
] 
 137         item1 
= self
.itemDataMap
[key1
][col
] 
 138         item2 
= self
.itemDataMap
[key2
][col
] 
 140         #--- Internationalization of string sorting with locale module 
 141         if type(item1
) == type('') or type(item2
) == type(''): 
 142             cmpVal 
= locale
.strcoll(str(item1
), str(item2
)) 
 144             cmpVal 
= cmp(item1
, item2
) 
 147         # If the items are equal then pick something else to make the sort value unique 
 149             cmpVal 
= apply(cmp, self
.GetSecondarySortValues(col
, key1
, key2
)) 
 157     def __updateImages(self
, oldCol
): 
 158         sortImages 
= self
.GetSortImages() 
 159         if self
._col 
!= -1 and sortImages
[0] != -1: 
 160             img 
= sortImages
[self
._colSortFlag
[self
._col
]] 
 161             list = self
.GetListCtrl() 
 163                 list.ClearColumnImage(oldCol
) 
 164             list.SetColumnImage(self
._col
, img
) 
 167 #---------------------------------------------------------------------------- 
 168 #---------------------------------------------------------------------------- 
 170 class ListCtrlAutoWidthMixin
: 
 171     """ A mix-in class that automatically resizes the last column to take up 
 172         the remaining width of the wx.ListCtrl. 
 174         This causes the wx.ListCtrl to automatically take up the full width of 
 175         the list, without either a horizontal scroll bar (unless absolutely 
 176         necessary) or empty space to the right of the last column. 
 178         NOTE:    This only works for report-style lists. 
 180         WARNING: If you override the EVT_SIZE event in your wx.ListCtrl, make 
 181                  sure you call event.Skip() to ensure that the mixin's 
 182                  _OnResize method is called. 
 184         This mix-in class was written by Erik Westra <ewestra@wave.co.nz> 
 187         """ Standard initialiser. 
 189         self
._resizeColMinWidth 
= None 
 190         self
._resizeColStyle 
= "LAST" 
 192         self
.Bind(wx
.EVT_SIZE
, self
._onResize
) 
 193         self
.Bind(wx
.EVT_LIST_COL_END_DRAG
, self
._onResize
, self
) 
 196     def setResizeColumn(self
, col
): 
 198         Specify which column that should be autosized.  Pass either 
 199         'LAST' or the column number.  Default is 'LAST'. 
 202             self
._resizeColStyle 
= "LAST" 
 204             self
._resizeColStyle 
= "COL" 
 205             self
._resizeCol 
= col
 
 208     def resizeLastColumn(self
, minWidth
): 
 209         """ Resize the last column appropriately. 
 211             If the list's columns are too wide to fit within the window, we use 
 212             a horizontal scrollbar.  Otherwise, we expand the right-most column 
 213             to take up the remaining free space in the list. 
 215             This method is called automatically when the wx.ListCtrl is resized; 
 216             you can also call it yourself whenever you want the last column to 
 217             be resized appropriately (eg, when adding, removing or resizing 
 220             'minWidth' is the preferred minimum width for the last column. 
 222         self
.resizeColumn(minWidth
) 
 225     def resizeColumn(self
, minWidth
): 
 226         self
._resizeColMinWidth 
= minWidth
 
 230     # ===================== 
 231     # == Private Methods == 
 232     # ===================== 
 234     def _onResize(self
, event
): 
 235         """ Respond to the wx.ListCtrl being resized. 
 237             We automatically resize the last column in the list. 
 239         if 'gtk2' in wx
.PlatformInfo
: 
 242             wx
.CallAfter(self
._doResize
) 
 247         """ Resize the last column as appropriate. 
 249             If the list's columns are too wide to fit within the window, we use 
 250             a horizontal scrollbar.  Otherwise, we expand the right-most column 
 251             to take up the remaining free space in the list. 
 253             We remember the current size of the last column, before resizing, 
 254             as the preferred minimum width if we haven't previously been given 
 255             or calculated a minimum width.  This ensure that repeated calls to 
 256             _doResize() don't cause the last column to size itself too large. 
 259         if not self
:  # avoid a PyDeadObject error 
 262         if self
.GetSize().height 
< 32: 
 263             return  # avoid an endless update bug when the height is small. 
 265         numCols 
= self
.GetColumnCount() 
 266         if numCols 
== 0: return # Nothing to resize. 
 268         if(self
._resizeColStyle 
== "LAST"): 
 269             resizeCol 
= self
.GetColumnCount() 
 271             resizeCol 
= self
._resizeCol
 
 273         if self
._resizeColMinWidth 
== None: 
 274             self
._resizeColMinWidth 
= self
.GetColumnWidth(resizeCol 
- 1) 
 276         # We're showing the vertical scrollbar -> allow for scrollbar width 
 277         # NOTE: on GTK, the scrollbar is included in the client size, but on 
 278         # Windows it is not included 
 279         listWidth 
= self
.GetClientSize().width
 
 280         if wx
.Platform 
!= '__WXMSW__': 
 281             if self
.GetItemCount() > self
.GetCountPerPage(): 
 282                 scrollWidth 
= wx
.SystemSettings_GetMetric(wx
.SYS_VSCROLL_X
) 
 283                 listWidth 
= listWidth 
- scrollWidth
 
 285         totColWidth 
= 0 # Width of all columns except last one. 
 286         for col 
in range(numCols
): 
 287             if col 
!= (resizeCol
-1): 
 288                 totColWidth 
= totColWidth 
+ self
.GetColumnWidth(col
) 
 290         resizeColWidth 
= self
.GetColumnWidth(resizeCol 
- 1) 
 292         if totColWidth 
+ self
._resizeColMinWidth 
> listWidth
: 
 293             # We haven't got the width to show the last column at its minimum 
 294             # width -> set it to its minimum width and allow the horizontal 
 296             self
.SetColumnWidth(resizeCol
-1, self
._resizeColMinWidth
) 
 299         # Resize the last column to take up the remaining available space. 
 301         self
.SetColumnWidth(resizeCol
-1, listWidth 
- totColWidth
) 
 306 #---------------------------------------------------------------------------- 
 307 #---------------------------------------------------------------------------- 
 309 SEL_FOC 
= wx
.LIST_STATE_SELECTED | wx
.LIST_STATE_FOCUSED
 
 310 def selectBeforePopup(event
): 
 311     """Ensures the item the mouse is pointing at is selected before a popup. 
 313     Works with both single-select and multi-select lists.""" 
 314     ctrl 
= event
.GetEventObject() 
 315     if isinstance(ctrl
, wx
.ListCtrl
): 
 316         n
, flags 
= ctrl
.HitTest(event
.GetPosition()) 
 318             if not ctrl
.GetItemState(n
, wx
.LIST_STATE_SELECTED
): 
 319                 for i 
in range(ctrl
.GetItemCount()): 
 320                     ctrl
.SetItemState(i
, 0, SEL_FOC
) 
 321                 #for i in getListCtrlSelection(ctrl, SEL_FOC): 
 322                 #    ctrl.SetItemState(i, 0, SEL_FOC) 
 323                 ctrl
.SetItemState(n
, SEL_FOC
, SEL_FOC
) 
 326 def getListCtrlSelection(listctrl
, state
=wx
.LIST_STATE_SELECTED
): 
 327     """ Returns list of item indexes of given state (selected by defaults) """ 
 331         idx 
= listctrl
.GetNextItem(idx
, wx
.LIST_NEXT_ALL
, state
) 
 337 wxEVT_DOPOPUPMENU 
= wx
.NewEventType() 
 338 EVT_DOPOPUPMENU 
= wx
.PyEventBinder(wxEVT_DOPOPUPMENU
, 0) 
 341 class ListCtrlSelectionManagerMix
: 
 342     """Mixin that defines a platform independent selection policy 
 344     As selection single and multi-select list return the item index or a 
 345     list of item indexes respectively. 
 350         self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnLCSMRightDown
) 
 351         self
.Bind(EVT_DOPOPUPMENU
, self
.OnLCSMDoPopup
) 
 352 #        self.Connect(-1, -1, self.wxEVT_DOPOPUPMENU, self.OnLCSMDoPopup) 
 355     def getPopupMenu(self
): 
 356         """ Override to implement dynamic menus (create) """ 
 360     def setPopupMenu(self
, menu
): 
 361         """ Must be set for default behaviour """ 
 365     def afterPopupMenu(self
, menu
): 
 366         """ Override to implement dynamic menus (destroy) """ 
 370     def getSelection(self
): 
 371         res 
= getListCtrlSelection(self
) 
 372         if self
.GetWindowStyleFlag() & wx
.LC_SINGLE_SEL
: 
 381     def OnLCSMRightDown(self
, event
): 
 382         selectBeforePopup(event
) 
 384         menu 
= self
.getPopupMenu() 
 387             evt
.SetEventType(wxEVT_DOPOPUPMENU
) 
 389             evt
.pos 
= event
.GetPosition() 
 390             wx
.PostEvent(self
, evt
) 
 393     def OnLCSMDoPopup(self
, event
): 
 394         self
.PopupMenu(event
.menu
, event
.pos
) 
 395         self
.afterPopupMenu(event
.menu
) 
 398 #---------------------------------------------------------------------------- 
 399 #---------------------------------------------------------------------------- 
 400 from bisect 
import bisect
 
 405     A mixin class that enables any text in any column of a 
 406     multi-column listctrl to be edited by clicking on the given row 
 407     and column.  You close the text editor by hitting the ENTER key or 
 408     clicking somewhere else on the listctrl. You switch to the next 
 409     column by hiting TAB. 
 411     To use the mixin you have to include it in the class definition 
 412     and call the __init__ function:: 
 414         class TestListCtrl(wx.ListCtrl, TextEditMixin): 
 415             def __init__(self, parent, ID, pos=wx.DefaultPosition, 
 416                          size=wx.DefaultSize, style=0): 
 417                 wx.ListCtrl.__init__(self, parent, ID, pos, size, style) 
 418                 TextEditMixin.__init__(self)  
 421     Authors:     Steve Zatz, Pim Van Heuven (pim@think-wize.com) 
 424     editorBgColour 
= wx
.Colour(255,255,175) # Yellow 
 425     editorFgColour 
= wx
.Colour(0,0,0)       # black 
 428         #editor = wx.TextCtrl(self, -1, pos=(-1,-1), size=(-1,-1), 
 429         #                     style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB \ 
 433         self
.Bind(wx
.EVT_TEXT_ENTER
, self
.CloseEditor
) 
 434         self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
) 
 435         self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
) 
 436         self
.Bind(wx
.EVT_LIST_ITEM_SELECTED
, self
.OnItemSelected
) 
 439     def make_editor(self
, col_style
=wx
.LIST_FORMAT_LEFT
): 
 441         style 
=wx
.TE_PROCESS_ENTER|wx
.TE_PROCESS_TAB|wx
.TE_RICH2
 
 442         style |
= {wx
.LIST_FORMAT_LEFT
: wx
.TE_LEFT
, 
 443                   wx
.LIST_FORMAT_RIGHT
: wx
.TE_RIGHT
, 
 444                   wx
.LIST_FORMAT_CENTRE 
: wx
.TE_CENTRE
 
 447         editor 
= wx
.TextCtrl(self
, -1, style
=style
) 
 448         editor
.SetBackgroundColour(self
.editorBgColour
) 
 449         editor
.SetForegroundColour(self
.editorFgColour
) 
 450         font 
= self
.GetFont() 
 457         if hasattr(self
, 'editor'): 
 458             self
.editor
.Destroy() 
 461         self
.col_style 
= col_style
 
 462         self
.editor
.Bind(wx
.EVT_CHAR
, self
.OnChar
) 
 463         self
.editor
.Bind(wx
.EVT_KILL_FOCUS
, self
.CloseEditor
) 
 466     def OnItemSelected(self
, evt
): 
 467         self
.curRow 
= evt
.GetIndex() 
 471     def OnChar(self
, event
): 
 472         ''' Catch the TAB, Shift-TAB, cursor DOWN/UP key code 
 473             so we can open the editor at the next column (if any).''' 
 475         keycode 
= event
.GetKeyCode() 
 476         if keycode 
== wx
.WXK_TAB 
and event
.ShiftDown(): 
 478             if self
.curCol
-1 >= 0: 
 479                 self
.OpenEditor(self
.curCol
-1, self
.curRow
) 
 481         elif keycode 
== wx
.WXK_TAB
: 
 483             if self
.curCol
+1 < self
.GetColumnCount(): 
 484                 self
.OpenEditor(self
.curCol
+1, self
.curRow
) 
 486         elif keycode 
== wx
.WXK_ESCAPE
: 
 489         elif keycode 
== wx
.WXK_DOWN
: 
 491             if self
.curRow
+1 < self
.GetItemCount(): 
 492                 self
._SelectIndex
(self
.curRow
+1) 
 493                 self
.OpenEditor(self
.curCol
, self
.curRow
) 
 495         elif keycode 
== wx
.WXK_UP
: 
 498                 self
._SelectIndex
(self
.curRow
-1) 
 499                 self
.OpenEditor(self
.curCol
, self
.curRow
) 
 505     def OnLeftDown(self
, evt
=None): 
 506         ''' Examine the click and double 
 507         click events to see if a row has been click on twice. If so, 
 508         determine the current row and columnn and open the editor.''' 
 510         if self
.editor
.IsShown(): 
 513         x
,y 
= evt
.GetPosition() 
 514         row
,flags 
= self
.HitTest((x
,y
)) 
 516         if row 
!= self
.curRow
: # self.curRow keeps track of the current row 
 520         # the following should really be done in the mixin's init but 
 521         # the wx.ListCtrl demo creates the columns after creating the 
 522         # ListCtrl (generally not a good idea) on the other hand, 
 523         # doing this here handles adjustable column widths 
 527         for n 
in range(self
.GetColumnCount()): 
 528             loc 
= loc 
+ self
.GetColumnWidth(n
) 
 529             self
.col_locs
.append(loc
) 
 532         col 
= bisect(self
.col_locs
, x
+self
.GetScrollPos(wx
.HORIZONTAL
)) - 1 
 533         self
.OpenEditor(col
, row
) 
 536     def OpenEditor(self
, col
, row
): 
 537         ''' Opens an editor at the current position. ''' 
 539         # give the derived class a chance to Allow/Veto this edit. 
 540         evt 
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
, self
.GetId()) 
 541         evt
.m_itemIndex 
= row
 
 543         item 
= self
.GetItem(row
, col
) 
 544         evt
.m_item
.SetId(item
.GetId())  
 545         evt
.m_item
.SetColumn(item
.GetColumn())  
 546         evt
.m_item
.SetData(item
.GetData())  
 547         evt
.m_item
.SetText(item
.GetText())  
 548         ret 
= self
.GetEventHandler().ProcessEvent(evt
) 
 549         if ret 
and not evt
.IsAllowed(): 
 550             return   # user code doesn't allow the edit. 
 552         if self
.GetColumn(col
).m_format 
!= self
.col_style
: 
 553             self
.make_editor(self
.GetColumn(col
).m_format
) 
 555         x0 
= self
.col_locs
[col
] 
 556         x1 
= self
.col_locs
[col
+1] - x0
 
 558         scrolloffset 
= self
.GetScrollPos(wx
.HORIZONTAL
) 
 561         if x0
+x1
-scrolloffset 
> self
.GetSize()[0]: 
 562             if wx
.Platform 
== "__WXMSW__": 
 563                 # don't start scrolling unless we really need to 
 564                 offset 
= x0
+x1
-self
.GetSize()[0]-scrolloffset
 
 565                 # scroll a bit more than what is minimum required 
 566                 # so we don't have to scroll everytime the user presses TAB 
 567                 # which is very tireing to the eye 
 568                 addoffset 
= self
.GetSize()[0]/4 
 569                 # but be careful at the end of the list 
 570                 if addoffset 
+ scrolloffset 
< self
.GetSize()[0]: 
 573                 self
.ScrollList(offset
, 0) 
 574                 scrolloffset 
= self
.GetScrollPos(wx
.HORIZONTAL
) 
 576                 # Since we can not programmatically scroll the ListCtrl 
 577                 # close the editor so the user can scroll and open the editor 
 579                 self
.editor
.SetValue(self
.GetItem(row
, col
).GetText()) 
 585         y0 
= self
.GetItemRect(row
)[1] 
 588         editor
.SetDimensions(x0
-scrolloffset
,y0
, x1
,-1) 
 590         editor
.SetValue(self
.GetItem(row
, col
).GetText())  
 593         editor
.SetSelection(-1,-1) 
 600     # FIXME: this function is usually called twice - second time because 
 601     # it is binded to wx.EVT_KILL_FOCUS. Can it be avoided? (MW) 
 602     def CloseEditor(self
, evt
=None): 
 603         ''' Close the editor and save the new value to the ListCtrl. ''' 
 604         if not self
.editor
.IsShown(): 
 606         text 
= self
.editor
.GetValue() 
 610         # post wxEVT_COMMAND_LIST_END_LABEL_EDIT 
 611         # Event can be vetoed. It doesn't has SetEditCanceled(), what would  
 612         # require passing extra argument to CloseEditor()  
 613         evt 
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_END_LABEL_EDIT
, self
.GetId()) 
 614         evt
.m_itemIndex 
= self
.curRow
 
 615         evt
.m_col 
= self
.curCol
 
 616         item 
= self
.GetItem(self
.curRow
, self
.curCol
) 
 617         evt
.m_item
.SetId(item
.GetId())  
 618         evt
.m_item
.SetColumn(item
.GetColumn())  
 619         evt
.m_item
.SetData(item
.GetData())  
 620         evt
.m_item
.SetText(text
) #should be empty string if editor was canceled 
 621         ret 
= self
.GetEventHandler().ProcessEvent(evt
) 
 622         if not ret 
or evt
.IsAllowed(): 
 624                 # replace by whather you use to populate the virtual ListCtrl 
 626                 self
.SetVirtualData(self
.curRow
, self
.curCol
, text
) 
 628                 self
.SetStringItem(self
.curRow
, self
.curCol
, text
) 
 629         self
.RefreshItem(self
.curRow
) 
 631     def _SelectIndex(self
, row
): 
 632         listlen 
= self
.GetItemCount() 
 633         if row 
< 0 and not listlen
: 
 635         if row 
> (listlen
-1): 
 638         self
.SetItemState(self
.curRow
, ~wx
.LIST_STATE_SELECTED
, 
 639                           wx
.LIST_STATE_SELECTED
) 
 640         self
.EnsureVisible(row
) 
 641         self
.SetItemState(row
, wx
.LIST_STATE_SELECTED
, 
 642                           wx
.LIST_STATE_SELECTED
) 
 646 #---------------------------------------------------------------------------- 
 647 #---------------------------------------------------------------------------- 
 650 FILENAME: CheckListCtrlMixin.py 
 651 AUTHOR:   Bruce Who (bruce.who.hk at gmail.com) 
 655     This script provide a mixin for ListCtrl which add a checkbox in the first 
 656     column of each row. It is inspired by limodou's CheckList.py(which can be 
 657     got from his NewEdit) and improved: 
 658         - You can just use InsertStringItem() to insert new items; 
 659         - Once a checkbox is checked/unchecked, the corresponding item is not 
 661         - You can use SetItemData() and GetItemData(); 
 662         - Interfaces are changed to OnCheckItem(), IsChecked(), CheckItem(). 
 664     You should not set a imagelist for the ListCtrl once this mixin is used. 
 667 1.3     - You can check/uncheck a group of sequential items by <Shift-click>: 
 668           First click(or <Shift-Click>) item1 to check/uncheck it, then 
 669           Shift-click item2 to check/uncheck it, and you'll find that all 
 670           items between item1 and item2 are check/unchecked! 
 671 1.2     - Add ToggleItem() 
 672 1.1     - Initial version 
 675 from wx 
import ImageFromStream
, BitmapFromImage
 
 676 import cStringIO
, zlib
 
 678 def getUncheckData(): 
 679     return zlib
.decompress( 
 680 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\ 
 681 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xbb{\xba8\x86X\xf4\ 
 682 &\xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\ 
 683 \x90'\x01\x08v\xec]\xd3\xa3qvU`l\x81\xd9\xd18\t\xd3\x84+\x0cll[\xa6t\xcc9\ 
 684 \xd4\xc1\xda\xc3<O\x9a1\xc3\x88\xc3j\xfa\x86_\xee@#\x19<]\xfd\\\xd69%4\x01\ 
 687 def getUncheckBitmap(): 
 688     return BitmapFromImage(getUncheckImage()) 
 690 def getUncheckImage(): 
 691     stream 
= cStringIO
.StringIO(getUncheckData()) 
 692     return ImageFromStream(stream
) 
 695     return zlib
.decompress( 
 696 'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\ 
 697 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe47{\xba8\x86X\xf4&\ 
 698 \xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\ 
 699 \x90\'\x01\x08v\xec\\2C\xe3\xec+\xc3\xbd\x05fG\xe3\x14n1\xcc5\xad\x8a8\x1a\ 
 700 \xb9\xa1\xeb\xd1\x853-\xaa\xc76\xecb\xb8i\x16c&\\\xc2\xb8\xe9Xvbx\xa1T\xc3U\ 
 701 \xd6p\'\xbd\x85\x19\xff\xbe\xbf\xd7\xe7R\xcb`\xd8\xa5\xf8\x83\xe1^\xc4\x0e\ 
 702 \xa1"\xce\xc3n\x93x\x14\xd8\x16\xb0(\x15q)\x8b\x19\xf0U\xe4\xb10\x08V\xa8\ 
 703 \x99\xf3\xdd\xde\xad\x06t\x0e\x83\xa7\xab\x9f\xcb:\xa7\x84&\x00\xe0HE\xab' ) 
 705 def getCheckBitmap(): 
 706     return BitmapFromImage(getCheckImage()) 
 709     stream 
= cStringIO
.StringIO(getCheckData()) 
 710     return ImageFromStream(stream
) 
 714 class CheckListCtrlMixin
: 
 716     This is a mixin for ListCtrl which add a checkbox in the first 
 717     column of each row. It is inspired by limodou's CheckList.py(which 
 718     can be got from his NewEdit) and improved: 
 720         - You can just use InsertStringItem() to insert new items; 
 722         - Once a checkbox is checked/unchecked, the corresponding item 
 725         - You can use SetItemData() and GetItemData(); 
 727         - Interfaces are changed to OnCheckItem(), IsChecked(), 
 730     You should not set a imagelist for the ListCtrl once this mixin is used. 
 732     def __init__(self
, check_image
=None, uncheck_image
=None): 
 733         self
.__imagelist
_ = wx
.ImageList(16, 16) 
 735             check_image 
= getCheckBitmap() 
 736         if not uncheck_image
: 
 737             uncheck_image 
= getUncheckBitmap() 
 738         self
.uncheck_image 
= self
.__imagelist
_.Add(uncheck_image
) 
 739         self
.check_image 
= self
.__imagelist
_.Add(check_image
) 
 740         self
.SetImageList(self
.__imagelist
_, wx
.IMAGE_LIST_SMALL
) 
 741         self
.__last
_check
_ = None 
 743         self
.Bind(wx
.EVT_LEFT_DOWN
, self
.__OnLeftDown
_) 
 745         # override the default methods of ListCtrl/ListView 
 746         self
.InsertStringItem 
= self
.__InsertStringItem
_ 
 748     # NOTE: if you use InsertItem, InsertImageItem or InsertImageStringItem, 
 749     #       you must set the image yourself. 
 750     def __InsertStringItem_(self
, index
, label
): 
 751         index 
= self
.InsertImageStringItem(index
, label
, 0) 
 754     def __OnLeftDown_(self
, evt
): 
 755         (index
, flags
) = self
.HitTest(evt
.GetPosition()) 
 756         if flags 
== wx
.LIST_HITTEST_ONITEMICON
: 
 757             img_idx 
= self
.GetItem(index
).GetImage() 
 758             flag_check 
= img_idx 
== 0 
 761             if self
.__last
_check
_ is not None \
 
 762                     and wx
.GetKeyState(wx
.WXK_SHIFT
): 
 763                 last_index
, last_flag_check 
= self
.__last
_check
_ 
 764                 if last_flag_check 
== flag_check
: 
 765                     # XXX what if the previous item is deleted or new items 
 767                     item_count 
= self
.GetItemCount() 
 768                     if last_index 
< item_count
: 
 769                         if last_index 
< index
: 
 770                             begin_index 
= last_index
 
 772                         elif last_index 
> index
: 
 774                             end_index 
= last_index
 
 777             while begin_index 
<= end_index
: 
 778                 self
.CheckItem(begin_index
, flag_check
) 
 780             self
.__last
_check
_ = (index
, flag_check
) 
 784     def OnCheckItem(self
, index
, flag
): 
 787     def IsChecked(self
, index
): 
 788         return self
.GetItem(index
).GetImage() == 1 
 790     def CheckItem(self
, index
, check 
= True): 
 791         img_idx 
= self
.GetItem(index
).GetImage() 
 792         if img_idx 
== 0 and check 
is True: 
 793             self
.SetItemImage(index
, 1) 
 794             self
.OnCheckItem(index
, True) 
 795         elif img_idx 
== 1 and check 
is False: 
 796             self
.SetItemImage(index
, 0) 
 797             self
.OnCheckItem(index
, False) 
 799     def ToggleItem(self
, index
): 
 800         self
.CheckItem(index
, not self
.IsChecked(index
)) 
 803 #----------------------------------------------------------------------------