1 |
1 #!/usr/bin/env python |
2 ''' |
2 # -*- coding: utf-8 -*- |
3 |
3 |
4 wxPython Custom Widget Collection 20060207 |
4 #This file is part of Beremiz, a Integrated Development Environment for |
5 Written By: Edward Flick (eddy -=at=- cdf-imaging -=dot=- com) |
5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. |
6 Michele Petrazzo (michele -=dot=- petrazzo -=at=- unipex -=dot=- it) |
6 # |
7 Will Sadkin (wsadkin-=at=- nameconnector -=dot=- com) |
7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
8 Copyright 2006 (c) CDF Inc. ( http://www.cdf-imaging.com ) |
8 # |
9 Contributed to the wxPython project under the wxPython project's license. |
9 #See COPYING file for copyrights details. |
10 |
10 # |
11 ''' |
11 #This library is free software; you can redistribute it and/or |
12 |
12 #modify it under the terms of the GNU General Public |
13 import locale, wx, sys, cStringIO |
13 #License as published by the Free Software Foundation; either |
14 |
14 #version 2.1 of the License, or (at your option) any later version. |
15 import wx.lib.mixins.listctrl as listmix |
15 # |
|
16 #This library is distributed in the hope that it will be useful, |
|
17 #but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 #General Public License for more details. |
|
20 # |
|
21 #You should have received a copy of the GNU General Public |
|
22 #License along with this library; if not, write to the Free Software |
|
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 |
|
25 import wx |
16 import cPickle |
26 import cPickle |
17 from wx import ImageFromStream, BitmapFromImage |
27 |
18 #---------------------------------------------------------------------- |
28 MAX_ITEM_COUNT = 10 |
19 def getSmallUpArrowData(): |
29 MAX_ITEM_SHOWN = 6 |
20 return \ |
30 ITEM_HEIGHT = 25 |
21 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ |
31 |
22 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ |
32 class TextCtrlAutoComplete(wx.TextCtrl): |
23 \x00\x00<IDAT8\x8dcddbf\xa0\x040Q\xa4{h\x18\xf0\xff\xdf\xdf\xffd\x1b\x00\xd3\ |
33 |
24 \x8c\xcf\x10\x9c\x06\xa0k\xc2e\x08m\xc2\x00\x97m\xd8\xc41\x0c \x14h\xe8\xf2\ |
34 def __init__ (self, parent, choices=None, dropDownClick=True, |
25 \x8c\xa3)q\x10\x18\x00\x00R\xd8#\xec\xb2\xcd\xc1Y\x00\x00\x00\x00IEND\xaeB`\ |
35 element_path=None, **therest): |
26 \x82' |
36 """ |
27 |
|
28 def getSmallUpArrowBitmap(): |
|
29 return BitmapFromImage(getSmallUpArrowImage()) |
|
30 |
|
31 def getSmallUpArrowImage(): |
|
32 stream = cStringIO.StringIO(getSmallUpArrowData()) |
|
33 return ImageFromStream(stream) |
|
34 |
|
35 |
|
36 def getSmallDnArrowData(): |
|
37 return \ |
|
38 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ |
|
39 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ |
|
40 \x00\x00HIDAT8\x8dcddbf\xa0\x040Q\xa4{\xd4\x00\x06\x06\x06\x06\x06\x16t\x81\ |
|
41 \xff\xff\xfe\xfe'\xa4\x89\x91\x89\x99\x11\xa7\x0b\x90%\ti\xc6j\x00>C\xb0\x89\ |
|
42 \xd3.\x10\xd1m\xc3\xe5*\xbc.\x80i\xc2\x17.\x8c\xa3y\x81\x01\x00\xa1\x0e\x04e\ |
|
43 ?\x84B\xef\x00\x00\x00\x00IEND\xaeB`\x82" |
|
44 |
|
45 def getSmallDnArrowBitmap(): |
|
46 return BitmapFromImage(getSmallDnArrowImage()) |
|
47 |
|
48 def getSmallDnArrowImage(): |
|
49 stream = cStringIO.StringIO(getSmallDnArrowData()) |
|
50 return ImageFromStream(stream) |
|
51 #---------------------------------------------------------------------- |
|
52 |
|
53 class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
|
54 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, |
|
55 size=wx.DefaultSize, style=0): |
|
56 wx.ListCtrl.__init__(self, parent, ID, pos, size, style) |
|
57 listmix.ListCtrlAutoWidthMixin.__init__(self) |
|
58 |
|
59 class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin ): |
|
60 |
|
61 def __init__ ( self, parent, colNames=None, choices = None, |
|
62 multiChoices=None, showHead=True, dropDownClick=True, |
|
63 colFetch=-1, colSearch=0, hideOnNoMatch=True, |
|
64 selectCallback=None, entryCallback=None, matchFunction=None, |
|
65 element_path=None, **therest) : |
|
66 ''' |
|
67 Constructor works just like wx.TextCtrl except you can pass in a |
37 Constructor works just like wx.TextCtrl except you can pass in a |
68 list of choices. You can also change the choice list at any time |
38 list of choices. You can also change the choice list at any time |
69 by calling setChoices. |
39 by calling setChoices. |
70 ''' |
40 """ |
71 |
41 |
72 if therest.has_key('style'): |
42 therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) |
73 therest['style']=wx.TE_PROCESS_ENTER | therest['style'] |
43 |
74 else: |
44 wx.TextCtrl.__init__(self, parent, **therest) |
75 therest['style']=wx.TE_PROCESS_ENTER |
|
76 |
|
77 wx.TextCtrl.__init__(self, parent, **therest ) |
|
78 |
45 |
79 #Some variables |
46 #Some variables |
80 self._dropDownClick = dropDownClick |
47 self._dropDownClick = dropDownClick |
81 self._colNames = colNames |
|
82 self._multiChoices = multiChoices |
|
83 self._showHead = showHead |
|
84 self._choices = choices |
|
85 self._lastinsertionpoint = 0 |
48 self._lastinsertionpoint = 0 |
86 self._hideOnNoMatch = hideOnNoMatch |
49 |
87 self._selectCallback = selectCallback |
50 self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) |
88 self._entryCallback = entryCallback |
|
89 self._matchFunction = matchFunction |
|
90 |
|
91 self._screenheight = wx.SystemSettings.GetMetric( wx.SYS_SCREEN_Y ) |
|
92 self.element_path = element_path |
51 self.element_path = element_path |
93 #sort variable needed by listmix |
52 |
94 self.itemDataMap = dict() |
|
95 |
|
96 #widgets |
53 #widgets |
97 self.dropdown = wx.PopupWindow( self ) |
54 self.dropdown = wx.PopupWindow(self) |
98 |
55 |
99 #Control the style |
56 #Control the style |
100 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING |
57 flags = wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT |
101 if not (showHead and multiChoices) : |
58 |
102 flags = flags | wx.LC_NO_HEADER |
|
103 |
|
104 #Create the list and bind the events |
59 #Create the list and bind the events |
105 self.dropdownlistbox = myListCtrl( self.dropdown, style=flags, |
60 self.dropdownlistbox = wx.ListBox(self.dropdown, style=flags, |
106 pos=wx.Point( 0, 0) ) |
61 pos=wx.Point(0, 0)) |
107 |
62 |
108 #initialize the parent |
63 self.SetChoices(choices) |
109 if multiChoices: ln = len(multiChoices) |
|
110 else: ln = 1 |
|
111 #else: ln = len(choices) |
|
112 listmix.ColumnSorterMixin.__init__(self, ln) |
|
113 |
|
114 #load the data |
|
115 if multiChoices: self.SetMultipleChoices (multiChoices, colSearch=colSearch, colFetch=colFetch) |
|
116 else: self.SetChoices ( choices ) |
|
117 |
64 |
118 #gp = self |
65 #gp = self |
119 #while ( gp != None ) : |
66 #while ( gp != None ) : |
120 # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) |
67 # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) |
121 # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) |
68 # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) |
122 # gp = gp.GetParent() |
69 # gp = gp.GetParent() |
123 |
70 |
124 self.Bind( wx.EVT_KILL_FOCUS, self.onControlChanged, self ) |
71 self.Bind(wx.EVT_KILL_FOCUS, self.onControlChanged, self) |
125 self.Bind( wx.EVT_TEXT , self.onEnteredText, self ) |
72 self.Bind(wx.EVT_TEXT_ENTER, self.onControlChanged, self) |
126 self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self ) |
73 self.Bind(wx.EVT_TEXT, self.onEnteredText, self) |
|
74 self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown, self) |
127 |
75 |
128 #If need drop down on left click |
76 #If need drop down on left click |
129 if dropDownClick: |
77 if dropDownClick: |
130 self.Bind ( wx.EVT_LEFT_DOWN , self.onClickToggleDown, self ) |
78 self.Bind(wx.EVT_LEFT_DOWN , self.onClickToggleDown, self) |
131 self.Bind ( wx.EVT_LEFT_UP , self.onClickToggleUp, self ) |
79 self.Bind(wx.EVT_LEFT_UP , self.onClickToggleUp, self) |
132 |
80 |
133 self.dropdown.Bind( wx.EVT_LISTBOX , self.onListItemSelected, self.dropdownlistbox ) |
81 self.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected) |
134 self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.onListClick) |
82 self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected) |
135 self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.onListDClick) |
83 |
136 self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.onListColClick) |
84 def ChangeValue(self, value): |
137 |
85 wx.TextCtrl.ChangeValue(self, value) |
138 self.il = wx.ImageList(16, 16) |
86 self._refreshListBoxChoices() |
139 |
|
140 self.sm_dn = self.il.Add(getSmallDnArrowBitmap()) |
|
141 self.sm_up = self.il.Add(getSmallUpArrowBitmap()) |
|
142 |
|
143 self.dropdownlistbox.SetImageList(self.il, wx.IMAGE_LIST_SMALL) |
|
144 self._ascending = True |
|
145 |
|
146 |
|
147 #-- methods called from mixin class |
|
148 def GetSortImages(self): |
|
149 return (self.sm_dn, self.sm_up) |
|
150 |
|
151 def GetListCtrl(self): |
|
152 return self.dropdownlistbox |
|
153 |
|
154 # -- event methods |
|
155 def onListClick(self, evt): |
|
156 toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() ) |
|
157 #no values on poition, return |
|
158 if toSel == -1: return |
|
159 self.dropdownlistbox.Select(toSel) |
|
160 |
|
161 def onListDClick(self, evt): |
|
162 self._setValueFromSelected() |
|
163 |
|
164 def onListColClick(self, evt): |
|
165 col = evt.GetColumn() |
|
166 |
|
167 #reverse the sort |
|
168 if col == self._colSearch: |
|
169 self._ascending = not self._ascending |
|
170 |
|
171 self.SortListItems( evt.GetColumn(), ascending=self._ascending ) |
|
172 self._colSearch = evt.GetColumn() |
|
173 evt.Skip() |
|
174 |
87 |
175 def onEnteredText(self, event): |
88 def onEnteredText(self, event): |
176 text = event.GetString() |
89 wx.CallAfter(self._refreshListBoxChoices) |
177 |
90 event.Skip() |
178 if self._entryCallback: |
91 |
179 self._entryCallback() |
92 def onKeyDown(self, event) : |
180 |
|
181 if not text: |
|
182 # control is empty; hide dropdown if shown: |
|
183 if self.dropdown.IsShown(): |
|
184 self._showDropDown(False) |
|
185 event.Skip() |
|
186 return |
|
187 |
|
188 |
|
189 found = False |
|
190 if self._multiChoices: |
|
191 #load the sorted data into the listbox |
|
192 dd = self.dropdownlistbox |
|
193 choices = [dd.GetItem(x, self._colSearch).GetText() |
|
194 for x in xrange(dd.GetItemCount())] |
|
195 else: |
|
196 choices = self._choices |
|
197 |
|
198 for numCh, choice in enumerate(choices): |
|
199 if self._matchFunction and self._matchFunction(text, choice): |
|
200 found = True |
|
201 elif choice.lower().startswith(text.lower()) : |
|
202 found = True |
|
203 if found: |
|
204 self._showDropDown(True) |
|
205 item = self.dropdownlistbox.GetItem(numCh) |
|
206 toSel = item.GetId() |
|
207 self.dropdownlistbox.Select(toSel) |
|
208 break |
|
209 |
|
210 if not found: |
|
211 self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False) |
|
212 if self._hideOnNoMatch: |
|
213 self._showDropDown(False) |
|
214 |
|
215 self._listItemVisible() |
|
216 |
|
217 event.Skip () |
|
218 |
|
219 def onKeyDown ( self, event ) : |
|
220 """ Do some work when the user press on the keys: |
93 """ Do some work when the user press on the keys: |
221 up and down: move the cursor |
94 up and down: move the cursor |
222 left and right: move the search |
95 """ |
223 """ |
|
224 skip = True |
|
225 sel = self.dropdownlistbox.GetFirstSelected() |
|
226 visible = self.dropdown.IsShown() |
96 visible = self.dropdown.IsShown() |
227 |
97 keycode = event.GetKeyCode() |
228 KC = event.GetKeyCode() |
98 if keycode in [wx.WXK_DOWN, wx.WXK_UP]: |
229 if KC == wx.WXK_DOWN : |
99 if not visible: |
230 if sel < (self.dropdownlistbox.GetItemCount () - 1) : |
100 self._showDropDown() |
231 self.dropdownlistbox.Select ( sel+1 ) |
101 elif keycode == wx.WXK_DOWN: |
232 self._listItemVisible() |
102 self._moveSelection(1) |
233 self._showDropDown () |
103 else: |
234 skip = False |
104 self._moveSelection(-1) |
235 elif KC == wx.WXK_UP : |
105 elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and visible: |
236 if sel > 0 : |
106 if self.dropdownlistbox.GetSelection() != wx.NOT_FOUND: |
237 self.dropdownlistbox.Select ( sel - 1 ) |
|
238 self._listItemVisible() |
|
239 self._showDropDown () |
|
240 skip = False |
|
241 elif KC == wx.WXK_LEFT : |
|
242 if not self._multiChoices: return |
|
243 if self._colSearch > 0: |
|
244 self._colSearch -=1 |
|
245 self._showDropDown () |
|
246 elif KC == wx.WXK_RIGHT: |
|
247 if not self._multiChoices: return |
|
248 if self._colSearch < self.dropdownlistbox.GetColumnCount() -1: |
|
249 self._colSearch += 1 |
|
250 self._showDropDown() |
|
251 |
|
252 if visible : |
|
253 if event.GetKeyCode() == wx.WXK_RETURN : |
|
254 self._setValueFromSelected() |
107 self._setValueFromSelected() |
255 skip = False |
108 else: |
256 if event.GetKeyCode() == wx.WXK_ESCAPE : |
109 self._showDropDown(False) |
257 self._showDropDown( False ) |
110 event.Skip() |
258 skip = False |
111 elif event.GetKeyCode() == wx.WXK_ESCAPE: |
259 if skip : |
112 self._showDropDown(False) |
|
113 else: |
260 event.Skip() |
114 event.Skip() |
261 |
115 |
262 def onListItemSelected (self, event): |
116 def onListItemSelected(self, event): |
263 self._setValueFromSelected() |
117 self._setValueFromSelected() |
264 event.Skip() |
118 event.Skip() |
265 |
119 |
266 def onClickToggleDown(self, event): |
120 def onClickToggleDown(self, event): |
267 self._lastinsertionpoint = self.GetInsertionPoint() |
121 self._lastinsertionpoint = self.GetInsertionPoint() |
268 event.Skip () |
122 event.Skip() |
269 |
123 |
270 def onClickToggleUp ( self, event ) : |
124 def onClickToggleUp(self, event): |
271 if ( self.GetInsertionPoint() == self._lastinsertionpoint ) : |
125 if self.GetInsertionPoint() == self._lastinsertionpoint: |
272 self._showDropDown ( not self.dropdown.IsShown() ) |
126 self._showDropDown(not self.dropdown.IsShown()) |
273 event.Skip () |
127 event.Skip() |
274 |
128 |
275 def onControlChanged(self, event): |
129 def onControlChanged(self, event): |
276 res = self.GetValue() |
130 res = self.GetValue() |
277 config = wx.ConfigBase.Get() |
131 config = wx.ConfigBase.Get() |
278 listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) |
132 listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) |
279 if len(res) and res not in listentries: |
133 if res and res not in listentries: |
280 config.Write(self.element_path, cPickle.dumps((listentries + [res])[-10:])) |
134 listentries = (listentries + [res])[-MAX_ITEM_COUNT:] |
|
135 config.Write(self.element_path, cPickle.dumps(listentries)) |
281 config.Flush() |
136 config.Flush() |
282 self.SetChoices((listentries + [res])[-10:]) |
137 self.SetChoices(listentries) |
283 self._showDropDown( False ) |
138 self._showDropDown(False) |
284 event.Skip() |
139 event.Skip() |
285 |
140 |
286 |
141 def SetChoices(self, choices): |
287 # -- Interfaces methods |
142 self._choices = choices |
288 def SetMultipleChoices(self, choices, colSearch=0, colFetch=-1): |
143 self._refreshListBoxChoices() |
289 ''' Set multi-column choice |
144 |
290 ''' |
145 def GetChoices(self): |
291 self._multiChoices = choices |
146 return self._choices |
292 self._choices = None |
147 |
293 if not isinstance(self._multiChoices, list): |
148 #------------------------------------------------------------------------------- |
294 self._multiChoices = [ x for x in self._multiChoices] |
149 # Internal methods |
295 |
150 #------------------------------------------------------------------------------- |
296 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING |
151 |
297 if not self._showHead: |
152 def _refreshListBoxChoices(self): |
298 flags |= wx.LC_NO_HEADER |
153 text = self.GetValue() |
299 self.dropdownlistbox.SetWindowStyleFlag(flags) |
154 |
300 |
155 self.dropdownlistbox.Clear() |
301 #prevent errors on "old" systems |
156 for choice in self._choices: |
302 if sys.version.startswith("2.3"): |
157 if choice.startswith(text): |
303 self._multiChoices.sort(lambda x, y: cmp(x[0].lower(), y[0].lower())) |
158 self.dropdownlistbox.Append(choice) |
|
159 |
|
160 itemcount = min(len(self.dropdownlistbox.GetStrings()), MAX_ITEM_SHOWN) |
|
161 self.popupsize = wx.Size(self.GetSize()[0], ITEM_HEIGHT * itemcount + 4) |
|
162 self.dropdownlistbox.SetSize(self.popupsize) |
|
163 self.dropdown.SetClientSize(self.popupsize) |
|
164 |
|
165 def _moveSelection(self, direction): |
|
166 selected = self.dropdownlistbox.GetSelection() |
|
167 if selected == wx.NOT_FOUND: |
|
168 if direction >= 0: |
|
169 selected = 0 |
|
170 else: |
|
171 selected = self.dropdownlistbox.GetCount() - 1 |
304 else: |
172 else: |
305 self._multiChoices.sort(key=lambda x: locale.strxfrm(x[0]).lower() ) |
173 selected = (selected + direction) % (self.dropdownlistbox.GetCount() + 1) |
306 |
174 if selected == self.dropdownlistbox.GetCount(): |
307 self._updateDataList(self._multiChoices) |
175 selected = wx.NOT_FOUND |
308 |
176 self.dropdownlistbox.SetSelection(selected) |
309 lChoices = len(choices) |
177 |
310 if lChoices < 2: |
178 def _setValueFromSelected(self): |
311 raise ValueError, "You have to pass me a multi-dimension list" |
179 """ |
312 |
|
313 for numCol, rowValues in enumerate(choices[0]): |
|
314 |
|
315 if self._colNames: colName = self._colNames[numCol] |
|
316 else: colName = "Select %i" % numCol |
|
317 |
|
318 self.dropdownlistbox.InsertColumn(numCol, colName) |
|
319 |
|
320 for numRow, valRow in enumerate(choices): |
|
321 |
|
322 for numCol, colVal in enumerate(valRow): |
|
323 if numCol == 0: |
|
324 index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) |
|
325 self.dropdownlistbox.SetStringItem(index, numCol, colVal) |
|
326 self.dropdownlistbox.SetItemData(index, numRow) |
|
327 |
|
328 self._setListSize() |
|
329 self._colSearch = colSearch |
|
330 self._colFetch = colFetch |
|
331 |
|
332 def SetChoices(self, choices): |
|
333 ''' |
|
334 Sets the choices available in the popup wx.ListBox. |
|
335 The items will be sorted case insensitively. |
|
336 ''' |
|
337 self._choices = choices |
|
338 self._multiChoices = None |
|
339 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER |
|
340 self.dropdownlistbox.SetWindowStyleFlag(flags) |
|
341 |
|
342 #if not isinstance(choices, list): |
|
343 # self._choices = [ x for x in choices if len(x)] |
|
344 self._choices = [ x for x in choices if len(x)] |
|
345 #prevent errors on "old" systems |
|
346 if sys.version.startswith("2.3"): |
|
347 self._choices.sort(lambda x, y: cmp(x.lower(), y.lower())) |
|
348 else: |
|
349 self._choices.sort(key=lambda x: locale.strxfrm(x).lower()) |
|
350 |
|
351 self._updateDataList(self._choices) |
|
352 |
|
353 self.dropdownlistbox.InsertColumn(0, "") |
|
354 |
|
355 for num, colVal in enumerate(self._choices): |
|
356 index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) |
|
357 |
|
358 self.dropdownlistbox.SetStringItem(index, 0, colVal) |
|
359 self.dropdownlistbox.SetItemData(index, num) |
|
360 |
|
361 self._setListSize() |
|
362 |
|
363 # there is only one choice for both search and fetch if setting a single column: |
|
364 self._colSearch = 0 |
|
365 self._colFetch = -1 |
|
366 |
|
367 def GetChoices(self): |
|
368 if self._choices: |
|
369 return self._choices |
|
370 else: |
|
371 return self._multiChoices |
|
372 |
|
373 def SetSelectCallback(self, cb=None): |
|
374 self._selectCallback = cb |
|
375 |
|
376 def SetEntryCallback(self, cb=None): |
|
377 self._entryCallback = cb |
|
378 |
|
379 def SetMatchFunction(self, mf=None): |
|
380 self._matchFunction = mf |
|
381 |
|
382 |
|
383 #-- Internal methods |
|
384 def _setValueFromSelected( self ) : |
|
385 ''' |
|
386 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. |
180 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. |
387 Will do nothing if no item is selected in the wx.ListCtrl. |
181 Will do nothing if no item is selected in the wx.ListCtrl. |
388 ''' |
182 """ |
389 sel = self.dropdownlistbox.GetFirstSelected() |
183 selected = self.dropdownlistbox.GetStringSelection() |
390 if sel > -1: |
184 if selected: |
391 if self._colFetch != -1: col = self._colFetch |
185 self.SetValue(selected) |
392 else: col = self._colSearch |
186 self._showDropDown(False) |
393 |
187 |
394 itemtext = self.dropdownlistbox.GetItem(sel, col).GetText() |
188 |
395 if self._selectCallback: |
189 def _showDropDown(self, show=True) : |
396 dd = self.dropdownlistbox |
190 """ |
397 values = [dd.GetItem(sel, x).GetText() |
|
398 for x in xrange(dd.GetColumnCount())] |
|
399 self._selectCallback( values ) |
|
400 |
|
401 self.SetValue (itemtext) |
|
402 self.SetInsertionPointEnd () |
|
403 self.SetSelection ( -1, -1 ) |
|
404 self._showDropDown ( False ) |
|
405 |
|
406 |
|
407 def _showDropDown ( self, show = True ) : |
|
408 ''' |
|
409 Either display the drop down list (show = True) or hide it (show = False). |
191 Either display the drop down list (show = True) or hide it (show = False). |
410 ''' |
192 """ |
411 if show : |
193 if show : |
412 size = self.dropdown.GetSize() |
194 size = self.dropdown.GetSize() |
413 width, height = self . GetSizeTuple() |
195 width, height = self.GetSizeTuple() |
414 x, y = self . ClientToScreenXY ( 0, height ) |
196 x, y = self.ClientToScreenXY(0, height) |
415 if size.GetWidth() != width : |
197 if size.GetWidth() != width : |
416 size.SetWidth(width) |
198 size.SetWidth(width) |
417 self.dropdown.SetSize(size) |
199 self.dropdown.SetSize(size) |
418 self.dropdownlistbox.SetSize(self.dropdown.GetClientSize()) |
200 self.dropdownlistbox.SetSize(self.dropdown.GetClientSize()) |
419 if (y + size.GetHeight()) < self._screenheight : |
201 if (y + size.GetHeight()) < self._screenheight : |
420 self.dropdown . SetPosition ( wx.Point(x, y) ) |
202 self.dropdown.SetPosition(wx.Point(x, y)) |
421 else: |
203 else: |
422 self.dropdown . SetPosition ( wx.Point(x, y - height - size.GetHeight()) ) |
204 self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight())) |
423 self.dropdown.Show ( show ) |
205 self.dropdown.Show(show) |
424 |
206 |
425 def _listItemVisible( self ) : |
|
426 ''' |
|
427 Moves the selected item to the top of the list ensuring it is always visible. |
|
428 ''' |
|
429 toSel = self.dropdownlistbox.GetFirstSelected () |
|
430 if toSel == -1: return |
|
431 self.dropdownlistbox.EnsureVisible( toSel ) |
|
432 |
|
433 def _updateDataList(self, choices): |
|
434 #delete, if need, all the previous data |
|
435 if self.dropdownlistbox.GetColumnCount() != 0: |
|
436 self.dropdownlistbox.DeleteAllColumns() |
|
437 self.dropdownlistbox.DeleteAllItems() |
|
438 |
|
439 #and update the dict |
|
440 if choices: |
|
441 for numVal, data in enumerate(choices): |
|
442 self.itemDataMap[numVal] = data |
|
443 else: |
|
444 numVal = 0 |
|
445 self.SetColumnCount(numVal) |
|
446 |
|
447 def _setListSize(self): |
|
448 if self._multiChoices: |
|
449 choices = self._multiChoices |
|
450 else: |
|
451 choices = self._choices |
|
452 |
|
453 longest = 0 |
|
454 for choice in choices : |
|
455 longest = max(len(choice), longest) |
|
456 |
|
457 longest += 3 |
|
458 itemcount = min( len( choices ) , 7 ) + 2 |
|
459 charheight = self.dropdownlistbox.GetCharHeight() |
|
460 charwidth = self.dropdownlistbox.GetCharWidth() |
|
461 self.popupsize = wx.Size( charwidth*longest, charheight*itemcount ) |
|
462 self.dropdownlistbox.SetSize ( self.popupsize ) |
|
463 self.dropdown.SetClientSize( self.popupsize ) |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 class test: |
|
469 def __init__(self): |
|
470 args = dict() |
|
471 if 1: |
|
472 args["colNames"] = ("col1", "col2") |
|
473 args["multiChoices"] = [ ("Zoey","WOW"), ("Alpha", "wxPython"), |
|
474 ("Ceda","Is"), ("Beta", "fantastic"), |
|
475 ("zoebob", "!!")] |
|
476 args["colFetch"] = 1 |
|
477 else: |
|
478 args["choices"] = ["123", "cs", "cds", "Bob","Marley","Alpha"] |
|
479 |
|
480 args["selectCallback"] = self.selectCallback |
|
481 |
|
482 self.dynamic_choices = [ |
|
483 'aardvark', 'abandon', 'acorn', 'acute', 'adore', |
|
484 'aegis', 'ascertain', 'asteroid', |
|
485 'beautiful', 'bold', 'classic', |
|
486 'daring', 'dazzling', 'debonair', 'definitive', |
|
487 'effective', 'elegant', |
|
488 'http://python.org', 'http://www.google.com', |
|
489 'fabulous', 'fantastic', 'friendly', 'forgiving', 'feature', |
|
490 'sage', 'scarlet', 'scenic', 'seaside', 'showpiece', 'spiffy', |
|
491 'www.wxPython.org', 'www.osafoundation.org' |
|
492 ] |
|
493 |
|
494 |
|
495 app = wx.PySimpleApp() |
|
496 frm = wx.Frame(None,-1,"Test",style=wx.TAB_TRAVERSAL|wx.DEFAULT_FRAME_STYLE) |
|
497 panel = wx.Panel(frm) |
|
498 sizer = wx.BoxSizer(wx.VERTICAL) |
|
499 |
|
500 self._ctrl = TextCtrlAutoComplete(panel, **args) |
|
501 but = wx.Button(panel,label="Set other multi-choice") |
|
502 but.Bind(wx.EVT_BUTTON, self.onBtMultiChoice) |
|
503 but2 = wx.Button(panel,label="Set other one-colum choice") |
|
504 but2.Bind(wx.EVT_BUTTON, self.onBtChangeChoice) |
|
505 but3 = wx.Button(panel,label="Set the starting choices") |
|
506 but3.Bind(wx.EVT_BUTTON, self.onBtStartChoices) |
|
507 but4 = wx.Button(panel,label="Enable dynamic choices") |
|
508 but4.Bind(wx.EVT_BUTTON, self.onBtDynamicChoices) |
|
509 |
|
510 sizer.Add(but, 0, wx.ADJUST_MINSIZE, 0) |
|
511 sizer.Add(but2, 0, wx.ADJUST_MINSIZE, 0) |
|
512 sizer.Add(but3, 0, wx.ADJUST_MINSIZE, 0) |
|
513 sizer.Add(but4, 0, wx.ADJUST_MINSIZE, 0) |
|
514 sizer.Add(self._ctrl, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0) |
|
515 panel.SetAutoLayout(True) |
|
516 panel.SetSizer(sizer) |
|
517 sizer.Fit(panel) |
|
518 sizer.SetSizeHints(panel) |
|
519 panel.Layout() |
|
520 app.SetTopWindow(frm) |
|
521 frm.Show() |
|
522 but.SetFocus() |
|
523 app.MainLoop() |
|
524 |
|
525 def onBtChangeChoice(self, event): |
|
526 #change the choices |
|
527 self._ctrl.SetChoices(["123", "cs", "cds", "Bob","Marley","Alpha"]) |
|
528 self._ctrl.SetEntryCallback(None) |
|
529 self._ctrl.SetMatchFunction(None) |
|
530 |
|
531 def onBtMultiChoice(self, event): |
|
532 #change the choices |
|
533 self._ctrl.SetMultipleChoices( [ ("Test","Hello"), ("Other word","World"), |
|
534 ("Yes!","it work?") ], colFetch = 1 ) |
|
535 self._ctrl.SetEntryCallback(None) |
|
536 self._ctrl.SetMatchFunction(None) |
|
537 |
|
538 def onBtStartChoices(self, event): |
|
539 #change the choices |
|
540 self._ctrl.SetMultipleChoices( [ ("Zoey","WOW"), ("Alpha", "wxPython"), |
|
541 ("Ceda","Is"), ("Beta", "fantastic"), |
|
542 ("zoebob", "!!")], colFetch = 1 ) |
|
543 self._ctrl.SetEntryCallback(None) |
|
544 self._ctrl.SetMatchFunction(None) |
|
545 |
|
546 def onBtDynamicChoices(self, event): |
|
547 ''' |
|
548 Demonstrate dynamic adjustment of the auto-complete list, based on what's |
|
549 been typed so far: |
|
550 ''' |
|
551 self._ctrl.SetChoices(self.dynamic_choices) |
|
552 self._ctrl.SetEntryCallback(self.setDynamicChoices) |
|
553 self._ctrl.SetMatchFunction(self.match) |
|
554 |
|
555 |
|
556 def match(self, text, choice): |
|
557 ''' |
|
558 Demonstrate "smart" matching feature, by ignoring http:// and www. when doing |
|
559 matches. |
|
560 ''' |
|
561 t = text.lower() |
|
562 c = choice.lower() |
|
563 if c.startswith(t): return True |
|
564 if c.startswith(r'http://'): c = c[7:] |
|
565 if c.startswith(t): return True |
|
566 if c.startswith('www.'): c = c[4:] |
|
567 return c.startswith(t) |
|
568 |
|
569 def setDynamicChoices(self): |
|
570 ctrl = self._ctrl |
|
571 text = ctrl.GetValue().lower() |
|
572 current_choices = ctrl.GetChoices() |
|
573 choices = [choice for choice in self.dynamic_choices if self.match(text, choice)] |
|
574 if choices != current_choices: |
|
575 ctrl.SetChoices(choices) |
|
576 |
|
577 def selectCallback(self, values): |
|
578 """ Simply function that receive the row values when the |
|
579 user select an item |
|
580 """ |
|
581 print "Select Callback called...:", values |
|
582 |
|
583 |
|
584 if __name__ == "__main__": |
|
585 test() |
|
586 |
|