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 if self
.GetSize().height
< 32:
253 return # avoid an endless update bug when the height is small.
255 numCols
= self
.GetColumnCount()
256 if numCols
== 0: return # Nothing to resize.
258 if(self
._resizeColStyle
== "LAST"):
259 resizeCol
= self
.GetColumnCount()
261 resizeCol
= self
._resizeCol
263 if self
._resizeColMinWidth
== None:
264 self
._resizeColMinWidth
= self
.GetColumnWidth(resizeCol
- 1)
266 # We're showing the vertical scrollbar -> allow for scrollbar width
267 # NOTE: on GTK, the scrollbar is included in the client size, but on
268 # Windows it is not included
269 listWidth
= self
.GetClientSize().width
270 if wx
.Platform
!= '__WXMSW__':
271 if self
.GetItemCount() > self
.GetCountPerPage():
272 scrollWidth
= wx
.SystemSettings_GetMetric(wx
.SYS_VSCROLL_X
)
273 listWidth
= listWidth
- scrollWidth
275 totColWidth
= 0 # Width of all columns except last one.
276 for col
in range(numCols
):
277 if col
!= (resizeCol
-1):
278 totColWidth
= totColWidth
+ self
.GetColumnWidth(col
)
280 resizeColWidth
= self
.GetColumnWidth(resizeCol
- 1)
282 if totColWidth
+ self
._resizeColMinWidth
> listWidth
:
283 # We haven't got the width to show the last column at its minimum
284 # width -> set it to its minimum width and allow the horizontal
286 self
.SetColumnWidth(resizeCol
-1, self
._resizeColMinWidth
)
289 # Resize the last column to take up the remaining available space.
291 self
.SetColumnWidth(resizeCol
-1, listWidth
- totColWidth
)
296 #----------------------------------------------------------------------------
297 #----------------------------------------------------------------------------
299 SEL_FOC
= wx
.LIST_STATE_SELECTED | wx
.LIST_STATE_FOCUSED
300 def selectBeforePopup(event
):
301 """Ensures the item the mouse is pointing at is selected before a popup.
303 Works with both single-select and multi-select lists."""
304 ctrl
= event
.GetEventObject()
305 if isinstance(ctrl
, wx
.ListCtrl
):
306 n
, flags
= ctrl
.HitTest(event
.GetPosition())
308 if not ctrl
.GetItemState(n
, wx
.LIST_STATE_SELECTED
):
309 for i
in range(ctrl
.GetItemCount()):
310 ctrl
.SetItemState(i
, 0, SEL_FOC
)
311 #for i in getListCtrlSelection(ctrl, SEL_FOC):
312 # ctrl.SetItemState(i, 0, SEL_FOC)
313 ctrl
.SetItemState(n
, SEL_FOC
, SEL_FOC
)
316 def getListCtrlSelection(listctrl
, state
=wx
.LIST_STATE_SELECTED
):
317 """ Returns list of item indexes of given state (selected by defaults) """
321 idx
= listctrl
.GetNextItem(idx
, wx
.LIST_NEXT_ALL
, state
)
327 wxEVT_DOPOPUPMENU
= wx
.NewEventType()
328 EVT_DOPOPUPMENU
= wx
.PyEventBinder(wxEVT_DOPOPUPMENU
, 0)
331 class ListCtrlSelectionManagerMix
:
332 """Mixin that defines a platform independent selection policy
334 As selection single and multi-select list return the item index or a
335 list of item indexes respectively.
340 self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnLCSMRightDown
)
341 self
.Bind(EVT_DOPOPUPMENU
, self
.OnLCSMDoPopup
)
342 # self.Connect(-1, -1, self.wxEVT_DOPOPUPMENU, self.OnLCSMDoPopup)
345 def getPopupMenu(self
):
346 """ Override to implement dynamic menus (create) """
350 def setPopupMenu(self
, menu
):
351 """ Must be set for default behaviour """
355 def afterPopupMenu(self
, menu
):
356 """ Override to implement dynamic menus (destroy) """
360 def getSelection(self
):
361 res
= getListCtrlSelection(self
)
362 if self
.GetWindowStyleFlag() & wx
.LC_SINGLE_SEL
:
371 def OnLCSMRightDown(self
, event
):
372 selectBeforePopup(event
)
374 menu
= self
.getPopupMenu()
377 evt
.SetEventType(wxEVT_DOPOPUPMENU
)
379 evt
.pos
= event
.GetPosition()
380 wx
.PostEvent(self
, evt
)
383 def OnLCSMDoPopup(self
, event
):
384 self
.PopupMenu(event
.menu
, event
.pos
)
385 self
.afterPopupMenu(event
.menu
)
388 #----------------------------------------------------------------------------
389 #----------------------------------------------------------------------------
390 from bisect
import bisect
395 A mixin class that enables any text in any column of a
396 multi-column listctrl to be edited by clicking on the given row
397 and column. You close the text editor by hitting the ENTER key or
398 clicking somewhere else on the listctrl. You switch to the next
399 column by hiting TAB.
401 To use the mixin you have to include it in the class definition
402 and call the __init__ function::
404 class TestListCtrl(wx.ListCtrl, TextEditMixin):
405 def __init__(self, parent, ID, pos=wx.DefaultPosition,
406 size=wx.DefaultSize, style=0):
407 wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
408 TextEditMixin.__init__(self)
411 Authors: Steve Zatz, Pim Van Heuven (pim@think-wize.com)
414 editorBgColour
= wx
.Colour(255,255,175) # Yellow
415 editorFgColour
= wx
.Colour(0,0,0) # black
418 #editor = wx.TextCtrl(self, -1, pos=(-1,-1), size=(-1,-1),
419 # style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB \
423 self
.Bind(wx
.EVT_TEXT_ENTER
, self
.CloseEditor
)
424 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
425 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
)
426 self
.Bind(wx
.EVT_LIST_ITEM_SELECTED
, self
.OnItemSelected
)
429 def make_editor(self
, col_style
=wx
.LIST_FORMAT_LEFT
):
431 style
=wx
.TE_PROCESS_ENTER|wx
.TE_PROCESS_TAB|wx
.TE_RICH2
432 style |
= {wx
.LIST_FORMAT_LEFT
: wx
.TE_LEFT
,
433 wx
.LIST_FORMAT_RIGHT
: wx
.TE_RIGHT
,
434 wx
.LIST_FORMAT_CENTRE
: wx
.TE_CENTRE
437 editor
= wx
.TextCtrl(self
, -1, style
=style
)
438 editor
.SetBackgroundColour(self
.editorBgColour
)
439 editor
.SetForegroundColour(self
.editorFgColour
)
440 font
= self
.GetFont()
447 if hasattr(self
, 'editor'):
448 self
.editor
.Destroy()
451 self
.col_style
= col_style
452 self
.editor
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
453 self
.editor
.Bind(wx
.EVT_KILL_FOCUS
, self
.CloseEditor
)
456 def OnItemSelected(self
, evt
):
457 self
.curRow
= evt
.GetIndex()
461 def OnChar(self
, event
):
462 ''' Catch the TAB, Shift-TAB, cursor DOWN/UP key code
463 so we can open the editor at the next column (if any).'''
465 keycode
= event
.GetKeyCode()
466 if keycode
== wx
.WXK_TAB
and event
.ShiftDown():
468 if self
.curCol
-1 >= 0:
469 self
.OpenEditor(self
.curCol
-1, self
.curRow
)
471 elif keycode
== wx
.WXK_TAB
:
473 if self
.curCol
+1 < self
.GetColumnCount():
474 self
.OpenEditor(self
.curCol
+1, self
.curRow
)
476 elif keycode
== wx
.WXK_ESCAPE
:
479 elif keycode
== wx
.WXK_DOWN
:
481 if self
.curRow
+1 < self
.GetItemCount():
482 self
._SelectIndex
(self
.curRow
+1)
483 self
.OpenEditor(self
.curCol
, self
.curRow
)
485 elif keycode
== wx
.WXK_UP
:
488 self
._SelectIndex
(self
.curRow
-1)
489 self
.OpenEditor(self
.curCol
, self
.curRow
)
495 def OnLeftDown(self
, evt
=None):
496 ''' Examine the click and double
497 click events to see if a row has been click on twice. If so,
498 determine the current row and columnn and open the editor.'''
500 if self
.editor
.IsShown():
503 x
,y
= evt
.GetPosition()
504 row
,flags
= self
.HitTest((x
,y
))
506 if row
!= self
.curRow
: # self.curRow keeps track of the current row
510 # the following should really be done in the mixin's init but
511 # the wx.ListCtrl demo creates the columns after creating the
512 # ListCtrl (generally not a good idea) on the other hand,
513 # doing this here handles adjustable column widths
517 for n
in range(self
.GetColumnCount()):
518 loc
= loc
+ self
.GetColumnWidth(n
)
519 self
.col_locs
.append(loc
)
522 col
= bisect(self
.col_locs
, x
+self
.GetScrollPos(wx
.HORIZONTAL
)) - 1
523 self
.OpenEditor(col
, row
)
526 def OpenEditor(self
, col
, row
):
527 ''' Opens an editor at the current position. '''
529 # give the derived class a chance to Allow/Veto this edit.
530 evt
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
, self
.GetId())
531 evt
.m_itemIndex
= row
533 item
= self
.GetItem(row
, col
)
534 evt
.m_item
.SetId(item
.GetId())
535 evt
.m_item
.SetColumn(item
.GetColumn())
536 evt
.m_item
.SetData(item
.GetData())
537 evt
.m_item
.SetText(item
.GetText())
538 ret
= self
.GetEventHandler().ProcessEvent(evt
)
539 if ret
and not evt
.IsAllowed():
540 return # user code doesn't allow the edit.
542 if self
.GetColumn(col
).m_format
!= self
.col_style
:
543 self
.make_editor(self
.GetColumn(col
).m_format
)
545 x0
= self
.col_locs
[col
]
546 x1
= self
.col_locs
[col
+1] - x0
548 scrolloffset
= self
.GetScrollPos(wx
.HORIZONTAL
)
551 if x0
+x1
-scrolloffset
> self
.GetSize()[0]:
552 if wx
.Platform
== "__WXMSW__":
553 # don't start scrolling unless we really need to
554 offset
= x0
+x1
-self
.GetSize()[0]-scrolloffset
555 # scroll a bit more than what is minimum required
556 # so we don't have to scroll everytime the user presses TAB
557 # which is very tireing to the eye
558 addoffset
= self
.GetSize()[0]/4
559 # but be careful at the end of the list
560 if addoffset
+ scrolloffset
< self
.GetSize()[0]:
563 self
.ScrollList(offset
, 0)
564 scrolloffset
= self
.GetScrollPos(wx
.HORIZONTAL
)
566 # Since we can not programmatically scroll the ListCtrl
567 # close the editor so the user can scroll and open the editor
569 self
.editor
.SetValue(self
.GetItem(row
, col
).GetText())
575 y0
= self
.GetItemRect(row
)[1]
578 editor
.SetDimensions(x0
-scrolloffset
,y0
, x1
,-1)
580 editor
.SetValue(self
.GetItem(row
, col
).GetText())
583 editor
.SetSelection(-1,-1)
590 # FIXME: this function is usually called twice - second time because
591 # it is binded to wx.EVT_KILL_FOCUS. Can it be avoided? (MW)
592 def CloseEditor(self
, evt
=None):
593 ''' Close the editor and save the new value to the ListCtrl. '''
594 if not self
.editor
.IsShown():
596 text
= self
.editor
.GetValue()
600 # post wxEVT_COMMAND_LIST_END_LABEL_EDIT
601 # Event can be vetoed. It doesn't has SetEditCanceled(), what would
602 # require passing extra argument to CloseEditor()
603 evt
= wx
.ListEvent(wx
.wxEVT_COMMAND_LIST_END_LABEL_EDIT
, self
.GetId())
604 evt
.m_itemIndex
= self
.curRow
605 evt
.m_col
= self
.curCol
606 item
= self
.GetItem(self
.curRow
, self
.curCol
)
607 evt
.m_item
.SetId(item
.GetId())
608 evt
.m_item
.SetColumn(item
.GetColumn())
609 evt
.m_item
.SetData(item
.GetData())
610 evt
.m_item
.SetText(text
) #should be empty string if editor was canceled
611 ret
= self
.GetEventHandler().ProcessEvent(evt
)
612 if not ret
or evt
.IsAllowed():
614 # replace by whather you use to populate the virtual ListCtrl
616 self
.SetVirtualData(self
.curRow
, self
.curCol
, text
)
618 self
.SetStringItem(self
.curRow
, self
.curCol
, text
)
619 self
.RefreshItem(self
.curRow
)
621 def _SelectIndex(self
, row
):
622 listlen
= self
.GetItemCount()
623 if row
< 0 and not listlen
:
625 if row
> (listlen
-1):
628 self
.SetItemState(self
.curRow
, ~wx
.LIST_STATE_SELECTED
,
629 wx
.LIST_STATE_SELECTED
)
630 self
.EnsureVisible(row
)
631 self
.SetItemState(row
, wx
.LIST_STATE_SELECTED
,
632 wx
.LIST_STATE_SELECTED
)
636 #----------------------------------------------------------------------------
637 #----------------------------------------------------------------------------
640 FILENAME: CheckListCtrlMixin.py
641 AUTHOR: Bruce Who (bruce.who.hk at gmail.com)
645 This script provide a mixin for ListCtrl which add a checkbox in the first
646 column of each row. It is inspired by limodou's CheckList.py(which can be
647 got from his NewEdit) and improved:
648 - You can just use InsertStringItem() to insert new items;
649 - Once a checkbox is checked/unchecked, the corresponding item is not
651 - You can use SetItemData() and GetItemData();
652 - Interfaces are changed to OnCheckItem(), IsChecked(), CheckItem().
654 You should not set a imagelist for the ListCtrl once this mixin is used.
657 1.3 - You can check/uncheck a group of sequential items by <Shift-click>:
658 First click(or <Shift-Click>) item1 to check/uncheck it, then
659 Shift-click item2 to check/uncheck it, and you'll find that all
660 items between item1 and item2 are check/unchecked!
661 1.2 - Add ToggleItem()
662 1.1 - Initial version
665 from wx
import ImageFromStream
, BitmapFromImage
666 import cStringIO
, zlib
668 def getUncheckData():
669 return zlib
.decompress(
670 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
671 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xbb{\xba8\x86X\xf4\
672 &\xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
673 \x90'\x01\x08v\xec]\xd3\xa3qvU`l\x81\xd9\xd18\t\xd3\x84+\x0cll[\xa6t\xcc9\
674 \xd4\xc1\xda\xc3<O\x9a1\xc3\x88\xc3j\xfa\x86_\xee@#\x19<]\xfd\\\xd69%4\x01\
677 def getUncheckBitmap():
678 return BitmapFromImage(getUncheckImage())
680 def getUncheckImage():
681 stream
= cStringIO
.StringIO(getUncheckData())
682 return ImageFromStream(stream
)
685 return zlib
.decompress(
686 'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
687 \x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe47{\xba8\x86X\xf4&\
688 \xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
689 \x90\'\x01\x08v\xec\\2C\xe3\xec+\xc3\xbd\x05fG\xe3\x14n1\xcc5\xad\x8a8\x1a\
690 \xb9\xa1\xeb\xd1\x853-\xaa\xc76\xecb\xb8i\x16c&\\\xc2\xb8\xe9Xvbx\xa1T\xc3U\
691 \xd6p\'\xbd\x85\x19\xff\xbe\xbf\xd7\xe7R\xcb`\xd8\xa5\xf8\x83\xe1^\xc4\x0e\
692 \xa1"\xce\xc3n\x93x\x14\xd8\x16\xb0(\x15q)\x8b\x19\xf0U\xe4\xb10\x08V\xa8\
693 \x99\xf3\xdd\xde\xad\x06t\x0e\x83\xa7\xab\x9f\xcb:\xa7\x84&\x00\xe0HE\xab' )
695 def getCheckBitmap():
696 return BitmapFromImage(getCheckImage())
699 stream
= cStringIO
.StringIO(getCheckData())
700 return ImageFromStream(stream
)
704 class CheckListCtrlMixin
:
706 This is a mixin for ListCtrl which add a checkbox in the first
707 column of each row. It is inspired by limodou's CheckList.py(which
708 can be got from his NewEdit) and improved:
710 - You can just use InsertStringItem() to insert new items;
712 - Once a checkbox is checked/unchecked, the corresponding item
715 - You can use SetItemData() and GetItemData();
717 - Interfaces are changed to OnCheckItem(), IsChecked(),
720 You should not set a imagelist for the ListCtrl once this mixin is used.
722 def __init__(self
, check_image
=None, uncheck_image
=None):
723 self
.__imagelist
_ = wx
.ImageList(16, 16)
725 check_image
= getCheckBitmap()
726 if not uncheck_image
:
727 uncheck_image
= getUncheckBitmap()
728 self
.uncheck_image
= self
.__imagelist
_.Add(uncheck_image
)
729 self
.check_image
= self
.__imagelist
_.Add(check_image
)
730 self
.SetImageList(self
.__imagelist
_, wx
.IMAGE_LIST_SMALL
)
731 self
.__last
_check
_ = None
733 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.__OnLeftDown
_)
735 # override the default methods of ListCtrl/ListView
736 self
.InsertStringItem
= self
.__InsertStringItem
_
738 # NOTE: if you use InsertItem, InsertImageItem or InsertImageStringItem,
739 # you must set the image yourself.
740 def __InsertStringItem_(self
, index
, label
):
741 index
= self
.InsertImageStringItem(index
, label
, 0)
744 def __OnLeftDown_(self
, evt
):
745 (index
, flags
) = self
.HitTest(evt
.GetPosition())
746 if flags
== wx
.LIST_HITTEST_ONITEMICON
:
747 img_idx
= self
.GetItem(index
).GetImage()
748 flag_check
= img_idx
== 0
751 if self
.__last
_check
_ is not None \
752 and wx
.GetKeyState(wx
.WXK_SHIFT
):
753 last_index
, last_flag_check
= self
.__last
_check
_
754 if last_flag_check
== flag_check
:
755 # XXX what if the previous item is deleted or new items
757 item_count
= self
.GetItemCount()
758 if last_index
< item_count
:
759 if last_index
< index
:
760 begin_index
= last_index
762 elif last_index
> index
:
764 end_index
= last_index
767 while begin_index
<= end_index
:
768 self
.CheckItem(begin_index
, flag_check
)
770 self
.__last
_check
_ = (index
, flag_check
)
774 def OnCheckItem(self
, index
, flag
):
777 def IsChecked(self
, index
):
778 return self
.GetItem(index
).GetImage() == 1
780 def CheckItem(self
, index
, check
= True):
781 img_idx
= self
.GetItem(index
).GetImage()
782 if img_idx
== 0 and check
is True:
783 self
.SetItemImage(index
, 1)
784 self
.OnCheckItem(index
, True)
785 elif img_idx
== 1 and check
is False:
786 self
.SetItemImage(index
, 0)
787 self
.OnCheckItem(index
, False)
789 def ToggleItem(self
, index
):
790 self
.CheckItem(index
, not self
.IsChecked(index
))
793 #----------------------------------------------------------------------------