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 self
.__updateImages
(oldCol
)
124 def __ColumnSorter(self
, key1
, key2
):
126 ascending
= self
._colSortFlag
[col
]
127 item1
= self
.itemDataMap
[key1
][col
]
128 item2
= self
.itemDataMap
[key2
][col
]
130 #--- Internationalization of string sorting with locale module
131 if type(item1
) == type('') or type(item2
) == type(''):
132 cmpVal
= locale
.strcoll(str(item1
), str(item2
))
134 cmpVal
= cmp(item1
, item2
)
137 # If the items are equal then pick something else to make the sort value unique
139 cmpVal
= apply(cmp, self
.GetSecondarySortValues(col
, key1
, key2
))
147 def __updateImages(self
, oldCol
):
148 sortImages
= self
.GetSortImages()
149 if self
._col
!= -1 and sortImages
[0] != -1:
150 img
= sortImages
[self
._colSortFlag
[self
._col
]]
151 list = self
.GetListCtrl()
153 list.ClearColumnImage(oldCol
)
154 list.SetColumnImage(self
._col
, img
)
157 #----------------------------------------------------------------------------
158 #----------------------------------------------------------------------------
160 class ListCtrlAutoWidthMixin
:
161 """ A mix-in class that automatically resizes the last column to take up
162 the remaining width of the wx.ListCtrl.
164 This causes the wx.ListCtrl to automatically take up the full width of
165 the list, without either a horizontal scroll bar (unless absolutely
166 necessary) or empty space to the right of the last column.
168 NOTE: This only works for report-style lists.
170 WARNING: If you override the EVT_SIZE event in your wx.ListCtrl, make
171 sure you call event.Skip() to ensure that the mixin's
172 _OnResize method is called.
174 This mix-in class was written by Erik Westra <ewestra@wave.co.nz>
177 """ Standard initialiser.
179 self
._resizeColMinWidth
= None
180 self
._resizeColStyle
= "LAST"
182 self
.Bind(wx
.EVT_SIZE
, self
._onResize
)
183 self
.Bind(wx
.EVT_LIST_COL_END_DRAG
, self
._onResize
, self
)
186 def setResizeColumn(self
, col
):
188 Specify which column that should be autosized. Pass either
189 'LAST' or the column number. Default is 'LAST'.
192 self
._resizeColStyle
= "LAST"
194 self
._resizeColStyle
= "COL"
195 self
._resizeCol
= col
198 def resizeLastColumn(self
, minWidth
):
199 """ Resize the last column appropriately.
201 If the list's columns are too wide to fit within the window, we use
202 a horizontal scrollbar. Otherwise, we expand the right-most column
203 to take up the remaining free space in the list.
205 This method is called automatically when the wx.ListCtrl is resized;
206 you can also call it yourself whenever you want the last column to
207 be resized appropriately (eg, when adding, removing or resizing
210 'minWidth' is the preferred minimum width for the last column.
212 self
.resizeColumn(minWidth
)
215 def resizeColumn(self
, minWidth
):
216 self
._resizeColMinWidth
= minWidth
220 # =====================
221 # == Private Methods ==
222 # =====================
224 def _onResize(self
, event
):
225 """ Respond to the wx.ListCtrl being resized.
227 We automatically resize the last column in the list.
229 if 'gtk2' in wx
.PlatformInfo
:
232 wx
.CallAfter(self
._doResize
)
237 """ Resize the last column as appropriate.
239 If the list's columns are too wide to fit within the window, we use
240 a horizontal scrollbar. Otherwise, we expand the right-most column
241 to take up the remaining free space in the list.
243 We remember the current size of the last column, before resizing,
244 as the preferred minimum width if we haven't previously been given
245 or calculated a minimum width. This ensure that repeated calls to
246 _doResize() don't cause the last column to size itself too large.
249 if not self
: # avoid a PyDeadObject error
252 numCols
= self
.GetColumnCount()
253 if numCols
== 0: return # Nothing to resize.
255 if(self
._resizeColStyle
== "LAST"):
256 resizeCol
= self
.GetColumnCount()
258 resizeCol
= self
._resizeCol
260 if self
._resizeColMinWidth
== None:
261 self
._resizeColMinWidth
= self
.GetColumnWidth(resizeCol
- 1)
263 # We're showing the vertical scrollbar -> allow for scrollbar width
264 # NOTE: on GTK, the scrollbar is included in the client size, but on
265 # Windows it is not included
266 listWidth
= self
.GetClientSize().width
267 if wx
.Platform
!= '__WXMSW__':
268 if self
.GetItemCount() > self
.GetCountPerPage():
269 scrollWidth
= wx
.SystemSettings_GetMetric(wx
.SYS_VSCROLL_X
)
270 listWidth
= listWidth
- scrollWidth
272 totColWidth
= 0 # Width of all columns except last one.
273 for col
in range(numCols
):
274 if col
!= (resizeCol
-1):
275 totColWidth
= totColWidth
+ self
.GetColumnWidth(col
)
277 resizeColWidth
= self
.GetColumnWidth(resizeCol
- 1)
279 if totColWidth
+ self
._resizeColMinWidth
> listWidth
:
280 # We haven't got the width to show the last column at its minimum
281 # width -> set it to its minimum width and allow the horizontal
283 self
.SetColumnWidth(resizeCol
-1, self
._resizeColMinWidth
)
286 # Resize the last column to take up the remaining available space.
288 self
.SetColumnWidth(resizeCol
-1, listWidth
- totColWidth
)
293 #----------------------------------------------------------------------------
294 #----------------------------------------------------------------------------
296 SEL_FOC
= wx
.LIST_STATE_SELECTED | wx
.LIST_STATE_FOCUSED
297 def selectBeforePopup(event
):
298 """Ensures the item the mouse is pointing at is selected before a popup.
300 Works with both single-select and multi-select lists."""
301 ctrl
= event
.GetEventObject()
302 if isinstance(ctrl
, wx
.ListCtrl
):
303 n
, flags
= ctrl
.HitTest(event
.GetPosition())
305 if not ctrl
.GetItemState(n
, wx
.LIST_STATE_SELECTED
):
306 for i
in range(ctrl
.GetItemCount()):
307 ctrl
.SetItemState(i
, 0, SEL_FOC
)
308 #for i in getListCtrlSelection(ctrl, SEL_FOC):
309 # ctrl.SetItemState(i, 0, SEL_FOC)
310 ctrl
.SetItemState(n
, SEL_FOC
, SEL_FOC
)
313 def getListCtrlSelection(listctrl
, state
=wx
.LIST_STATE_SELECTED
):
314 """ Returns list of item indexes of given state (selected by defaults) """
318 idx
= listctrl
.GetNextItem(idx
, wx
.LIST_NEXT_ALL
, state
)
324 wxEVT_DOPOPUPMENU
= wx
.NewEventType()
325 EVT_DOPOPUPMENU
= wx
.PyEventBinder(wxEVT_DOPOPUPMENU
, 0)
328 class ListCtrlSelectionManagerMix
:
329 """Mixin that defines a platform independent selection policy
331 As selection single and multi-select list return the item index or a
332 list of item indexes respectively.
337 self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnLCSMRightDown
)
338 self
.Bind(EVT_DOPOPUPMENU
, self
.OnLCSMDoPopup
)
339 # self.Connect(-1, -1, self.wxEVT_DOPOPUPMENU, self.OnLCSMDoPopup)
342 def getPopupMenu(self
):
343 """ Override to implement dynamic menus (create) """
347 def setPopupMenu(self
, menu
):
348 """ Must be set for default behaviour """
352 def afterPopupMenu(self
, menu
):
353 """ Override to implement dynamic menus (destroy) """
357 def getSelection(self
):
358 res
= getListCtrlSelection(self
)
359 if self
.GetWindowStyleFlag() & wx
.LC_SINGLE_SEL
:
368 def OnLCSMRightDown(self
, event
):
369 selectBeforePopup(event
)
371 menu
= self
.getPopupMenu()
374 evt
.SetEventType(wxEVT_DOPOPUPMENU
)
376 evt
.pos
= event
.GetPosition()
377 wx
.PostEvent(self
, evt
)
380 def OnLCSMDoPopup(self
, event
):
381 self
.PopupMenu(event
.menu
, event
.pos
)
382 self
.afterPopupMenu(event
.menu
)
385 #----------------------------------------------------------------------------
386 #----------------------------------------------------------------------------
387 from bisect
import bisect
392 A mixin class that enables any text in any column of a
393 multi-column listctrl to be edited by clicking on the given row
394 and column. You close the text editor by hitting the ENTER key or
395 clicking somewhere else on the listctrl. You switch to the next
396 column by hiting TAB.
398 To use the mixin you have to include it in the class definition
399 and call the __init__ function::
401 class TestListCtrl(wx.ListCtrl, TextEditMixin):
402 def __init__(self, parent, ID, pos=wx.DefaultPosition,
403 size=wx.DefaultSize, style=0):
404 wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
405 TextEditMixin.__init__(self)
408 Authors: Steve Zatz, Pim Van Heuven (pim@think-wize.com)
411 editorBgColour
= wx
.Colour(255,255,175) # Yellow
412 editorFgColour
= wx
.Colour(0,0,0) # black
415 #editor = wx.TextCtrl(self, -1, pos=(-1,-1), size=(-1,-1),
416 # style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB \
420 self
.Bind(wx
.EVT_TEXT_ENTER
, self
.CloseEditor
)
421 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
422 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
)
423 self
.Bind(wx
.EVT_LIST_ITEM_SELECTED
, self
.OnItemSelected
)
426 def make_editor(self
, col_style
=wx
.LIST_FORMAT_LEFT
):
428 style
=wx
.TE_PROCESS_ENTER|wx
.TE_PROCESS_TAB|wx
.TE_RICH2
429 style |
= {wx
.LIST_FORMAT_LEFT
: wx
.TE_LEFT
,
430 wx
.LIST_FORMAT_RIGHT
: wx
.TE_RIGHT
,
431 wx
.LIST_FORMAT_CENTRE
: wx
.TE_CENTRE
434 editor
= wx
.TextCtrl(self
, -1, style
=style
)
435 editor
.SetBackgroundColour(self
.editorBgColour
)
436 editor
.SetForegroundColour(self
.editorFgColour
)
437 font
= self
.GetFont()
444 if hasattr(self
, 'editor'):
445 self
.editor
.Destroy()
448 self
.col_style
= col_style
449 self
.editor
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
450 self
.editor
.Bind(wx
.EVT_KILL_FOCUS
, self
.CloseEditor
)
453 def OnItemSelected(self
, evt
):
454 self
.curRow
= evt
.GetIndex()
458 def OnChar(self
, event
):
459 ''' Catch the TAB, Shift-TAB, cursor DOWN/UP key code
460 so we can open the editor at the next column (if any).'''
462 keycode
= event
.GetKeyCode()
463 if keycode
== wx
.WXK_TAB
and event
.ShiftDown():
465 if self
.curCol
-1 >= 0:
466 self
.OpenEditor(self
.curCol
-1, self
.curRow
)
468 elif keycode
== wx
.WXK_TAB
:
470 if self
.curCol
+1 < self
.GetColumnCount():
471 self
.OpenEditor(self
.curCol
+1, self
.curRow
)
473 elif keycode
== wx
.WXK_ESCAPE
:
476 elif keycode
== wx
.WXK_DOWN
:
478 if self
.curRow
+1 < self
.GetItemCount():
479 self
._SelectIndex
(self
.curRow
+1)
480 self
.OpenEditor(self
.curCol
, self
.curRow
)
482 elif keycode
== wx
.WXK_UP
:
485 self
._SelectIndex
(self
.curRow
-1)
486 self
.OpenEditor(self
.curCol
, self
.curRow
)
492 def OnLeftDown(self
, evt
=None):
493 ''' Examine the click and double
494 click events to see if a row has been click on twice. If so,
495 determine the current row and columnn and open the editor.'''
497 if self
.editor
.IsShown():
500 x
,y
= evt
.GetPosition()
501 row
,flags
= self
.HitTest((x
,y
))
503 if row
!= self
.curRow
: # self.curRow keeps track of the current row
507 # the following should really be done in the mixin's init but
508 # the wx.ListCtrl demo creates the columns after creating the
509 # ListCtrl (generally not a good idea) on the other hand,
510 # doing this here handles adjustable column widths
514 for n
in range(self
.GetColumnCount()):
515 loc
= loc
+ self
.GetColumnWidth(n
)
516 self
.col_locs
.append(loc
)
519 col
= bisect(self
.col_locs
, x
+self
.GetScrollPos(wx
.HORIZONTAL
)) - 1
520 self
.OpenEditor(col
, row
)
523 def OpenEditor(self
, col
, row
):
524 ''' Opens an editor at the current position. '''
526 # give the derived class a chance to Allow/Veto this edit.
527 evt
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
, self
.GetId())
528 evt
.m_itemIndex
= row
530 item
= self
.GetItem(row
, col
)
531 evt
.m_item
.SetId(item
.GetId())
532 evt
.m_item
.SetColumn(item
.GetColumn())
533 evt
.m_item
.SetData(item
.GetData())
534 evt
.m_item
.SetText(item
.GetText())
535 ret
= self
.GetEventHandler().ProcessEvent(evt
)
536 if ret
and not evt
.IsAllowed():
537 return # user code doesn't allow the edit.
539 if self
.GetColumn(col
).m_format
!= self
.col_style
:
540 self
.make_editor(self
.GetColumn(col
).m_format
)
542 x0
= self
.col_locs
[col
]
543 x1
= self
.col_locs
[col
+1] - x0
545 scrolloffset
= self
.GetScrollPos(wx
.HORIZONTAL
)
548 if x0
+x1
-scrolloffset
> self
.GetSize()[0]:
549 if wx
.Platform
== "__WXMSW__":
550 # don't start scrolling unless we really need to
551 offset
= x0
+x1
-self
.GetSize()[0]-scrolloffset
552 # scroll a bit more than what is minimum required
553 # so we don't have to scroll everytime the user presses TAB
554 # which is very tireing to the eye
555 addoffset
= self
.GetSize()[0]/4
556 # but be careful at the end of the list
557 if addoffset
+ scrolloffset
< self
.GetSize()[0]:
560 self
.ScrollList(offset
, 0)
561 scrolloffset
= self
.GetScrollPos(wx
.HORIZONTAL
)
563 # Since we can not programmatically scroll the ListCtrl
564 # close the editor so the user can scroll and open the editor
566 self
.editor
.SetValue(self
.GetItem(row
, col
).GetText())
572 y0
= self
.GetItemRect(row
)[1]
575 editor
.SetDimensions(x0
-scrolloffset
,y0
, x1
,-1)
577 editor
.SetValue(self
.GetItem(row
, col
).GetText())
580 editor
.SetSelection(-1,-1)
587 # FIXME: this function is usually called twice - second time because
588 # it is binded to wx.EVT_KILL_FOCUS. Can it be avoided? (MW)
589 def CloseEditor(self
, evt
=None):
590 ''' Close the editor and save the new value to the ListCtrl. '''
591 if not self
.editor
.IsShown():
593 text
= self
.editor
.GetValue()
597 # post wxEVT_COMMAND_LIST_END_LABEL_EDIT
598 # Event can be vetoed. It doesn't has SetEditCanceled(), what would
599 # require passing extra argument to CloseEditor()
600 evt
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_END_LABEL_EDIT
, self
.GetId())
601 evt
.m_itemIndex
= self
.curRow
602 evt
.m_col
= self
.curCol
603 item
= self
.GetItem(self
.curRow
, self
.curCol
)
604 evt
.m_item
.SetId(item
.GetId())
605 evt
.m_item
.SetColumn(item
.GetColumn())
606 evt
.m_item
.SetData(item
.GetData())
607 evt
.m_item
.SetText(text
) #should be empty string if editor was canceled
608 ret
= self
.GetEventHandler().ProcessEvent(evt
)
609 if not ret
or evt
.IsAllowed():
611 # replace by whather you use to populate the virtual ListCtrl
613 self
.SetVirtualData(self
.curRow
, self
.curCol
, text
)
615 self
.SetStringItem(self
.curRow
, self
.curCol
, text
)
616 self
.RefreshItem(self
.curRow
)
618 def _SelectIndex(self
, row
):
619 listlen
= self
.GetItemCount()
620 if row
< 0 and not listlen
:
622 if row
> (listlen
-1):
625 self
.SetItemState(self
.curRow
, ~wx
.LIST_STATE_SELECTED
,
626 wx
.LIST_STATE_SELECTED
)
627 self
.EnsureVisible(row
)
628 self
.SetItemState(row
, wx
.LIST_STATE_SELECTED
,
629 wx
.LIST_STATE_SELECTED
)
633 #----------------------------------------------------------------------------
634 #----------------------------------------------------------------------------
637 FILENAME: CheckListCtrlMixin.py
638 AUTHOR: Bruce Who (bruce.who.hk at gmail.com)
642 This script provide a mixin for ListCtrl which add a checkbox in the first
643 column of each row. It is inspired by limodou's CheckList.py(which can be
644 got from his NewEdit) and improved:
645 - You can just use InsertStringItem() to insert new items;
646 - Once a checkbox is checked/unchecked, the corresponding item is not
648 - You can use SetItemData() and GetItemData();
649 - Interfaces are changed to OnCheckItem(), IsChecked(), CheckItem().
651 You should not set a imagelist for the ListCtrl once this mixin is used.
654 1.3 - You can check/uncheck a group of sequential items by <Shift-click>:
655 First click(or <Shift-Click>) item1 to check/uncheck it, then
656 Shift-click item2 to check/uncheck it, and you'll find that all
657 items between item1 and item2 are check/unchecked!
658 1.2 - Add ToggleItem()
659 1.1 - Initial version
662 from wx
import ImageFromStream
, BitmapFromImage
663 import cStringIO
, zlib
665 def getUncheckData():
666 return zlib
.decompress(
667 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
668 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xbb{\xba8\x86X\xf4\
669 &\xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
670 \x90'\x01\x08v\xec]\xd3\xa3qvU`l\x81\xd9\xd18\t\xd3\x84+\x0cll[\xa6t\xcc9\
671 \xd4\xc1\xda\xc3<O\x9a1\xc3\x88\xc3j\xfa\x86_\xee@#\x19<]\xfd\\\xd69%4\x01\
674 def getUncheckBitmap():
675 return BitmapFromImage(getUncheckImage())
677 def getUncheckImage():
678 stream
= cStringIO
.StringIO(getUncheckData())
679 return ImageFromStream(stream
)
682 return zlib
.decompress(
683 'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
684 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe47{\xba8\x86X\xf4&\
685 \xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
686 \x90\'\x01\x08v\xec\\2C\xe3\xec+\xc3\xbd\x05fG\xe3\x14n1\xcc5\xad\x8a8\x1a\
687 \xb9\xa1\xeb\xd1\x853-\xaa\xc76\xecb\xb8i\x16c&\\\xc2\xb8\xe9Xvbx\xa1T\xc3U\
688 \xd6p\'\xbd\x85\x19\xff\xbe\xbf\xd7\xe7R\xcb`\xd8\xa5\xf8\x83\xe1^\xc4\x0e\
689 \xa1"\xce\xc3n\x93x\x14\xd8\x16\xb0(\x15q)\x8b\x19\xf0U\xe4\xb10\x08V\xa8\
690 \x99\xf3\xdd\xde\xad\x06t\x0e\x83\xa7\xab\x9f\xcb:\xa7\x84&\x00\xe0HE\xab' )
692 def getCheckBitmap():
693 return BitmapFromImage(getCheckImage())
696 stream
= cStringIO
.StringIO(getCheckData())
697 return ImageFromStream(stream
)
701 class CheckListCtrlMixin
:
703 This is a mixin for ListCtrl which add a checkbox in the first
704 column of each row. It is inspired by limodou's CheckList.py(which
705 can be got from his NewEdit) and improved:
706 - You can just use InsertStringItem() to insert new items;
707 - Once a checkbox is checked/unchecked, the corresponding item is not
709 - You can use SetItemData() and GetItemData();
710 - Interfaces are changed to OnCheckItem(), IsChecked(), CheckItem().
712 You should not set a imagelist for the ListCtrl once this mixin is used.
714 def __init__(self
, check_image
=None, uncheck_image
=None):
715 self
.__imagelist
_ = wx
.ImageList(16, 16)
717 check_image
= getCheckBitmap()
718 if not uncheck_image
:
719 uncheck_image
= getUncheckBitmap()
720 self
.uncheck_image
= self
.__imagelist
_.Add(uncheck_image
)
721 self
.check_image
= self
.__imagelist
_.Add(check_image
)
722 self
.SetImageList(self
.__imagelist
_, wx
.IMAGE_LIST_SMALL
)
723 self
.__last
_check
_ = None
725 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.__OnLeftDown
_)
727 # override the default methods of ListCtrl/ListView
728 self
.InsertStringItem
= self
.__InsertStringItem
_
730 # NOTE: if you use InsertItem, InsertImageItem or InsertImageStringItem,
731 # you must set the image yourself.
732 def __InsertStringItem_(self
, index
, label
):
733 index
= self
.InsertImageStringItem(index
, label
, 0)
736 def __OnLeftDown_(self
, evt
):
737 (index
, flags
) = self
.HitTest(evt
.GetPosition())
738 if flags
== wx
.LIST_HITTEST_ONITEMICON
:
739 img_idx
= self
.GetItem(index
).GetImage()
740 flag_check
= img_idx
== 0
743 if self
.__last
_check
_ is not None \
744 and wx
.GetKeyState(wx
.WXK_SHIFT
):
745 last_index
, last_flag_check
= self
.__last
_check
_
746 if last_flag_check
== flag_check
:
747 # XXX what if the previous item is deleted or new items
749 item_count
= self
.GetItemCount()
750 if last_index
< item_count
:
751 if last_index
< index
:
752 begin_index
= last_index
754 elif last_index
> index
:
756 end_index
= last_index
759 while begin_index
<= end_index
:
760 self
.CheckItem(begin_index
, flag_check
)
762 self
.__last
_check
_ = (index
, flag_check
)
766 def OnCheckItem(self
, index
, flag
):
769 def IsChecked(self
, index
):
770 return self
.GetItem(index
).GetImage() == 1
772 def CheckItem(self
, index
, check
= True):
773 img_idx
= self
.GetItem(index
).GetImage()
774 if img_idx
== 0 and check
is True:
775 self
.SetItemImage(index
, 1)
776 self
.OnCheckItem(index
, True)
777 elif img_idx
== 1 and check
is False:
778 self
.SetItemImage(index
, 0)
779 self
.OnCheckItem(index
, False)
781 def ToggleItem(self
, index
):
782 self
.CheckItem(index
, not self
.IsChecked(index
))
785 #----------------------------------------------------------------------------