]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/ComboCtrl.py
Layout tweak
[wxWidgets.git] / wxPython / demo / ComboCtrl.py
CommitLineData
84bc0d49
RD
1
2import wx
3import wx.combo
4import os
5
6#----------------------------------------------------------------------
7
8class NullLog:
9 def write(*args):
10 pass
11
12
13# This class is used to provide an interface between a ComboCtrl and a
14# ListCtrl that is used as the popoup for the combo widget. In this
15# case we use multiple inheritance to derive from both wx.ListCtrl and
16# wx.ComboPopup, but it also works well when deriving from just
17# ComboPopup and using a has-a relationship with the popup control,
18# you just need to be sure to return the control itself from the
19# GetControl method.
20
21class ListCtrlComboPopup(wx.ListCtrl, wx.combo.ComboPopup):
22
23 def __init__(self, log=None):
24 if log:
25 self.log = log
26 else:
27 self.log = NullLog()
28
29
30 # Since we are using multiple inheritance, and don't know yet
31 # which window is to be the parent, we'll do 2-phase create of
32 # the ListCtrl instead, and call its Create method later in
33 # our Create method. (See Create below.)
34 self.PostCreate(wx.PreListCtrl())
35
36 # Also init the ComboPopup base class.
37 wx.combo.ComboPopup.__init__(self)
38
39
40 def AddItem(self, txt):
41 self.InsertStringItem(self.GetItemCount(), txt)
42
43 def OnMotion(self, evt):
44 item, flags = self.HitTest(evt.GetPosition())
45 if item >= 0:
46 self.Select(item)
47 self.curitem = item
48
49 def OnLeftDown(self, evt):
50 self.value = self.curitem
51 self.Dismiss()
52
53
54 # The following methods are those that are overridable from the
55 # ComboPopup base class. Most of them are not required, but all
56 # are shown here for demonstration purposes.
57
58
59 # This is called immediately after construction finishes. You can
60 # use self.GetCombo if needed to get to the ComboCtrl instance.
61 def Init(self):
62 self.log.write("ListCtrlComboPopup.Init")
63 self.value = -1
64 self.curitem = -1
65
66
67 # Create the popup child control. Return true for success.
68 def Create(self, parent):
69 self.log.write("ListCtrlComboPopup.Create")
70 wx.ListCtrl.Create(self, parent,
71 style=wx.LC_LIST|wx.LC_SINGLE_SEL|
72 wx.LC_SORT_ASCENDING|wx.SIMPLE_BORDER)
73 self.Bind(wx.EVT_MOTION, self.OnMotion)
74 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
75 return True
76
77
78 # Return the widget that is to be used for the popup
79 def GetControl(self):
80 #self.log.write("ListCtrlComboPopup.GetControl")
81 return self
82
83 # Called just prior to displaying the popup, you can use it to
84 # 'select' the current item.
85 def SetStringValue(self, val):
86 self.log.write("ListCtrlComboPopup.SetStringValue")
87 idx = self.FindItem(-1, val)
88 if idx != wx.NOT_FOUND:
89 self.Select(idx)
90
91 # Return a string representation of the current item.
92 def GetStringValue(self):
93 self.log.write("ListCtrlComboPopup.GetStringValue")
94 if self.value >= 0:
95 return self.GetItemText(self.value)
96 return ""
97
98 # Called immediately after the popup is shown
99 def OnPopup(self):
100 self.log.write("ListCtrlComboPopup.OnPopup")
101 wx.combo.ComboPopup.OnPopup(self)
102
103 # Called when popup is dismissed
104 def OnDismiss(self):
105 self.log.write("ListCtrlComboPopup.OnDismiss")
106 wx.combo.ComboPopup.OnDismiss(self)
107
108 # This is called to custom paint in the combo control itself
109 # (ie. not the popup). Default implementation draws value as
110 # string.
111 def PaintComboControl(self, dc, rect):
112 self.log.write("ListCtrlComboPopup.PaintComboControl")
113 wx.combo.ComboPopup.PaintComboControl(self, dc, rect)
114
115 # Receives key events from the parent ComboCtrl. Events not
116 # handled should be skipped, as usual.
117 def OnComboKeyEvent(self, event):
118 self.log.write("ListCtrlComboPopup.OnComboKeyEvent")
119 wx.combo.ComboPopup.OnComboKeyEvent(self, event)
120
121 # Implement if you need to support special action when user
122 # double-clicks on the parent wxComboCtrl.
123 def OnComboDoubleClick(self):
124 self.log.write("ListCtrlComboPopup.OnComboDoubleClick")
125 wx.combo.ComboPopup.OnComboDoubleClick(self)
126
127 # Return final size of popup. Called on every popup, just prior to OnPopup.
128 # minWidth = preferred minimum width for window
129 # prefHeight = preferred height. Only applies if > 0,
130 # maxHeight = max height for window, as limited by screen size
131 # and should only be rounded down, if necessary.
132 def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
133 self.log.write("ListCtrlComboPopup.GetAdjustedSize: %d, %d, %d" % (minWidth, prefHeight, maxHeight))
134 return wx.combo.ComboPopup.GetAdjustedSize(self, minWidth, prefHeight, maxHeight)
135
136 # Return true if you want delay the call to Create until the popup
137 # is shown for the first time. It is more efficient, but note that
138 # it is often more convenient to have the control created
139 # immediately.
140 # Default returns false.
141 def LazyCreate(self):
142 self.log.write("ListCtrlComboPopup.LazyCreate")
143 return wx.combo.ComboPopup.LazyCreate(self)
144
145
146
147#----------------------------------------------------------------------
148# This class is a popup containing a TreeCtrl. This time we'll use a
149# has-a style (instead of is-a like above.)
150
151class TreeCtrlComboPopup(wx.combo.ComboPopup):
152
153 # overridden ComboPopup methods
154
155 def Init(self):
156 self.value = None
157 self.curitem = None
158
159
160 def Create(self, parent):
161 self.tree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
162 |wx.TR_HAS_BUTTONS
163 |wx.TR_SINGLE
164 |wx.TR_LINES_AT_ROOT
165 |wx.SIMPLE_BORDER)
166 self.tree.Bind(wx.EVT_MOTION, self.OnMotion)
167 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
168
169
170 def GetControl(self):
171 return self.tree
172
173
174 def GetStringValue(self):
175 if self.value:
176 return self.tree.GetItemText(self.value)
177 return ""
178
179
180 def OnPopup(self):
181 if self.value:
182 self.tree.EnsureVisible(self.value)
183 self.tree.SelectItem(self.value)
184
185
186 def SetStringValue(self, value):
187 # this assumes that item strings are unique...
188 root = self.tree.GetRootItem()
189 if not root:
190 return
191 found = self.FindItem(root, value)
192 if found:
193 self.value = found
194 self.tree.SelectItem(found)
195
196
197 def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
198 return wx.Size(minWidth, min(200, maxHeight))
199
200
201 # helpers
202
203 def FindItem(self, parentItem, text):
204 item, cookie = self.tree.GetFirstChild(parentItem)
205 while item:
206 if self.tree.GetItemText(item) == text:
207 return item
208 if self.tree.ItemHasChildren(item):
209 item = self.FindItem(item, text)
210 item, cookie = self.tree.GetNextChild(parentItem, cookie)
211 return wx.TreeItemId();
212
213
214 def AddItem(self, value, parent=None):
215 if not parent:
216 root = self.tree.GetRootItem()
217 if not root:
218 root = self.tree.AddRoot("<hidden root>")
219 parent = root
220
221 item = self.tree.AppendItem(parent, value)
222 return item
223
224
225 def OnMotion(self, evt):
226 # have the selection follow the mouse, like in a real combobox
227 item, flags = self.tree.HitTest(evt.GetPosition())
228 if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
229 self.tree.SelectItem(item)
230 self.curitem = item
231 evt.Skip()
232
233
234 def OnLeftDown(self, evt):
235 # do the combobox selection
236 item, flags = self.tree.HitTest(evt.GetPosition())
237 if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
238 self.curitem = item
239 self.value = item
240 self.Dismiss()
241 evt.Skip()
242
243
244#----------------------------------------------------------------------
245# Here we subclass wx.combo.ComboCtrl to do some custom popup animation
246
247CUSTOM_COMBOBOX_ANIMATION_DURATION = 200
248
249class ComboCtrlWithCustomPopupAnim(wx.combo.ComboCtrl):
250 def __init__(self, *args, **kw):
251 wx.combo.ComboCtrl.__init__(self, *args, **kw)
252 self.Bind(wx.EVT_TIMER, self.OnTimer)
253 self.aniTimer = wx.Timer(self)
254
255
256 def AnimateShow(self, rect, flags):
257 self.aniStart = wx.GetLocalTimeMillis()
258 self.aniRect = wx.Rect(*rect)
259 self.aniFlags = flags
260
261 dc = wx.ScreenDC()
262 bmp = wx.EmptyBitmap(rect.width, rect.height)
263 mdc = wx.MemoryDC(bmp)
264 mdc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
265 del mdc
266 self.aniBackBitmap = bmp
267
268 self.aniTimer.Start(10, wx.TIMER_CONTINUOUS)
269 self.OnTimer(None)
270 return False
271
272
273 def OnTimer(self, evt):
274 stopTimer = False
275 popup = self.GetPopupControl().GetControl()
276 rect = self.aniRect
277 dc = wx.ScreenDC()
278
279 if self.IsPopupWindowState(self.Hidden):
280 stopTimer = True
281 else:
282 pos = wx.GetLocalTimeMillis() - self.aniStart
283 if pos < CUSTOM_COMBOBOX_ANIMATION_DURATION:
284 # Actual animation happens here
285 width = rect.width
286 height = rect.height
287
288 center_x = rect.x + (width/2)
289 center_y = rect.y + (height/2)
290
291 dc.SetPen( wx.BLACK_PEN )
292 dc.SetBrush( wx.TRANSPARENT_BRUSH )
293
294 w = (((pos*256)/CUSTOM_COMBOBOX_ANIMATION_DURATION)*width)/256
295 ratio = float(w) / float(width)
296 h = int(height * ratio)
297
298 dc.DrawBitmap( self.aniBackBitmap, rect.x, rect.y )
299 dc.DrawRectangle( center_x - w/2, center_y - h/2, w, h )
300 else:
301 stopTimer = True
302
303 if stopTimer:
304 dc.DrawBitmap( self.aniBackBitmap, rect.x, rect.y )
305 popup.Move( (0, 0) )
306 self.aniTimer.Stop()
307 self.DoShowPopup( rect, self.aniFlags )
308
309
310#----------------------------------------------------------------------
311# FileSelectorCombo displays a dialog instead of a popup control, it
312# also uses a custom bitmap on the combo button.
313
314class FileSelectorCombo(wx.combo.ComboCtrl):
315 def __init__(self, *args, **kw):
316 wx.combo.ComboCtrl.__init__(self, *args, **kw)
317
318 # make a custom bitmap showing "..."
319 bw, bh = 16, 16
320 bmp = wx.EmptyBitmap(bw,bh)
321 dc = wx.MemoryDC(bmp)
322
323 # clear to a specific background colour
324 bgcolor = wx.Colour(255,0,255)
325 dc.SetBackground(wx.Brush(bgcolor))
326 dc.Clear()
327
328 # draw the label onto the bitmap
329 label = "..."
330 font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
331 font.SetWeight(wx.FONTWEIGHT_BOLD)
332 dc.SetFont(font)
333 tw,th = dc.GetTextExtent(label)
334 print tw, th
335 dc.DrawText(label, (bw-tw)/2, (bw-tw)/2)
336 del dc
337
338 # now apply a mask using the bgcolor
339 bmp.SetMaskColour(bgcolor)
340
341 # and tell the ComboCtrl to use it
342 self.SetButtonBitmaps(bmp, True)
343
344
345 # Overridden from ComboCtrl, called when the combo button is clicked
346 def OnButtonClick(self):
347 path = ""
348 name = ""
349 if self.GetValue():
350 path, name = os.path.split(self.GetValue())
351
352 dlg = wx.FileDialog(self, "Choose File", path, name,
353 "All files (*.*)|*.*", wx.FD_OPEN)
354 if dlg.ShowModal() == wx.ID_OK:
355 self.SetValue(dlg.GetPath())
356 dlg.Destroy()
357
358
359 # Overridden from ComboCtrl to avoid assert since there is no ComboPopup
360 def DoSetPopupControl(self, popup):
361 pass
362
363
364#----------------------------------------------------------------------
365
366
367class TestPanel(wx.Panel):
368 def __init__(self, parent, log):
369 self.log = log
370 wx.Panel.__init__(self, parent, -1)
371
372 fgs = wx.FlexGridSizer(cols=3, hgap=10, vgap=10)
373
374 cc = self.MakeLCCombo(log=self.log)
375 fgs.Add(cc)
376 fgs.Add((10,10))
377 fgs.Add(wx.StaticText(self, -1, "wx.ComboCtrl with a ListCtrl popup"))
378
379 cc = self.MakeLCCombo(style=wx.CB_READONLY)
380 fgs.Add(cc)
381 fgs.Add((10,10))
382 fgs.Add(wx.StaticText(self, -1, " Read-only"))
383
384 cc = self.MakeLCCombo()
385 cc.SetButtonPosition(side=wx.LEFT)
386 fgs.Add(cc)
387 fgs.Add((10,10))
388 fgs.Add(wx.StaticText(self, -1, " Button on the left"))
389
390 cc = self.MakeLCCombo()
391 cc.SetPopupMaxHeight(250)
392 fgs.Add(cc)
393 fgs.Add((10,10))
394 fgs.Add(wx.StaticText(self, -1, " Max height of popup set"))
395
396 cc = wx.combo.ComboCtrl(self, size=(250,-1))
397 tcp = TreeCtrlComboPopup()
398 cc.SetPopupControl(tcp)
399 fgs.Add(cc)
400 fgs.Add((10,10))
401 fgs.Add(wx.StaticText(self, -1, "TreeCtrl popup"))
402 # add some items to the tree
403 for i in range(5):
404 item = tcp.AddItem('Item %d' % (i+1))
405 for j in range(15):
406 tcp.AddItem('Subitem %d-%d' % (i+1, j+1), parent=item)
407
408 cc = ComboCtrlWithCustomPopupAnim(self, size=(250, -1))
409 popup = ListCtrlComboPopup()
410 cc.SetPopupMaxHeight(150)
411 cc.SetPopupControl(popup)
412 fgs.Add(cc)
413 fgs.Add((10,10))
414 fgs.Add(wx.StaticText(self, -1, "Custom popup animation"))
415 for word in "How cool was that!? Way COOL!".split():
416 popup.AddItem(word)
417
418
419 cc = FileSelectorCombo(self, size=(250, -1))
420 fgs.Add(cc)
421 fgs.Add((10,10))
422 fgs.Add(wx.StaticText(self, -1, "Custom popup action, and custom button bitmap"))
423
424
425 box = wx.BoxSizer()
426 box.Add(fgs, 1, wx.EXPAND|wx.ALL, 20)
427 self.SetSizer(box)
428
429
430 def MakeLCCombo(self, log=None, style=0):
431 # Create a ComboCtrl
432 cc = wx.combo.ComboCtrl(self, style=style, size=(250,-1))
433
434 # Create a Popup
435 popup = ListCtrlComboPopup(log)
436
437 # Associate them with each other. This also triggers the
438 # creation of the ListCtrl.
439 cc.SetPopupControl(popup)
440
441 # Add some items to the listctrl.
442 for x in range(75):
443 popup.AddItem("Item-%02d" % x)
444
445 return cc
446
447
448#----------------------------------------------------------------------
449
450def runTest(frame, nb, log):
451 win = TestPanel(nb, log)
452 return win
453
454#----------------------------------------------------------------------
455
456
457
458overview = """<html><body>
459<h2><center>wx.combo.ComboCtrl</center></h2>
460
461</body></html>
462"""
463
464
465
466if __name__ == '__main__':
467 import sys,os
468 import run
469 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])