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 #----------------------------------------------------------------------------