Size: 39367
Comment:
|
Size: 39389
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 5: | Line 5: |
Selecting and manipulating items in wxListCtrls (using SetItem* methods) Report-view List controls "why don't my items show up" (missing headers) {{{self.list.InsertColumn( 0, "Items", width=-1)}}} | Selecting and manipulating items in wx.ListCtrls (using SetItem* methods) Report-view List controls "why don't my items show up" (missing headers) {{{self.list.InsertColumn( 0, "Items", width=-1)}}} |
Line 15: | Line 15: |
== Associating Python Data with Items == | == Associating Python Data with Items (Phoenix) == |
Line 19: | Line 19: |
listctrl.SetItemData( item_by_index, integer_for_item ) | listctrl.SetItemData(item_by_index, integer_for_item) |
Line 23: | Line 23: |
To associate more, make a num generator (or use wxNew`Id) and keep a dict binding id to whatever data you want to associate. | To associate more, make a num generator (or use wx.NewIdRef) and keep a dict binding id to whatever data you want to associate. |
Line 28: | Line 28: |
id = wxNewId() data_dict[ id ] = some_data_to_associate listctrl.InsertStringItem( index_to_place_at, string_name_of_item ) listctrl.SetItemData( index_to_place_at, id ) |
id = wx.NewIdRef() data_dict[id] = some_data_to_associate listctrl.InsertItem(index_to_place_at, string_name_of_item) listctrl.SetItemData(index_to_place_at, id) |
Line 79: | Line 79: |
To select an item programmatically in a wxListCtrl (Note that this appears to be a bug? Shouldn't need to use wxLIST_STATE_SELECTED for stateMask argument, should be able to use a MASK argument instead?): . {{{self.SetItemState ( ID, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED )}}} It should be (I think): . {{{self.SetItemState( ID, wxLIST_STATE_SELECTED, wxLIST_MASK_STATE )}}} ''Note: I just tried both versions, and the first version works in wxPython 2.5; the second version (using LIST_MASK_STATE) does not.'' |
To select an item programmatically in a wx.ListCtrl (note that this appears to be a bug ? Shouldn't need to use wx.LIST_STATE_SELECTED for stateMask argument, should be able to use a MASK argument instead ?) : . {{{self.SetItemState(ID, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)}}} It should be (I think) : . {{{self.SetItemState(ID, wx.LIST_STATE_SELECTED, wx.LIST_MASK_STATE)}}} ''Note : I just tried both versions, and the first version works in wxPython 2.5 ; the second version (using LIST_MASK_STATE) does not.'' |
Line 95: | Line 95: |
def _getSelectedIndices( self, state = wxLIST_STATE_SELECTED): | def _getSelectedIndices(self, state = wx.LIST_STATE_SELECTED): |
Line 111: | Line 111: |
== wxListCtrl's Hello world == It took half an hour for a newbie like me to get this tiny application running. Until I saw that you must call "InsertStringItem" :-) I was trying to use InsertItem. |
== wx.ListCtrl's Hello world (Phoenix) == It took half an hour for a newbie like me to get this tiny application running. Until I saw that you must call "[[InsertStringItem|InsertItem]]" :-) . |
Line 120: | Line 120: |
id=wx.NewId() self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER) |
id = wx.NewIdRef() self.list = wx.ListCtrl(frame, id, style=wx.LC_REPORT|wx.SUNKEN_BORDER) |
Line 124: | Line 124: |
self.list.InsertColumn(0,"Data #1") self.list.InsertColumn(1,"Data #2") self.list.InsertColumn(2,"Data #3") |
self.list.InsertColumn(0, "Data #1") self.list.InsertColumn(1, "Data #2") self.list.InsertColumn(2, "Data #3") |
Line 129: | Line 129: |
pos = self.list.InsertStringItem(0,"hello") # add values in the other columns on the same row self.list.SetStringItem(pos,1,"world") self.list.SetStringItem(pos,2,"!") |
pos = self.list.InsertItem(0, "hello") # Add values in the other columns on the same row self.list.SetItem(pos, 1, "world") self.list.SetItem(pos, 2, "!") |
Line 141: | Line 141: |
== Changing fonts in a ListCtrl == | == Changing fonts in a ListCtrl (Phoenix) == |
Line 153: | Line 153: |
class FancyListCtrl( wx.ListCtrl ): def __init__( self, parent, id ): wx.ListCtrl.__init__( self, parent, id, style = wx.LC_REPORT|wx.LC_VIRTUAL ) |
class FancyListCtrl(wx.ListCtrl): def __init__(self, parent, id): wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL) |
Line 159: | Line 159: |
# We can set the text colour to be different: self._funky_attr.SetTextColour( wx.BLUE ) # But we can't do either of: # self._funky_attr.GetFont().SetWeight( wx.BOLD ) # (Above fails silently - probably GetFont() returns a copy.) |
# We can set the text colour to be different : self._funky_attr.SetTextColour(wx.BLUE) # But we can't do either of : # self._funky_attr.GetFont().SetWeight(wx.BOLD) # (Above fails silently - probably GetFont() returns a copy). |
Line 165: | Line 165: |
# (No default constructor in wxPython.) # But we can extract the default font: |
# (No default constructor in wxPython). # But we can extract the default font : |
Line 168: | Line 168: |
# Set the weight on it: new_font.SetWeight( wx.BOLD ) |
# Set the weight on it : new_font.SetWeight(wx.BOLD) |
Line 171: | Line 171: |
self._funky_attr.SetFont( new_font ) | self._funky_attr.SetFont(new_font) |
Line 173: | Line 173: |
# (since this happens to be virtual.) }}} Simply changing the font of a specific item: {{{#!python # Get the item at a specific index: |
# (since this happens to be virtual). }}} Simply changing the font of a specific item : {{{#!python # Get the item at a specific index : |
Line 180: | Line 180: |
# Get its font, change it, and put it back: | # Get its font, change it, and put it back : |
Line 187: | Line 187: |
== Unselecting an item programmatically == | == Unselecting an item programmatically (Phoenix) == |
Line 191: | Line 191: |
# selecting as shown in the demo self.SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED) # unselecting self.SetItemState(item, 0, wxLIST_STATE_SELECTED) |
# Selecting as shown in the demo self.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) # Unselecting self.SetItemState(item, 0, wx.LIST_STATE_SELECTED) |
Line 849: | Line 849: |
This striped drag list allows for dragging and dropping within itself while maintaining its appearance. While not used in the example stub, it'll also stripe itself on an insert event (like from a pop-up file dialog) or on a delete event. | This striped drag list allows for dragging and dropping within itself while maintaining its appearance. While not used in the example stub, it'll also stripe itself on an insert event (like from a pop-up file dialog) or on a delete event. |
List Controls
Basic operation
- Associating python data with items
Selecting and manipulating items in wx.ListCtrls (using SetItem* methods) Report-view List controls "why don't my items show up" (missing headers) self.list.InsertColumn( 0, "Items", width=-1)
See Also:
Contents
-
List Controls
- Associating Python Data with Items (Phoenix)
- Python Data Mixin
- Selecting Items Programmatically
- Retrieve Currently Selected Indices in List Control
- wx.ListCtrl's Hello world (Phoenix)
- Changing fonts in a ListCtrl (Phoenix)
- Unselecting an item programmatically (Phoenix)
- Type Ahead Mixin
- Customizing ColumnSorterMixin to Sort Dates
- Drag and Drop with lists - 1
- Drag and Drop with lists - 2 (Phoenix)
- Drag and Drop with a striped drag list (Phoenix)
Associating Python Data with Items (Phoenix)
When you need to associate data with an item:
1 listctrl.SetItemData(item_by_index, integer_for_item)
Yep! You get to associate a WHOLE integer with the item!
To associate more, make a num generator (or use wx.NewIdRef) and keep a dict binding id to whatever data you want to associate.
So, for example:
1 id = wx.NewIdRef()
2 data_dict[id] = some_data_to_associate
3 listctrl.InsertItem(index_to_place_at, string_name_of_item)
4 listctrl.SetItemData(index_to_place_at, id)
So, now you can traverse:
index --> associated data (id) --> some_data_to_associate
Python Data Mixin
I'm surprised I haven't seen one of these yet.
1 class ListCtrlPyDataMixin(object):
2 def __init__(self):
3 import sys
4
5 self.id = -sys.maxint
6 self.map = {}
7
8 self.Bind(wx.EVT_LIST_DELETE_ITEM, self.OnDeleteItem)
9 self.Bind(wx.EVT_LIST_DELETE_ALL_ITEMS, self.OnDeleteAllItems)
10
11 def SetPyData(self, item, data):
12 self.map[self.id] = data
13 self.SetItemData(item, self.id)
14 self.id += 1
15
16 def SortPyItems(self, fn):
17 from functools import wraps
18
19 @wraps(fn)
20 def wrapper(a, b):
21 return fn(self.map[a], self.map[b])
22
23 self.SortItems(wrapper)
24
25 def OnDeleteItem(self, event):
26 try:
27 del self.map[event.Data]
28 except KeyError:
29 pass
30 event.Skip()
31
32 def OnDeleteAllItems(self, event):
33 self.map.clear()
34 event.Skip()
Have fun. -- ChristopherMonsanto
Selecting Items Programmatically
To select an item programmatically in a wx.ListCtrl (note that this appears to be a bug ? Shouldn't need to use wx.LIST_STATE_SELECTED for stateMask argument, should be able to use a MASK argument instead ?) :
self.SetItemState(ID, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
It should be (I think) :
self.SetItemState(ID, wx.LIST_STATE_SELECTED, wx.LIST_MASK_STATE)
Note : I just tried both versions, and the first version works in wxPython 2.5 ; the second version (using LIST_MASK_STATE) does not.
I think only the first version is correct. The last parameter is a mask of all the states you want to modify, so it must consist only of wx.LIST_STATE_* combinations. The wx.LIST_MASK_STATE is only used in SetItem, where it tells which fields are valid. Seems the docs could be clearer though.
Retrieve Currently Selected Indices in List Control
A common task, there's no pre-built function to accomplish this, so here's a recipe:
1 def _getSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
2 indices = []
3 lastFound = -1
4 while True:
5 index = self.GetNextItem(
6 lastFound,
7 wxLIST_NEXT_ALL,
8 state,
9 )
10 if index == -1:
11 break
12 else:
13 lastFound = index
14 indices.append( index )
15 return indices
wx.ListCtrl's Hello world (Phoenix)
It took half an hour for a newbie like me to get this tiny application running. Until I saw that you must call "InsertItem" .
1 import wx
2 class MyApp(wx.App):
3 def OnInit(self):
4 frame = wx.Frame(None, -1, "Hello from wxPython")
5
6 id = wx.NewIdRef()
7 self.list = wx.ListCtrl(frame, id, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
8 self.list.Show(True)
9
10 self.list.InsertColumn(0, "Data #1")
11 self.list.InsertColumn(1, "Data #2")
12 self.list.InsertColumn(2, "Data #3")
13
14 # 0 will insert at the start of the list
15 pos = self.list.InsertItem(0, "hello")
16 # Add values in the other columns on the same row
17 self.list.SetItem(pos, 1, "world")
18 self.list.SetItem(pos, 2, "!")
19
20 frame.Show(True)
21 self.SetTopWindow(frame)
22 return True
23
24 app = MyApp(0)
25 app.MainLoop()
Changing fonts in a ListCtrl (Phoenix)
Supposing you have a List Control, and you want to set some items to, say, bold. That's all - no changing anything else about the font, so the face remains the same as the other items. (Bear in mind the face will most likely be the default face as set by the system, and hence we don't know what it is.)
Because I (Dave Cridland) am terminally stupid, I didn't figure out how to do this, and couldn't find any documentation. In case anyone else is trying to do the same thing, and can't figure it out either, here's what I did:
1 import wx
2 # I'm using the new namespace. Hoorah!
3
4 # Note this is very much incomplete, and only dwells on how to set the font stuff in an attr.
5
6 class FancyListCtrl(wx.ListCtrl):
7 def __init__(self, parent, id):
8 wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL)
9 # We setup our pretty attribute once, here.
10 self._funky_attr = wx.ListItemAttr()
11 # This now contains default attributes.
12 # We can set the text colour to be different :
13 self._funky_attr.SetTextColour(wx.BLUE)
14 # But we can't do either of :
15 # self._funky_attr.GetFont().SetWeight(wx.BOLD)
16 # (Above fails silently - probably GetFont() returns a copy).
17 # new_font = wx.Font()
18 # (No default constructor in wxPython).
19 # But we can extract the default font :
20 new_font = self._funky_attr.GetFont()
21 # Set the weight on it :
22 new_font.SetWeight(wx.BOLD)
23 # Then put it back.
24 self._funky_attr.SetFont(new_font)
25 # self._funky_attr is now ready to be returned by OnGetItemAttr()
26 # (since this happens to be virtual).
Simply changing the font of a specific item :
Unselecting an item programmatically (Phoenix)
It took me (Werner Bruhin) some searching, as always when one figures it out it is easy!
Type Ahead Mixin
Windows (and presumably other OS do as well) provides Type Ahead functionality by default in list controls. But not if they are Virtual. I had a need to provide Type Ahead for some virtual controls in my app, so I knocked up this Mixin class for wxListCtrl objects. Happily it works with both Virtual and Normal list controls which means if you use it in your app then all your list controls can have the same Type Ahead behaviour.
There are some things I've tried to make configurable; the type ahead timeout, case sensitivity to the search, special keycodes to avoid during typeahead, and I've provided methods that you could override to change the default behaviour that I've coded (it was almost on a whim as I didn't really like the behaviour that Windows was providing, it seemed, weird). To help understand what's going on I've tried to document the code to explain the bits I think are weird.
One thing that is notable is that the default windows type ahead doesn't seem to recognise the space character, with some playing about with EVT_KEY_DOWN and EVT_CHAR events I've made my type ahead deal with spaces. Which is nice.
Anyway, on with the code (please excuse the verbosity of my variable and method names ).
1 from wxPython.wx import *
2
3 class TypeAheadListCtrlMixin:
4 """ A Mixin Class that does type ahead scrolling for list controls.
5 Assumes that the wxListCtrl it is mixed into is sorted (it's using
6 a binary search to find the correct item).
7
8 I wrote this because on Windows there was no type ahead when you
9 used a virtual list control, and then couldn't be bohered deciphering
10 the default windows typeahead for non-virtual list controls. So I
11 made it work for both virtual and non-virtual controls so that all
12 my list controls would have the same functionality.
13
14 Things you can change programatically:
15 * expand or contract the list of keycodes that stop the type ahead
16 * expand or contract the list of keycodes that are allowed to be
17 used inside typeahead, but won't start it (e.g. space which normally
18 acts as an Activation Key)
19 * change the timeout time (init param, defaults to 500 milliseconds)
20 * change the sensitivity of the search (init param, defaults to caseinsensitive)
21 Things you can change in the class that you mix this into:
22 * override the following methods:
23 - currentlySelectedItemFoundByTypeAhead
24 - currentlySelectedItemNotFoundByTypeAhead
25 - newItemFoundByTypeAhead
26 - nothingFoundByTypeAhead
27 changing these changes the behaviour of the typeahead in various stages.
28 See doc comments on methods.
29
30 Written by Murray Steele (muz at h-lame dot com)
31 """
32
33 # These Keycodes are the ones that if we detect them we will cancel the current
34 # typeahead state.
35 stopTypeAheadKeyCodes = [
36 WXK_BACK,
37 WXK_TAB,
38 WXK_RETURN,
39 WXK_ESCAPE,
40 WXK_DELETE,
41 WXK_START,
42 WXK_LBUTTON,
43 WXK_RBUTTON,
44 WXK_CANCEL,
45 WXK_MBUTTON,
46 WXK_CLEAR,
47 WXK_PAUSE,
48 WXK_CAPITAL,
49 WXK_PRIOR,
50 WXK_NEXT,
51 WXK_END,
52 WXK_HOME,
53 WXK_LEFT,
54 WXK_UP,
55 WXK_RIGHT,
56 WXK_DOWN,
57 WXK_SELECT,
58 WXK_PRINT,
59 WXK_EXECUTE,
60 WXK_SNAPSHOT,
61 WXK_INSERT,
62 WXK_HELP,
63 WXK_F1,
64 WXK_F2,
65 WXK_F3,
66 WXK_F4,
67 WXK_F5,
68 WXK_F6,
69 WXK_F7,
70 WXK_F8,
71 WXK_F9,
72 WXK_F10,
73 WXK_F11,
74 WXK_F12,
75 WXK_F13,
76 WXK_F14,
77 WXK_F15,
78 WXK_F16,
79 WXK_F17,
80 WXK_F18,
81 WXK_F19,
82 WXK_F20,
83 WXK_F21,
84 WXK_F22,
85 WXK_F23,
86 WXK_F24,
87 WXK_NUMLOCK,
88 WXK_SCROLL]
89 # These are the keycodes that we have to catch in evt_key_down, not evt_char
90 # By the time they get to evt_char then the OS has looked at them and gone,
91 # hey, this keypress means do something (like pressing space acts as an ACTIVATE
92 # key in a list control.
93 catchInKeyDownIfDuringTypeAheadKeyCodes = [
94 WXK_SPACE
95 ]
96 # These are the keycodes that we will allow during typeahead, but won't allow to start
97 # the type ahead process.
98 dontStartTypeAheadKeyCodes = [
99 WXK_SHIFT,
100 WXK_CONTROL,
101 WXK_MENU #ALT Key, ALT Gr generates both WXK_CONTROL and WXK_MENU.
102 ]
103 dontStartTypeAheadKeyCodes.extend(catchInKeyDownIfDuringTypeAheadKeyCodes)
104
105 def __init__(self, typeAheadTimeout = 500, casesensitive = False, columnToSearch = 0):
106 # Do most work in the char handler instead of keydown.
107 # This means we get the correct keycode for the key pressed as it should
108 # appear on screen, rather than all uppercase or "default" us keyboard
109 # punctuation.
110 # However there are things that we need to catch in key_down to stop
111 # them getting sent to the underlying windows control and generating
112 # other events (notably I'm talking about the SPACE key which generates
113 # an ACTIVATE event in these list controls).
114 EVT_KEY_DOWN(self, self.OnTypeAheadKeyDown)
115 EVT_CHAR(self, self.OnTypeAheadChar)
116 timerId = wxNewId()
117 self.typeAheadTimer = wxTimer(self,timerId)
118 EVT_TIMER(self,timerId,self.OnTypeAheadTimer)
119 self.clearTypeAhead()
120 self.typeAheadTimeout = typeAheadTimeout
121 self.columnToSearch = columnToSearch
122 if not casesensitive:
123 self._GetItemText = lambda idx: self.GetItem(idx,self.columnToSearch).GetText().lower()
124 self._GetKeyCode = lambda keycode: chr(keycode).lower()
125 else:
126 self._GetItemText = lambda idx: self.GetItem(idx,self.columnToSearch).GetText()
127 self._GetKeyCode = chr
128
129 def OnTypeAheadKeyDown(self, event):
130 keycode = event.GetKeyCode()
131 if keycode in self.stopTypeAheadKeyCodes:
132 self.clearTypeAhead()
133 elif self.typeAhead == None:
134 if keycode in self.dontStartTypeAheadKeyCodes:
135 self.clearTypeAhead()
136 else:
137 if keycode in self.catchInKeyDownIfDuringTypeAheadKeyCodes:
138 self.OnTypeAheadChar(event)
139 return
140 event.Skip()
141
142 def OnTypeAheadChar(self, event):
143 # stop the timer, to make sure that it doesn't fire in the middle of
144 # doing this and screw up by None-ifying the typeAhead string.
145 # TODO: Yes some kind of lock around a typeAheadState object
146 # that contained typeAhead, lastTypeAheadFoundAnything and lastTypeAhead
147 # would be better...
148 self.typeAheadTimer.Stop()
149 keycode = event.GetKeyCode()
150 if keycode in self.stopTypeAheadKeyCodes:
151 self.clearTypeAhead()
152 event.Skip()
153 return
154 else:
155 if self.typeAhead == None:
156 if keycode in self.dontStartTypeAheadKeyCodes:
157 self.clearTypeAhead()
158 event.Skip()
159 return
160 else:
161 self.typeAhead = self._GetKeyCode(keycode)
162 else:
163 self.typeAhead += self._GetKeyCode(keycode)
164 self.doTypeAhead()
165 # This timer is used to nullify the typeahead after a while
166 self.typeAheadTimer.Start(self.typeAheadTimeout,wxTIMER_ONE_SHOT)
167
168 def inTypeAhead(self):
169 return self.typeAhead != None
170
171 def currentlySelectedItemFoundByTypeAhead(self, idx):
172 """This method is called when the typeahead string matches
173 the text of the currently selected item. Put code here if
174 you want to have something happen in this case.
175 NOTE: Method only called if there was a currently selected item.
176
177 idx refers to the index of the currently selected item."""
178 # we don't do anything as we've already selected the thing we want
179 pass
180
181 def currentlySelectedItemNotFoundByTypeAhead(self, idx):
182 """This method is called when the typeahead string matches
183 an item that isn't the currently selected one. Put code
184 here if you want something to happen to the currently
185 selected item.
186 NOTE: use newItemFoundByTypeAhead for doing something to
187 the newly matched item.
188 NOTE: Method only called if there was a currently selected item.
189
190 idx referes to the index of the currently selected item."""
191 # we deselect it.
192 self.SetItemState(idx, 0, wxLIST_STATE_SELECTED)
193 self.SetItemState(idx, 0, wxLIST_STATE_FOCUSED)
194
195 def newItemFoundByTypeAhead(self, idx):
196 """This is called when the typeahead string matches
197 an item that isn't the currently selected one. Put
198 code here if you want something to happen to the newly
199 found item.
200 NOTE: use currentlySelectedItemNotFoundByTypeAhead for
201 doing something to the previously selected item.
202
203 idx refers to the index of the newly matched item."""
204 # we select it and make sure it is focused
205 self.SetItemState(idx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED)
206 self.SetItemState(idx, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED)
207 self.EnsureVisible(idx)
208
209 def nothingFoundByTypeAhead(self, idx):
210 """This method is called when the typeahead string doesn't
211 match any items. Put code here if you want something to
212 happen in this case.
213
214 idx refers to the index of the currently selected item
215 or -1 if nothing was selected."""
216 # don't do anything here, what could we do?
217 pass
218
219 def doTypeAhead(self):
220 curselected = -1
221 if self.lastTypeAheadFoundSomething:
222 curselected = self.lastTypeAhead
223 else:
224 curselected = self.GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)
225 min = 0
226 if curselected != -1:
227 term_name = self._GetItemText(curselected)
228 if term_name.startswith(self.typeAhead):
229 self.currentlySelectedItemFoundByTypeAhead(curselected)
230 self.lastTypeAheadFoundAnything = True
231 self.lastTypeAhead = curselected
232 return #We don't want this edgecase falling through
233
234 new_idx = self.binary_search(self.typeAhead,min)
235 if new_idx != -1:
236 if new_idx != curselected and curselected != -1:
237 self.currentlySelectedItemNotFoundByTypeAhead(curselected)
238 self.newItemFoundByTypeAhead(new_idx)
239 self.lastTypeAheadFoundAnything = True
240 self.lastTypeAhead = new_idx
241 else:
242 self.nothingFoundByTypeAhead(curselected)
243 self.lastTypeAheadFoundAnything = False
244 self.lastTypeAhead = -1
245
246 # NOTE: Originally from ASPN. Augmented.
247 def binary_search(self, t, min = 0):
248 min = min;
249 max = self.GetItemCount() - 1
250 while True:
251 if max < min:
252 return self.doEdgeCase(m, t)
253 m = (min + max) / 2
254 cur_term = self._GetItemText(m)
255 if cur_term < t:
256 min = m + 1
257 elif cur_term > t:
258 max = m - 1
259 else:
260 return m
261
262 def doEdgeCase(self, m, t):
263 """ This method makes sure that if we don't find the typeahead
264 as an actual string, then we will return the first item
265 that starts with the typeahead string (if there is one)"""
266 before = self._GetItemText(max(0,m-1))
267 this = self._GetItemText(m)
268 after = self._GetItemText(min(self.GetItemCount()-1,m+1))
269 if this.startswith(t):
270 return m
271 elif before.startswith(t):
272 return max(0,m-1)
273 elif after.startswith(t):
274 return min(self.GetItemCount()-1,m+1)
275 else:
276 return -1
277
278 def clearTypeAhead(self):
279 self.typeAhead = None
280 self.lastTypeAheadFoundSomething = False
281 self.lastTypeAheadIdx = -1
282
283 def OnTypeAheadTimer(self, event):
284 self.clearTypeAhead()
Customizing ColumnSorterMixin to Sort Dates
This code augments wx.lib.mixins.listctrl.ColumnSorterMixin so that it can sort dates that match the date_re regular expression listed at the top of the code. You need to modify another section of the code to transform your date into the YYYYMMDD format so that the dates can be easily sorted numerically. Alternatively, you could use the datetime module and store and sort date objects.
1 import wx
2 import wx.lib.mixins.listctrl
3 import locale
4 import re
5
6 # Change this to your settings
7 date_re = re.compile("(\d{2})-(\d{2})-(\d{4})")
8
9 #----------------------------------------------------------------------------
10
11 class CustColumnSorterMixin(wx.lib.mixins.listctrl.ColumnSorterMixin):
12 def __init__(self, numColumns):
13 wx.lib.mixins.listctrl.ColumnSorterMixin(self, numColumns)
14
15 def GetColumnSorter(self):
16 return self.CustColumnSorter
17
18 def CustColumnSorter(self, key1, key2):
19 col = self._col
20 ascending = self._colSortFlag[col]
21 item1 = self.itemDataMap[key1][col]
22 item2 = self.itemDataMap[key2][col]
23
24 alpha = date_re.match(item1)
25 beta = date_re.match(item2)
26 if alpha and beta:
27 # Change these from your settings to YYYYMMDD
28 item1 = alpha.group(3)+alpha.group(1)+alpha.group(2)
29 item2 = beta.group(3)+ beta.group(1)+ beta.group(2)
30
31 item1 = int(item1)
32 item2 = int(item2)
33
34 #--- Internationalization of string sorting with locale module
35 if type(item1) == type('') or type(item2) == type(''):
36 cmpVal = locale.strcoll(str(item1), str(item2))
37 else:
38 cmpVal = cmp(item1, item2)
39 #---
40
41 # If the items are equal then pick something else to make the sort value unique
42 if cmpVal == 0:
43 cmpVal = cmp(*self.GetSecondarySortValues(col, key1, key2))
44
45 if ascending:
46 return cmpVal
47 else:
48 return -cmpVal
Drag and Drop with lists - 1
Here's how you can use drag-and-drop to reorder a simple list, or to move items from one list to another.
As it stands, item data will be lost. Maybe in version 2! - JohnFouhy
1 """ DnD demo with listctrl. """
2
3 import wx
4
5 class DragList(wx.ListCtrl):
6 def __init__(self, *arg, **kw):
7 if 'style' in kw and (kw['style']&wx.LC_LIST or kw['style']&wx.LC_REPORT):
8 kw['style'] |= wx.LC_SINGLE_SEL
9 else:
10 kw['style'] = wx.LC_SINGLE_SEL|wx.LC_LIST
11
12 wx.ListCtrl.__init__(self, *arg, **kw)
13
14 self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._startDrag)
15
16 dt = ListDrop(self._insert)
17 self.SetDropTarget(dt)
18
19 def _startDrag(self, e):
20 """ Put together a data object for drag-and-drop _from_ this list. """
21
22 # Create the data object: Just use plain text.
23 data = wx.PyTextDataObject()
24 idx = e.GetIndex()
25 text = self.GetItem(idx).GetText()
26 data.SetText(text)
27
28 # Create drop source and begin drag-and-drop.
29 dropSource = wx.DropSource(self)
30 dropSource.SetData(data)
31 res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
32
33 # If move, we want to remove the item from this list.
34 if res == wx.DragMove:
35 # It's possible we are dragging/dropping from this list to this list. In which case, the
36 # index we are removing may have changed...
37
38 # Find correct position.
39 pos = self.FindItem(idx, text)
40 self.DeleteItem(pos)
41
42 def _insert(self, x, y, text):
43 """ Insert text at given x, y coordinates --- used with drag-and-drop. """
44
45 # Clean text.
46 import string
47 text = filter(lambda x: x in (string.letters + string.digits + string.punctuation + ' '), text)
48
49 # Find insertion point.
50 index, flags = self.HitTest((x, y))
51
52 if index == wx.NOT_FOUND:
53 if flags & wx.LIST_HITTEST_NOWHERE:
54 index = self.GetItemCount()
55 else:
56 return
57
58 # Get bounding rectangle for the item the user is dropping over.
59 rect = self.GetItemRect(index)
60
61 # If the user is dropping into the lower half of the rect, we want to insert _after_ this item.
62 if y > rect.y + rect.height/2:
63 index += 1
64
65 self.InsertStringItem(index, text)
66
67 class ListDrop(wx.PyDropTarget):
68 """ Drop target for simple lists. """
69
70 def __init__(self, setFn):
71 """ Arguments:
72 - setFn: Function to call on drop.
73 """
74 wx.PyDropTarget.__init__(self)
75
76 self.setFn = setFn
77
78 # specify the type of data we will accept
79 self.data = wx.PyTextDataObject()
80 self.SetDataObject(self.data)
81
82 # Called when OnDrop returns True. We need to get the data and
83 # do something with it.
84 def OnData(self, x, y, d):
85 # copy the data from the drag source to our data object
86 if self.GetData():
87 self.setFn(x, y, self.data.GetText())
88
89 # what is returned signals the source what to do
90 # with the original data (move, copy, etc.) In this
91 # case we just return the suggested value given to us.
92 return d
93
94 if __name__ == '__main__':
95 items = ['Foo', 'Bar', 'Baz', 'Zif', 'Zaf', 'Zof']
96
97 class MyApp(wx.App):
98 def OnInit(self):
99 self.frame = wx.Frame(None, title='Main Frame')
100 self.frame.Show(True)
101 self.SetTopWindow(self.frame)
102 return True
103
104 app = MyApp(redirect=False)
105 dl1 = DragList(app.frame)
106 dl2 = DragList(app.frame)
107 sizer = wx.BoxSizer()
108 app.frame.SetSizer(sizer)
109 sizer.Add(dl1, proportion=1, flag=wx.EXPAND)
110 sizer.Add(dl2, proportion=1, flag=wx.EXPAND)
111 for item in items:
112 dl1.InsertStringItem(99, item)
113 dl2.InsertStringItem(99, item)
114 app.frame.Layout()
115 app.MainLoop()
Drag and Drop with lists - 2 (Phoenix)
1 # dragAndDropList.py
2
3 """
4
5 Drag and drop (DnD) demo with listctrl :
6 - Dragging of multiple selected items.
7 - Dropping on an empty list.
8 - Dropping of items on a list with a different number of columns.
9 - Dropping on a different applications.
10
11 """
12
13 import pickle
14 import sys
15 from random import choice
16 import wx
17
18 # class MyDragList
19 # class MyListDrop
20 # class MyApp
21
22 #-------------------------------------------------------------------------------
23
24 class MyDragList(wx.ListCtrl):
25 def __init__(self, *arg, **kw):
26 wx.ListCtrl.__init__(self, *arg, **kw)
27
28 self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._startDrag)
29
30 dt = MyListDrop(self)
31 self.SetDropTarget(dt)
32
33 def getItemInfo(self, idx):
34 """Collect all relevant data of a listitem, and put it in a list"""
35 l = []
36 l.append(idx) # We need the original index, so it is easier to eventualy delete it
37 l.append(self.GetItemData(idx)) # Itemdata
38 l.append(self.GetItemText(idx)) # Text first column
39 for i in range(1, self.GetColumnCount()): # Possible extra columns
40 l.append(self.GetItem(idx, i).GetText())
41 return l
42
43 def _startDrag(self, e):
44 """ Put together a data object for drag-and-drop _from_ this list. """
45 l = []
46 idx = -1
47 while True: # Find all the selected items and put them in a list
48 idx = self.GetNextItem(idx, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
49 if idx == -1:
50 break
51 l.append(self.getItemInfo(idx))
52
53 # Pickle the items list.
54 itemdata = pickle.dumps(l, 1)
55 # Create our own data format and use it in a
56 # Custom data object
57 ldata = wx.CustomDataObject("ListCtrlItems")
58 ldata.SetData(itemdata)
59 # Now make a data object for the item list.
60 data = wx.DataObjectComposite()
61 data.Add(ldata)
62
63 # Create drop source and begin drag-and-drop.
64 dropSource = wx.DropSource(self)
65 dropSource.SetData(data)
66 res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
67
68 # If move, we want to remove the item from this list.
69 if res == wx.DragMove:
70 # It's possible we are dragging/dropping from this list to this list.
71 # In which case, the index we are removing may have changed...
72
73 # Find correct position.
74 l.reverse() # Delete all the items, starting with the last item
75 for i in l:
76 pos = self.FindItem(i[0], i[2])
77 self.DeleteItem(pos)
78
79 def _insert(self, x, y, seq):
80 """ Insert text at given x, y coordinates --- used with drag-and-drop. """
81
82 # Find insertion point.
83 index, flags = self.HitTest((x, y))
84
85 if index == wx.NOT_FOUND: # Not clicked on an item
86 if flags & (wx.LIST_HITTEST_NOWHERE|wx.LIST_HITTEST_ABOVE|wx.LIST_HITTEST_BELOW): # Empty list or below last item
87 index = self.GetItemCount() # Append to end of list
88 elif self.GetItemCount() > 0:
89 if y <= self.GetItemRect(0).y: # Clicked just above first item
90 index = 0 # Append to top of list
91 else:
92 index = self.GetItemCount() + 1 # Append to end of list
93 else: # Clicked on an item
94 # Get bounding rectangle for the item the user is dropping over.
95 rect = self.GetItemRect(index)
96
97 # If the user is dropping into the lower half of the rect, we want to insert _after_ this item.
98 # Correct for the fact that there may be a heading involved
99 if y > rect.y - self.GetItemRect(0).y + rect.height/2:
100 index += 1
101
102 for i in seq: # Insert the item data
103 idx = self.InsertItem(index, i[2])
104 self.SetItemData(idx, i[1])
105 for j in range(1, self.GetColumnCount()):
106 try: # Target list can have more columns than source
107 self.SetItem(idx, j, i[2+j])
108 except:
109 pass # Ignore the extra columns
110 index += 1
111
112 #-------------------------------------------------------------------------------
113
114 class MyListDrop(wx.DropTarget):
115 """ Drop target for simple lists. """
116
117 def __init__(self, source):
118 """ Arguments:
119 - source: source listctrl.
120 """
121 wx.DropTarget.__init__(self)
122
123 self.dv = source
124
125 # Specify the type of data we will accept
126 self.data = wx.CustomDataObject("ListCtrlItems")
127 self.SetDataObject(self.data)
128
129 # Called when OnDrop returns True. We need to get the
130 # data and do something with it.
131 def OnData(self, x, y, d):
132 # Copy the data from the drag source to our data object
133 if self.GetData():
134 # Convert it back to a list and give it to the viewer
135 ldata = self.data.GetData()
136 l = pickle.loads(ldata)
137 self.dv._insert(x, y, l)
138
139 # What is returned signals the source what to do
140 # with the original data (move, copy, etc.) In this
141 # case we just return the suggested value given to us.
142 return d
143
144 #-------------------------------------------------------------------------------
145 # Main
146
147 if __name__ == '__main__':
148 items = ['Foo', 'Bar', 'Baz', 'Zif', 'Zaf', 'Zof']
149
150 #---------------------------------------------------------------------------
151
152 class MyApp(wx.App):
153 def OnInit(self):
154 self.frame = wx.Frame(None, title='Main Frame')
155 self.frame.Show(True)
156 self.SetTopWindow(self.frame)
157 return True
158
159 app = MyApp(redirect=False)
160 dl1 = MyDragList(app.frame, style=wx.LC_LIST)
161 dl2 = MyDragList(app.frame, style=wx.LC_REPORT)
162 dl2.InsertColumn(0, "Column #0", wx.LIST_FORMAT_LEFT)
163 dl2.InsertColumn(1, "Column #1", wx.LIST_FORMAT_RIGHT)
164 dl2.InsertColumn(2, "Column #2", wx.LIST_FORMAT_LEFT)
165 sizer = wx.BoxSizer()
166 app.frame.SetSizer(sizer)
167 sizer.Add(dl1, proportion=1, flag=wx.EXPAND)
168 sizer.Add(dl2, proportion=1, flag=wx.EXPAND)
169
170 maxs = -sys.maxsize - 1
171
172 for item in items:
173 dl1.InsertItem(maxs, item)
174 idx = dl2.InsertItem(maxs, item)
175 dl2.SetItem(idx, 1, choice(items))
176 dl2.SetItem(idx, 2, choice(items))
177 app.frame.Layout()
178 app.MainLoop()
Drag and Drop with a striped drag list (Phoenix)
This striped drag list allows for dragging and dropping within itself while maintaining its appearance.
While not used in the example stub, it'll also stripe itself on an insert event (like from a pop-up file dialog) or on a delete event.
1 # dragListStriped.py
2
3 import wx
4
5 # class MyDragList
6 # class MyApp
7
8 #-------------------------------------------------------------------------------
9
10 class MyDragListStriped(wx.ListCtrl):
11 def __init__(self, *arg, **kw):
12 wx.ListCtrl.__init__(self, *arg, **kw)
13
14 self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._onDrag)
15 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._onSelect)
16 self.Bind(wx.EVT_LEFT_UP,self._onMouseUp)
17 self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
18 self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow)
19 self.Bind(wx.EVT_ENTER_WINDOW, self._onEnterWindow)
20 self.Bind(wx.EVT_LIST_INSERT_ITEM, self._onInsert)
21 self.Bind(wx.EVT_LIST_DELETE_ITEM, self._onDelete)
22
23 #---------------
24 # Variables
25 #---------------
26 self.IsInControl = True
27 self.startIndex = -1
28 self.dropIndex = -1
29 self.IsDrag = False
30 self.dragIndex = -1
31
32 def _onLeaveWindow(self, event):
33 self.IsInControl = False
34 self.IsDrag = False
35 event.Skip()
36
37 def _onEnterWindow(self, event):
38 self.IsInControl = True
39 event.Skip()
40
41 def _onDrag(self, event):
42 self.IsDrag = True
43 self.dragIndex = event.Index
44 event.Skip()
45 pass
46
47 def _onSelect(self, event):
48 self.startIndex = event.Index
49 event.Skip()
50
51 def _onMouseUp(self, event):
52 # Purpose: to generate a dropIndex.
53 # Process: check self.IsInControl, check self.IsDrag, HitTest, compare HitTest value
54 # The mouse can end up in 5 different places:
55 # Outside the Control
56 # On itself
57 # Above its starting point and on another item
58 # Below its starting point and on another item
59 # Below its starting point and not on another item
60
61 if self.IsInControl == False: #1. Outside the control : Do Nothing
62 self.IsDrag = False
63 else: # In control but not a drag event : Do Nothing
64 if self.IsDrag == False:
65 pass
66 else: # In control and is a drag event : Determine Location
67 self.hitIndex = self.HitTest(event.GetPosition())
68 self.dropIndex = self.hitIndex[0]
69 # -- Drop index indicates where the drop location is; what index number
70 #---------
71 # Determine dropIndex and its validity
72 #--------
73 if self.dropIndex == self.startIndex or self.dropIndex == -1: #2. On itself or below control : Do Nothing
74 pass
75 else:
76 #----------
77 # Now that dropIndex has been established do 3 things
78 # 1. gather item data
79 # 2. delete item in list
80 # 3. insert item & it's data into the list at the new index
81 #----------
82 dropList = [] # Drop List is the list of field values from the list control
83 thisItem = self.GetItem(self.startIndex)
84 for x in range(self.GetColumnCount()):
85 dropList.append(self.GetItem(self.startIndex, x).GetText())
86 thisItem.SetId(self.dropIndex)
87 self.DeleteItem(self.startIndex)
88 self.InsertItem(thisItem)
89 for x in range(self.GetColumnCount()):
90 self.SetItem(self.dropIndex, x, dropList[x])
91 #------------
92 # I don't know exactly why, but the mouse event MUST
93 # call the stripe procedure if the control is to be successfully
94 # striped. Every time it was only in the _onInsert, it failed on
95 # dragging index 3 to the index 1 spot.
96 #-------------
97 # Furthermore, in the load button on the wxFrame that this lives in,
98 # I had to call the _onStripe directly because it would occasionally fail
99 # to stripe without it. You'll notice that this is present in the example stub.
100 # Someone with more knowledge than I probably knows why...and how to fix it properly.
101 #-------------
102 self._onStripe()
103 self.IsDrag = False
104 event.Skip()
105
106 def _onMouseDown(self, event):
107 self.IsInControl = True
108 event.Skip()
109
110 def _onInsert(self, event):
111 # Sequencing on a drop event is:
112 # wx.EVT_LIST_ITEM_SELECTED
113 # wx.EVT_LIST_BEGIN_DRAG
114 # wx.EVT_LEFT_UP
115 # wx.EVT_LIST_ITEM_SELECTED (at the new index)
116 # wx.EVT_LIST_INSERT_ITEM
117 #--------------------------------
118 # this call to onStripe catches any addition to the list; drag or not
119 self._onStripe()
120 self.dragIndex = -1
121 event.Skip()
122
123 def _onDelete(self, event):
124 self._onStripe()
125 event.Skip()
126
127 def _onStripe(self):
128 if self.GetItemCount() > 0:
129 for x in range(self.GetItemCount()):
130 if x % 2 == 0:
131 self.SetItemBackgroundColour(x, wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
132 else:
133 self.SetItemBackgroundColour(x, wx.WHITE)
134
135 #-------------------------------------------------------------------------------
136
137 if __name__ == "__main__":
138 firstNameList = ["Ben", "Bruce", "Clark", "Dick"]
139 lastNameList = ["Grimm","Wayne", "Kent", "Grayson"]
140 superNameList = ["The Thing", "Batman", "Superman", "Robin"]
141
142 #---------------------------------------------------------------------------
143
144 class MyApp(wx.App):
145 def OnInit(self):
146 self.myFrame = wx.Frame(None, title='Drag List Striped Example')
147 self.myFrame.Show(True)
148 self.SetTopWindow(self.myFrame)
149 return True
150
151 myApp = MyApp(redirect=False)
152 dls = MyDragListStriped(myApp.myFrame, style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
153 dls.InsertColumn(0, "First Name", wx.LIST_FORMAT_LEFT, 125)
154 dls.InsertColumn(1, "Last Name", wx.LIST_FORMAT_LEFT, 125)
155 dls.InsertColumn(2, "Superhero Name", wx.LIST_FORMAT_LEFT, 130)
156 sizer = wx.BoxSizer()
157 myApp.myFrame.SetSizer(sizer)
158 sizer.Add(dls, proportion=1, flag=wx.EXPAND)
159
160 for index in range(len(firstNameList)):
161 dls.InsertItem(index, firstNameList[index])
162 dls.SetItem(index, 1, lastNameList[index])
163 dls.SetItem(index, 2, superNameList[index])
164 myApp.myFrame.Layout()
165 dls._onStripe()
166 myApp.MainLoop()
The content on this page came from ListAndTreeControls, which was split up into this page and TreeControls .