1 #----------------------------------------------------------------------
3 # Purpose: Various kinds of generic buttons, (not native controls but
10 # Copyright: (c) 1999 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------
13 # 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net)
15 # o Updated for wx namespace
16 # o Tested with updated demo
20 This module implements various forms of generic buttons, meaning that
21 they are not built on native controls but are self-drawn.
23 The GenButton is the base. It acts like a normal button but you
24 are able to better control how it looks, bevel width, colours, etc.
26 GenBitmapButton is a button with one or more bitmaps that show
27 the various states the button can be in.
29 GenToggleButton stays depressed when clicked, until clicked again.
31 GenBitmapToggleButton the same but with bitmaps.
39 #----------------------------------------------------------------------
41 class GenButtonEvent(wx
.PyCommandEvent
):
42 def __init__(self
, eventType
, ID
):
43 wx
.PyCommandEvent
.__init
__(self
, eventType
, ID
)
47 def SetIsDown(self
, isDown
):
53 def SetButtonObj(self
, btn
):
56 def GetButtonObj(self
):
60 #----------------------------------------------------------------------
62 class GenButton(wx
.PyControl
):
65 def __init__(self
, parent
, ID
, label
,
66 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
67 style
= 0, validator
= wx
.DefaultValidator
,
72 wx
.PyControl
.__init
__(self
, parent
, ID
, pos
, size
, cstyle
, validator
, name
)
76 if style
& wx
.NO_BORDER
:
78 self
.useFocusInd
= False
81 self
.useFocusInd
= True
84 self
.InheritAttributes()
85 self
.SetBestFittingSize(size
)
88 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
89 self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
)
90 if wx
.Platform
== '__WXMSW__':
91 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
)
92 self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
)
93 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnGainFocus
)
94 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnLoseFocus
)
95 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
96 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
97 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
98 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
101 def SetBestSize(self
, size
=None):
103 Given the current font and bezel width settings, calculate
107 size
= wx
.DefaultSize
108 wx
.PyControl
.SetBestFittingSize(self
, size
)
111 def DoGetBestSize(self
):
113 Overridden base class virtual. Determines the best size of the
114 button based on the label and bezel size.
116 w
, h
, useMin
= self
._GetLabelSize
()
117 defSize
= wx
.Button
.GetDefaultSize()
119 if useMin
and width
< defSize
.width
:
120 width
= defSize
.width
122 if useMin
and height
< defSize
.height
:
123 height
= defSize
.height
124 width
= width
+ self
.bezelWidth
- 1
125 height
= height
+ self
.bezelWidth
- 1
126 return (width
, height
)
129 def AcceptsFocus(self
):
130 """Overridden base class virtual."""
131 return self
.IsShown() and self
.IsEnabled()
134 def GetDefaultAttributes(self
):
136 Overridden base class virtual. By default we should use
137 the same font/colour attributes as the native Button.
139 return wx
.Button
.GetClassDefaultAttributes()
142 def ShouldInheritColours(self
):
144 Overridden base class virtual. Buttons usually don't inherit
145 the parent's colours.
150 def Enable(self
, enable
=True):
151 wx
.PyControl
.Enable(self
, enable
)
155 def SetBezelWidth(self
, width
):
156 """Set the width of the 3D effect"""
157 self
.bezelWidth
= width
159 def GetBezelWidth(self
):
160 """Return the width of the 3D effect"""
161 return self
.bezelWidth
163 def SetUseFocusIndicator(self
, flag
):
164 """Specifiy if a focus indicator (dotted line) should be used"""
165 self
.useFocusInd
= flag
167 def GetUseFocusIndicator(self
):
168 """Return focus indicator flag"""
169 return self
.useFocusInd
172 def InitColours(self
):
174 Calculate a new set of highlight and shadow colours based on
175 the background colour. Works okay if the colour is dark...
177 faceClr
= self
.GetBackgroundColour()
178 r
, g
, b
= faceClr
.Get()
179 fr
, fg
, fb
= min(255,r
+32), min(255,g
+32), min(255,b
+32)
180 self
.faceDnClr
= wx
.Colour(fr
, fg
, fb
)
181 sr
, sg
, sb
= max(0,r
-32), max(0,g
-32), max(0,b
-32)
182 self
.shadowPen
= wx
.Pen(wx
.Colour(sr
,sg
,sb
), 1, wx
.SOLID
)
183 hr
, hg
, hb
= min(255,r
+64), min(255,g
+64), min(255,b
+64)
184 self
.highlightPen
= wx
.Pen(wx
.Colour(hr
,hg
,hb
), 1, wx
.SOLID
)
185 self
.focusClr
= wx
.Colour(hr
, hg
, hb
)
187 textClr
= self
.GetForegroundColour()
188 if wx
.Platform
== "__WXMAC__":
189 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.SOLID
)
191 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.USER_DASH
)
192 self
.focusIndPen
.SetDashes([1,1])
193 self
.focusIndPen
.SetCap(wx
.CAP_BUTT
)
196 def SetBackgroundColour(self
, colour
):
197 wx
.PyControl
.SetBackgroundColour(self
, colour
)
201 def SetForegroundColour(self
, colour
):
202 wx
.PyControl
.SetForegroundColour(self
, colour
)
205 def SetDefault(self
):
206 self
.GetParent().SetDefaultItem(self
)
208 def _GetLabelSize(self
):
209 """ used internally """
210 w
, h
= self
.GetTextExtent(self
.GetLabel())
215 evt
= GenButtonEvent(wx
.wxEVT_COMMAND_BUTTON_CLICKED
, self
.GetId())
216 evt
.SetIsDown(not self
.up
)
217 evt
.SetButtonObj(self
)
218 evt
.SetEventObject(self
)
219 self
.GetEventHandler().ProcessEvent(evt
)
222 def DrawBezel(self
, dc
, x1
, y1
, x2
, y2
):
223 # draw the upper left sides
225 dc
.SetPen(self
.highlightPen
)
227 dc
.SetPen(self
.shadowPen
)
228 for i
in range(self
.bezelWidth
):
229 dc
.DrawLine(x1
+i
, y1
, x1
+i
, y2
-i
)
230 dc
.DrawLine(x1
, y1
+i
, x2
-i
, y1
+i
)
232 # draw the lower right sides
234 dc
.SetPen(self
.shadowPen
)
236 dc
.SetPen(self
.highlightPen
)
237 for i
in range(self
.bezelWidth
):
238 dc
.DrawLine(x1
+i
, y2
-i
, x2
+1, y2
-i
)
239 dc
.DrawLine(x2
-i
, y1
+i
, x2
-i
, y2
)
242 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
243 dc
.SetFont(self
.GetFont())
245 dc
.SetTextForeground(self
.GetForegroundColour())
247 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
248 label
= self
.GetLabel()
249 tw
, th
= dc
.GetTextExtent(label
)
251 dw
= dy
= self
.labelDelta
252 dc
.DrawText(label
, (width
-tw
)/2+dw
, (height
-th
)/2+dy
)
255 def DrawFocusIndicator(self
, dc
, w
, h
):
257 self
.focusIndPen
.SetColour(self
.focusClr
)
258 dc
.SetLogicalFunction(wx
.INVERT
)
259 dc
.SetPen(self
.focusIndPen
)
260 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
261 dc
.DrawRectangle(bw
+2,bw
+2, w
-bw
*2-4, h
-bw
*2-4)
262 dc
.SetLogicalFunction(wx
.COPY
)
265 def OnPaint(self
, event
):
266 (width
, height
) = self
.GetClientSizeTuple()
270 dc
= wx
.BufferedPaintDC(self
)
272 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour(), wx
.SOLID
))
274 dc
.SetBackground(wx
.Brush(self
.faceDnClr
, wx
.SOLID
))
276 self
.DrawBezel(dc
, x1
, y1
, x2
, y2
)
277 self
.DrawLabel(dc
, width
, height
)
278 if self
.hasFocus
and self
.useFocusInd
:
279 self
.DrawFocusIndicator(dc
, width
, height
)
282 def OnEraseBackground(self
, event
):
286 def OnLeftDown(self
, event
):
287 if not self
.IsEnabled():
296 def OnLeftUp(self
, event
):
297 if not self
.IsEnabled() or not self
.HasCapture():
299 if self
.HasCapture():
301 if not self
.up
: # if the button was down when the mouse was released...
308 def OnMotion(self
, event
):
309 if not self
.IsEnabled() or not self
.HasCapture():
311 if event
.LeftIsDown() and self
.HasCapture():
312 x
,y
= event
.GetPositionTuple()
313 w
,h
= self
.GetClientSizeTuple()
314 if self
.up
and x
<w
and x
>=0 and y
<h
and y
>=0:
318 if not self
.up
and (x
<0 or y
<0 or x
>=w
or y
>=h
):
325 def OnGainFocus(self
, event
):
327 dc
= wx
.ClientDC(self
)
328 w
, h
= self
.GetClientSizeTuple()
330 self
.DrawFocusIndicator(dc
, w
, h
)
333 def OnLoseFocus(self
, event
):
334 self
.hasFocus
= False
335 dc
= wx
.ClientDC(self
)
336 w
, h
= self
.GetClientSizeTuple()
338 self
.DrawFocusIndicator(dc
, w
, h
)
341 def OnKeyDown(self
, event
):
342 if self
.hasFocus
and event
.KeyCode() == ord(" "):
348 def OnKeyUp(self
, event
):
349 if self
.hasFocus
and event
.KeyCode() == ord(" "):
356 #----------------------------------------------------------------------
358 class GenBitmapButton(GenButton
):
359 def __init__(self
, parent
, ID
, bitmap
,
360 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
361 style
= 0, validator
= wx
.DefaultValidator
,
363 self
.bmpDisabled
= None
365 self
.bmpSelected
= None
366 self
.SetBitmapLabel(bitmap
)
367 GenButton
.__init
__(self
, parent
, ID
, "", pos
, size
, style
, validator
, name
)
370 def GetBitmapLabel(self
):
372 def GetBitmapDisabled(self
):
373 return self
.bmpDisabled
374 def GetBitmapFocus(self
):
376 def GetBitmapSelected(self
):
377 return self
.bmpSelected
380 def SetBitmapDisabled(self
, bitmap
):
381 """Set bitmap to display when the button is disabled"""
382 self
.bmpDisabled
= bitmap
384 def SetBitmapFocus(self
, bitmap
):
385 """Set bitmap to display when the button has the focus"""
386 self
.bmpFocus
= bitmap
387 self
.SetUseFocusIndicator(False)
389 def SetBitmapSelected(self
, bitmap
):
390 """Set bitmap to display when the button is selected (pressed down)"""
391 self
.bmpSelected
= bitmap
393 def SetBitmapLabel(self
, bitmap
, createOthers
=True):
395 Set the bitmap to display normally.
396 This is the only one that is required. If
397 createOthers is True, then the other bitmaps
398 will be generated on the fly. Currently,
399 only the disabled bitmap is generated.
401 self
.bmpLabel
= bitmap
402 if bitmap
is not None and createOthers
:
403 image
= wx
.ImageFromBitmap(bitmap
)
404 imageutils
.grayOut(image
)
405 self
.SetBitmapDisabled(wx
.BitmapFromImage(image
))
408 def _GetLabelSize(self
):
409 """ used internally """
410 if not self
.bmpLabel
:
412 return self
.bmpLabel
.GetWidth()+2, self
.bmpLabel
.GetHeight()+2, False
414 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
416 if self
.bmpDisabled
and not self
.IsEnabled():
417 bmp
= self
.bmpDisabled
418 if self
.bmpFocus
and self
.hasFocus
:
420 if self
.bmpSelected
and not self
.up
:
421 bmp
= self
.bmpSelected
422 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
424 dw
= dy
= self
.labelDelta
425 hasMask
= bmp
.GetMask() != None
426 dc
.DrawBitmap(bmp
, (width
-bw
)/2+dw
, (height
-bh
)/2+dy
, hasMask
)
429 #----------------------------------------------------------------------
432 class GenBitmapTextButton(GenBitmapButton
): # generic bitmapped button with Text Label
433 def __init__(self
, parent
, ID
, bitmap
, label
,
434 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
435 style
= 0, validator
= wx
.DefaultValidator
,
437 GenBitmapButton
.__init
__(self
, parent
, ID
, bitmap
, pos
, size
, style
, validator
, name
)
441 def _GetLabelSize(self
):
442 """ used internally """
443 w
, h
= self
.GetTextExtent(self
.GetLabel())
444 if not self
.bmpLabel
:
445 return w
, h
, True # if there isn't a bitmap use the size of the text
447 w_bmp
= self
.bmpLabel
.GetWidth()+2
448 h_bmp
= self
.bmpLabel
.GetHeight()+2
454 return width
, height
, True
457 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
459 if bmp
!= None: # if the bitmap is used
460 if self
.bmpDisabled
and not self
.IsEnabled():
461 bmp
= self
.bmpDisabled
462 if self
.bmpFocus
and self
.hasFocus
:
464 if self
.bmpSelected
and not self
.up
:
465 bmp
= self
.bmpSelected
466 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
468 dw
= dy
= self
.labelDelta
469 hasMask
= bmp
.GetMask() != None
471 bw
= bh
= 0 # no bitmap -> size is zero
473 dc
.SetFont(self
.GetFont())
475 dc
.SetTextForeground(self
.GetForegroundColour())
477 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
479 label
= self
.GetLabel()
480 tw
, th
= dc
.GetTextExtent(label
) # size of text
482 dw
= dy
= self
.labelDelta
484 pos_x
= (width
-bw
-tw
)/2+dw
# adjust for bitmap and text to centre
486 dc
.DrawBitmap(bmp
, pos_x
, (height
-bh
)/2+dy
, hasMask
) # draw bitmap if available
487 pos_x
= pos_x
+ 2 # extra spacing from bitmap
489 dc
.DrawText(label
, pos_x
+ dw
+bw
, (height
-th
)/2+dy
) # draw the text
492 #----------------------------------------------------------------------
496 def SetToggle(self
, flag
):
505 def OnLeftDown(self
, event
):
506 if not self
.IsEnabled():
508 self
.saveUp
= self
.up
509 self
.up
= not self
.up
514 def OnLeftUp(self
, event
):
515 if not self
.IsEnabled() or not self
.HasCapture():
517 if self
.HasCapture():
518 if self
.up
!= self
.saveUp
:
523 def OnKeyDown(self
, event
):
526 def OnMotion(self
, event
):
527 if not self
.IsEnabled():
529 if event
.LeftIsDown() and self
.HasCapture():
530 x
,y
= event
.GetPositionTuple()
531 w
,h
= self
.GetClientSizeTuple()
532 if x
<w
and x
>=0 and y
<h
and y
>=0:
533 self
.up
= not self
.saveUp
536 if (x
<0 or y
<0 or x
>=w
or y
>=h
):
537 self
.up
= self
.saveUp
542 def OnKeyUp(self
, event
):
543 if self
.hasFocus
and event
.KeyCode() == ord(" "):
544 self
.up
= not self
.up
552 class GenToggleButton(__ToggleMixin
, GenButton
):
555 class GenBitmapToggleButton(__ToggleMixin
, GenBitmapButton
):
558 class GenBitmapTextToggleButton(__ToggleMixin
, GenBitmapTextButton
):
561 #----------------------------------------------------------------------