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. They act
22 like normal buttons but you are able to better control how they look,
23 bevel width, colours, etc.
30 #----------------------------------------------------------------------
32 class GenButtonEvent(wx
.PyCommandEvent
):
33 """Event sent from the generic buttons when the button is activated. """
34 def __init__(self
, eventType
, ID
):
35 wx
.PyCommandEvent
.__init
__(self
, eventType
, ID
)
39 def SetIsDown(self
, isDown
):
45 def SetButtonObj(self
, btn
):
48 def GetButtonObj(self
):
52 #----------------------------------------------------------------------
54 class GenButton(wx
.PyControl
):
55 """A generic button, and base class for the other generic buttons."""
59 def __init__(self
, parent
, ID
, label
,
60 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
61 style
= 0, validator
= wx
.DefaultValidator
,
65 cstyle
= wx
.BORDER_NONE
66 wx
.PyControl
.__init
__(self
, parent
, ID
, pos
, size
, cstyle
, validator
, name
)
71 if style
& wx
.BORDER_NONE
:
73 self
.useFocusInd
= False
76 self
.useFocusInd
= True
79 self
.InheritAttributes()
80 self
.SetBestFittingSize(size
)
83 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
84 self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
)
85 if wx
.Platform
== '__WXMSW__':
86 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
)
87 self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
)
88 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnGainFocus
)
89 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnLoseFocus
)
90 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
91 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
92 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
95 def SetBestSize(self
, size
=None):
97 Given the current font and bezel width settings, calculate
101 size
= wx
.DefaultSize
102 wx
.PyControl
.SetBestFittingSize(self
, size
)
105 def DoGetBestSize(self
):
107 Overridden base class virtual. Determines the best size of the
108 button based on the label and bezel size.
110 w
, h
, useMin
= self
._GetLabelSize
()
111 defSize
= wx
.Button
.GetDefaultSize()
113 if useMin
and width
< defSize
.width
:
114 width
= defSize
.width
116 if useMin
and height
< defSize
.height
:
117 height
= defSize
.height
118 width
= width
+ self
.bezelWidth
- 1
119 height
= height
+ self
.bezelWidth
- 1
120 return (width
, height
)
123 def AcceptsFocus(self
):
124 """Overridden base class virtual."""
125 return self
.IsShown() and self
.IsEnabled()
128 def GetDefaultAttributes(self
):
130 Overridden base class virtual. By default we should use
131 the same font/colour attributes as the native Button.
133 return wx
.Button
.GetClassDefaultAttributes()
136 def ShouldInheritColours(self
):
138 Overridden base class virtual. Buttons usually don't inherit
139 the parent's colours.
144 def Enable(self
, enable
=True):
145 wx
.PyControl
.Enable(self
, enable
)
149 def SetBezelWidth(self
, width
):
150 """Set the width of the 3D effect"""
151 self
.bezelWidth
= width
153 def GetBezelWidth(self
):
154 """Return the width of the 3D effect"""
155 return self
.bezelWidth
157 def SetUseFocusIndicator(self
, flag
):
158 """Specifiy if a focus indicator (dotted line) should be used"""
159 self
.useFocusInd
= flag
161 def GetUseFocusIndicator(self
):
162 """Return focus indicator flag"""
163 return self
.useFocusInd
166 def InitColours(self
):
168 Calculate a new set of highlight and shadow colours based on
169 the background colour. Works okay if the colour is dark...
171 faceClr
= self
.GetBackgroundColour()
172 r
, g
, b
= faceClr
.Get()
173 fr
, fg
, fb
= min(255,r
+32), min(255,g
+32), min(255,b
+32)
174 self
.faceDnClr
= wx
.Colour(fr
, fg
, fb
)
175 sr
, sg
, sb
= max(0,r
-32), max(0,g
-32), max(0,b
-32)
176 self
.shadowPen
= wx
.Pen(wx
.Colour(sr
,sg
,sb
), 1, wx
.SOLID
)
177 hr
, hg
, hb
= min(255,r
+64), min(255,g
+64), min(255,b
+64)
178 self
.highlightPen
= wx
.Pen(wx
.Colour(hr
,hg
,hb
), 1, wx
.SOLID
)
179 self
.focusClr
= wx
.Colour(hr
, hg
, hb
)
181 textClr
= self
.GetForegroundColour()
182 if wx
.Platform
== "__WXMAC__":
183 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.SOLID
)
185 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.USER_DASH
)
186 self
.focusIndPen
.SetDashes([1,1])
187 self
.focusIndPen
.SetCap(wx
.CAP_BUTT
)
190 def SetBackgroundColour(self
, colour
):
191 wx
.PyControl
.SetBackgroundColour(self
, colour
)
195 def SetForegroundColour(self
, colour
):
196 wx
.PyControl
.SetForegroundColour(self
, colour
)
199 def SetDefault(self
):
200 self
.GetParent().SetDefaultItem(self
)
202 def _GetLabelSize(self
):
203 """ used internally """
204 w
, h
= self
.GetTextExtent(self
.GetLabel())
209 evt
= GenButtonEvent(wx
.wxEVT_COMMAND_BUTTON_CLICKED
, self
.GetId())
210 evt
.SetIsDown(not self
.up
)
211 evt
.SetButtonObj(self
)
212 evt
.SetEventObject(self
)
213 self
.GetEventHandler().ProcessEvent(evt
)
216 def DrawBezel(self
, dc
, x1
, y1
, x2
, y2
):
217 # draw the upper left sides
219 dc
.SetPen(self
.highlightPen
)
221 dc
.SetPen(self
.shadowPen
)
222 for i
in range(self
.bezelWidth
):
223 dc
.DrawLine(x1
+i
, y1
, x1
+i
, y2
-i
)
224 dc
.DrawLine(x1
, y1
+i
, x2
-i
, y1
+i
)
226 # draw the lower right sides
228 dc
.SetPen(self
.shadowPen
)
230 dc
.SetPen(self
.highlightPen
)
231 for i
in range(self
.bezelWidth
):
232 dc
.DrawLine(x1
+i
, y2
-i
, x2
+1, y2
-i
)
233 dc
.DrawLine(x2
-i
, y1
+i
, x2
-i
, y2
)
236 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
237 dc
.SetFont(self
.GetFont())
239 dc
.SetTextForeground(self
.GetForegroundColour())
241 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
242 label
= self
.GetLabel()
243 tw
, th
= dc
.GetTextExtent(label
)
245 dw
= dy
= self
.labelDelta
246 dc
.DrawText(label
, (width
-tw
)/2+dw
, (height
-th
)/2+dy
)
249 def DrawFocusIndicator(self
, dc
, w
, h
):
251 self
.focusIndPen
.SetColour(self
.focusClr
)
252 dc
.SetLogicalFunction(wx
.INVERT
)
253 dc
.SetPen(self
.focusIndPen
)
254 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
255 dc
.DrawRectangle(bw
+2,bw
+2, w
-bw
*2-4, h
-bw
*2-4)
256 dc
.SetLogicalFunction(wx
.COPY
)
259 def OnPaint(self
, event
):
260 (width
, height
) = self
.GetClientSizeTuple()
265 dc
= wx
.BufferedPaintDC(self
)
269 colBg
= self
.GetBackgroundColour()
270 brush
= wx
.Brush(colBg
, wx
.SOLID
)
271 if self
.style
& wx
.BORDER_NONE
:
272 myAttr
= self
.GetDefaultAttributes()
273 parAttr
= self
.GetParent().GetDefaultAttributes()
274 myDef
= colBg
== myAttr
.colBg
275 parDef
= self
.GetParent().GetBackgroundColour() == parAttr
.colBg
277 if wx
.Platform
== "__WXMAC__":
278 brush
.MacSetTheme(1) # 1 == kThemeBrushDialogBackgroundActive
279 elif wx
.Platform
== "__WXMSW__":
280 if self
.DoEraseBackground(dc
):
282 elif myDef
and not parDef
:
283 colBg
= self
.GetParent().GetBackgroundColour()
284 brush
= wx
.Brush(colBg
, wx
.SOLID
)
286 brush
= wx
.Brush(self
.faceDnClr
, wx
.SOLID
)
287 if brush
is not None:
288 dc
.SetBackground(brush
)
291 self
.DrawBezel(dc
, x1
, y1
, x2
, y2
)
292 self
.DrawLabel(dc
, width
, height
)
293 if self
.hasFocus
and self
.useFocusInd
:
294 self
.DrawFocusIndicator(dc
, width
, height
)
297 def OnLeftDown(self
, event
):
298 if not self
.IsEnabled():
307 def OnLeftUp(self
, event
):
308 if not self
.IsEnabled() or not self
.HasCapture():
310 if self
.HasCapture():
312 if not self
.up
: # if the button was down when the mouse was released...
319 def OnMotion(self
, event
):
320 if not self
.IsEnabled() or not self
.HasCapture():
322 if event
.LeftIsDown() and self
.HasCapture():
323 x
,y
= event
.GetPositionTuple()
324 w
,h
= self
.GetClientSizeTuple()
325 if self
.up
and x
<w
and x
>=0 and y
<h
and y
>=0:
329 if not self
.up
and (x
<0 or y
<0 or x
>=w
or y
>=h
):
336 def OnGainFocus(self
, event
):
338 dc
= wx
.ClientDC(self
)
339 w
, h
= self
.GetClientSizeTuple()
341 self
.DrawFocusIndicator(dc
, w
, h
)
344 def OnLoseFocus(self
, event
):
345 self
.hasFocus
= False
346 dc
= wx
.ClientDC(self
)
347 w
, h
= self
.GetClientSizeTuple()
349 self
.DrawFocusIndicator(dc
, w
, h
)
352 def OnKeyDown(self
, event
):
353 if self
.hasFocus
and event
.KeyCode() == ord(" "):
359 def OnKeyUp(self
, event
):
360 if self
.hasFocus
and event
.KeyCode() == ord(" "):
367 #----------------------------------------------------------------------
369 class GenBitmapButton(GenButton
):
370 """A generic bitmap button."""
372 def __init__(self
, parent
, ID
, bitmap
,
373 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
374 style
= 0, validator
= wx
.DefaultValidator
,
376 self
.bmpDisabled
= None
378 self
.bmpSelected
= None
379 self
.SetBitmapLabel(bitmap
)
380 GenButton
.__init
__(self
, parent
, ID
, "", pos
, size
, style
, validator
, name
)
383 def GetBitmapLabel(self
):
385 def GetBitmapDisabled(self
):
386 return self
.bmpDisabled
387 def GetBitmapFocus(self
):
389 def GetBitmapSelected(self
):
390 return self
.bmpSelected
393 def SetBitmapDisabled(self
, bitmap
):
394 """Set bitmap to display when the button is disabled"""
395 self
.bmpDisabled
= bitmap
397 def SetBitmapFocus(self
, bitmap
):
398 """Set bitmap to display when the button has the focus"""
399 self
.bmpFocus
= bitmap
400 self
.SetUseFocusIndicator(False)
402 def SetBitmapSelected(self
, bitmap
):
403 """Set bitmap to display when the button is selected (pressed down)"""
404 self
.bmpSelected
= bitmap
406 def SetBitmapLabel(self
, bitmap
, createOthers
=True):
408 Set the bitmap to display normally.
409 This is the only one that is required. If
410 createOthers is True, then the other bitmaps
411 will be generated on the fly. Currently,
412 only the disabled bitmap is generated.
414 self
.bmpLabel
= bitmap
415 if bitmap
is not None and createOthers
:
416 image
= wx
.ImageFromBitmap(bitmap
)
417 imageutils
.grayOut(image
)
418 self
.SetBitmapDisabled(wx
.BitmapFromImage(image
))
421 def _GetLabelSize(self
):
422 """ used internally """
423 if not self
.bmpLabel
:
425 return self
.bmpLabel
.GetWidth()+2, self
.bmpLabel
.GetHeight()+2, False
427 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
429 if self
.bmpDisabled
and not self
.IsEnabled():
430 bmp
= self
.bmpDisabled
431 if self
.bmpFocus
and self
.hasFocus
:
433 if self
.bmpSelected
and not self
.up
:
434 bmp
= self
.bmpSelected
435 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
437 dw
= dy
= self
.labelDelta
438 hasMask
= bmp
.GetMask() != None
439 dc
.DrawBitmap(bmp
, (width
-bw
)/2+dw
, (height
-bh
)/2+dy
, hasMask
)
442 #----------------------------------------------------------------------
445 class GenBitmapTextButton(GenBitmapButton
):
446 """A generic bitmapped button with text label"""
447 def __init__(self
, parent
, ID
, bitmap
, label
,
448 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
449 style
= 0, validator
= wx
.DefaultValidator
,
451 GenBitmapButton
.__init
__(self
, parent
, ID
, bitmap
, pos
, size
, style
, validator
, name
)
455 def _GetLabelSize(self
):
456 """ used internally """
457 w
, h
= self
.GetTextExtent(self
.GetLabel())
458 if not self
.bmpLabel
:
459 return w
, h
, True # if there isn't a bitmap use the size of the text
461 w_bmp
= self
.bmpLabel
.GetWidth()+2
462 h_bmp
= self
.bmpLabel
.GetHeight()+2
468 return width
, height
, True
471 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
473 if bmp
!= None: # if the bitmap is used
474 if self
.bmpDisabled
and not self
.IsEnabled():
475 bmp
= self
.bmpDisabled
476 if self
.bmpFocus
and self
.hasFocus
:
478 if self
.bmpSelected
and not self
.up
:
479 bmp
= self
.bmpSelected
480 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
482 dw
= dy
= self
.labelDelta
483 hasMask
= bmp
.GetMask() != None
485 bw
= bh
= 0 # no bitmap -> size is zero
487 dc
.SetFont(self
.GetFont())
489 dc
.SetTextForeground(self
.GetForegroundColour())
491 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
493 label
= self
.GetLabel()
494 tw
, th
= dc
.GetTextExtent(label
) # size of text
496 dw
= dy
= self
.labelDelta
498 pos_x
= (width
-bw
-tw
)/2+dw
# adjust for bitmap and text to centre
500 dc
.DrawBitmap(bmp
, pos_x
, (height
-bh
)/2+dy
, hasMask
) # draw bitmap if available
501 pos_x
= pos_x
+ 2 # extra spacing from bitmap
503 dc
.DrawText(label
, pos_x
+ dw
+bw
, (height
-th
)/2+dy
) # draw the text
506 #----------------------------------------------------------------------
510 def SetToggle(self
, flag
):
519 def OnLeftDown(self
, event
):
520 if not self
.IsEnabled():
522 self
.saveUp
= self
.up
523 self
.up
= not self
.up
528 def OnLeftUp(self
, event
):
529 if not self
.IsEnabled() or not self
.HasCapture():
531 if self
.HasCapture():
534 if self
.up
!= self
.saveUp
:
537 def OnKeyDown(self
, event
):
540 def OnMotion(self
, event
):
541 if not self
.IsEnabled():
543 if event
.LeftIsDown() and self
.HasCapture():
544 x
,y
= event
.GetPositionTuple()
545 w
,h
= self
.GetClientSizeTuple()
546 if x
<w
and x
>=0 and y
<h
and y
>=0:
547 self
.up
= not self
.saveUp
550 if (x
<0 or y
<0 or x
>=w
or y
>=h
):
551 self
.up
= self
.saveUp
556 def OnKeyUp(self
, event
):
557 if self
.hasFocus
and event
.KeyCode() == ord(" "):
558 self
.up
= not self
.up
566 class GenToggleButton(__ToggleMixin
, GenButton
):
567 """A generic toggle button"""
570 class GenBitmapToggleButton(__ToggleMixin
, GenBitmapButton
):
571 """A generic toggle bitmap button"""
574 class GenBitmapTextToggleButton(__ToggleMixin
, GenBitmapTextButton
):
575 """A generic toggle bitmap button with text label"""
578 #----------------------------------------------------------------------