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 #----------------------------------------------------------------------
15 This module implements various forms of generic buttons, meaning that
16 they are not built on native controls but are self-drawn.
18 The GenButton is the base. It acts like a normal button but you
19 are able to better control how it looks, bevel width, colours, etc.
21 GenBitmapButton is a button with one or more bitmaps that show
22 the various states the button can be in.
24 GenToggleButton stays depressed when clicked, until clicked again.
26 GenBitmapToggleButton the same but with bitmaps.
34 #----------------------------------------------------------------------
36 class GenButtonEvent(wx
.PyCommandEvent
):
37 def __init__(self
, eventType
, ID
):
38 wx
.PyCommandEvent
.__init
__(self
, eventType
, ID
)
42 def SetIsDown(self
, isDown
):
48 def SetButtonObj(self
, btn
):
51 def GetButtonObj(self
):
55 #----------------------------------------------------------------------
57 class GenButton(wx
.PyControl
):
60 def __init__(self
, parent
, ID
, label
,
61 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
62 style
= 0, validator
= wx
.DefaultValidator
,
66 wx
.PyControl
.__init
__(self
, parent
, ID
, pos
, size
, style
, validator
, name
)
71 self
.useFocusInd
= True
75 font
= parent
.GetFont()
77 font
= wx
.SystemSettings
.GetSystemFont(wx
.SYS_DEFAULT_GUI_FONT
)
79 self
.SetBestSize(size
)
82 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
83 self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
)
84 if wx
.Platform
== '__WXMSW__':
85 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDown
)
86 self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
)
87 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnGainFocus
)
88 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnLoseFocus
)
89 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
90 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
91 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
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
.Size(-1,-1)
102 if type(size
) == type(()):
103 size
= wx
.Size(size
[0], size
[1])
104 size
= wx
.Size(size
.width
, size
.height
) # make a copy
106 best
= self
.GetBestSize()
108 size
.width
= best
.width
109 if size
.height
== -1:
110 size
.height
= best
.height
115 def DoGetBestSize(self
):
116 """Overridden base class virtual. Determines the best size of the
117 button based on the label and bezel size."""
118 w
, h
, useMin
= self
._GetLabelSize
()
119 defSize
= wx
.Button
.GetDefaultSize()
121 if useMin
and width
< defSize
.width
:
122 width
= defSize
.width
124 if useMin
and height
< defSize
.height
:
125 height
= defSize
.height
126 width
= width
+ self
.bezelWidth
- 1
127 height
= height
+ self
.bezelWidth
- 1
128 return (width
, height
)
131 def AcceptsFocus(self
):
132 """Overridden base class virtual."""
133 return self
.IsShown() and self
.IsEnabled()
136 def Enable(self
, enable
=True):
137 wx
.PyControl
.Enable(self
, enable
)
141 def SetBezelWidth(self
, width
):
142 """Set the width of the 3D effect"""
143 self
.bezelWidth
= width
145 def GetBezelWidth(self
):
146 """Return the width of the 3D effect"""
147 return self
.bezelWidth
149 def SetUseFocusIndicator(self
, flag
):
150 """Specifiy if a focus indicator (dotted line) should be used"""
151 self
.useFocusInd
= flag
153 def GetUseFocusIndicator(self
):
154 """Return focus indicator flag"""
155 return self
.useFocusInd
158 def InitColours(self
):
159 faceClr
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_BTNFACE
)
160 textClr
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_BTNTEXT
)
161 self
.faceDnClr
= faceClr
162 self
.SetBackgroundColour(faceClr
)
163 self
.SetForegroundColour(textClr
)
165 shadowClr
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
166 highlightClr
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_BTNHIGHLIGHT
)
167 self
.shadowPen
= wx
.Pen(shadowClr
, 1, wx
.SOLID
)
168 self
.highlightPen
= wx
.Pen(highlightClr
, 1, wx
.SOLID
)
169 if wx
.Platform
== "__WXMAC__":
170 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.SOLID
)
172 self
.focusIndPen
= wx
.Pen(textClr
, 1, wx
.USER_DASH
)
173 self
.focusIndPen
.SetDashes([1,1])
174 self
.focusIndPen
.SetCap(wx
.CAP_BUTT
)
175 self
.focusClr
= highlightClr
178 def SetBackgroundColour(self
, colour
):
179 wx
.PyControl
.SetBackgroundColour(self
, colour
)
180 colour
= self
.GetBackgroundColour()
182 # Calculate a new set of highlight and shadow colours based on
183 # the new background colour. Works okay if the colour is dark...
184 r
, g
, b
= colour
.Get()
185 fr
, fg
, fb
= min(255,r
+32), min(255,g
+32), min(255,b
+32)
186 self
.faceDnClr
= wx
.Colour(fr
, fg
, fb
)
187 sr
, sg
, sb
= max(0,r
-32), max(0,g
-32), max(0,b
-32)
188 self
.shadowPen
= wx
.Pen(wx
.Colour(sr
,sg
,sb
), 1, wx
.SOLID
)
189 hr
, hg
, hb
= min(255,r
+64), min(255,g
+64), min(255,b
+64)
190 self
.highlightPen
= wx
.Pen(wx
.Colour(hr
,hg
,hb
), 1, wx
.SOLID
)
191 self
.focusClr
= wx
.Colour(hr
, hg
, hb
)
194 def _GetLabelSize(self
):
195 """ used internally """
196 w
, h
= self
.GetTextExtent(self
.GetLabel())
201 evt
= GenButtonEvent(wx
.wxEVT_COMMAND_BUTTON_CLICKED
, self
.GetId())
202 evt
.SetIsDown(not self
.up
)
203 evt
.SetButtonObj(self
)
204 evt
.SetEventObject(self
)
205 self
.GetEventHandler().ProcessEvent(evt
)
208 def DrawBezel(self
, dc
, x1
, y1
, x2
, y2
):
209 # draw the upper left sides
211 dc
.SetPen(self
.highlightPen
)
213 dc
.SetPen(self
.shadowPen
)
214 for i
in range(self
.bezelWidth
):
215 dc
.DrawLine((x1
+i
, y1
), (x1
+i
, y2
-i
))
216 dc
.DrawLine((x1
, y1
+i
), (x2
-i
, y1
+i
))
218 # draw the lower right sides
220 dc
.SetPen(self
.shadowPen
)
222 dc
.SetPen(self
.highlightPen
)
223 for i
in range(self
.bezelWidth
):
224 dc
.DrawLine((x1
+i
, y2
-i
), (x2
+1, y2
-i
))
225 dc
.DrawLine((x2
-i
, y1
+i
), (x2
-i
, y2
))
228 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
229 dc
.SetFont(self
.GetFont())
231 dc
.SetTextForeground(self
.GetForegroundColour())
233 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
234 label
= self
.GetLabel()
235 tw
, th
= dc
.GetTextExtent(label
)
237 dw
= dy
= self
.labelDelta
238 dc
.DrawText(label
, ((width
-tw
)/2+dw
, (height
-th
)/2+dy
))
241 def DrawFocusIndicator(self
, dc
, w
, h
):
244 ## self.focusIndPen.SetColour(self.GetForegroundColour())
246 ## #self.focusIndPen.SetColour(self.GetBackgroundColour())
247 ## self.focusIndPen.SetColour(self.GetForegroundColour())
248 self
.focusIndPen
.SetColour(self
.focusClr
)
249 dc
.SetLogicalFunction(wx
.INVERT
)
250 dc
.SetPen(self
.focusIndPen
)
251 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
252 dc
.DrawRectangle((bw
+2,bw
+2), (w
-bw
*2-4, h
-bw
*2-4))
253 dc
.SetLogicalFunction(wx
.COPY
)
256 def OnPaint(self
, event
):
257 (width
, height
) = self
.GetClientSizeTuple()
261 dc
= wx
.BufferedPaintDC(self
)
263 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour(), wx
.SOLID
))
265 dc
.SetBackground(wx
.Brush(self
.faceDnClr
, wx
.SOLID
))
267 self
.DrawBezel(dc
, x1
, y1
, x2
, y2
)
268 self
.DrawLabel(dc
, width
, height
)
269 if self
.hasFocus
and self
.useFocusInd
:
270 self
.DrawFocusIndicator(dc
, width
, height
)
273 def OnEraseBackground(self
, event
):
277 def OnLeftDown(self
, event
):
278 if not self
.IsEnabled():
287 def OnLeftUp(self
, event
):
288 if not self
.IsEnabled() or not self
.HasCapture():
290 if self
.HasCapture():
292 if not self
.up
: # if the button was down when the mouse was released...
299 def OnMotion(self
, event
):
300 if not self
.IsEnabled() or not self
.HasCapture():
302 if event
.LeftIsDown() and self
.HasCapture():
303 x
,y
= event
.GetPositionTuple()
304 w
,h
= self
.GetClientSizeTuple()
305 if self
.up
and x
<w
and x
>=0 and y
<h
and y
>=0:
309 if not self
.up
and (x
<0 or y
<0 or x
>=w
or y
>=h
):
316 def OnGainFocus(self
, event
):
318 dc
= wx
.ClientDC(self
)
319 w
, h
= self
.GetClientSizeTuple()
321 self
.DrawFocusIndicator(dc
, w
, h
)
324 def OnLoseFocus(self
, event
):
325 self
.hasFocus
= False
326 dc
= wx
.ClientDC(self
)
327 w
, h
= self
.GetClientSizeTuple()
329 self
.DrawFocusIndicator(dc
, w
, h
)
332 def OnKeyDown(self
, event
):
333 if self
.hasFocus
and event
.KeyCode() == ord(" "):
339 def OnKeyUp(self
, event
):
340 if self
.hasFocus
and event
.KeyCode() == ord(" "):
347 #----------------------------------------------------------------------
349 class GenBitmapButton(GenButton
):
350 def __init__(self
, parent
, ID
, bitmap
,
351 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
352 style
= 0, validator
= wx
.DefaultValidator
,
354 self
.bmpDisabled
= None
356 self
.bmpSelected
= None
357 self
.SetBitmapLabel(bitmap
)
358 GenButton
.__init
__(self
, parent
, ID
, "", pos
, size
, style
, validator
, name
)
361 def GetBitmapLabel(self
):
363 def GetBitmapDisabled(self
):
364 return self
.bmpDisabled
365 def GetBitmapFocus(self
):
367 def GetBitmapSelected(self
):
368 return self
.bmpSelected
371 def SetBitmapDisabled(self
, bitmap
):
372 """Set bitmap to display when the button is disabled"""
373 self
.bmpDisabled
= bitmap
375 def SetBitmapFocus(self
, bitmap
):
376 """Set bitmap to display when the button has the focus"""
377 self
.bmpFocus
= bitmap
378 self
.SetUseFocusIndicator(False)
380 def SetBitmapSelected(self
, bitmap
):
381 """Set bitmap to display when the button is selected (pressed down)"""
382 self
.bmpSelected
= bitmap
384 def SetBitmapLabel(self
, bitmap
, createOthers
=True):
386 Set the bitmap to display normally.
387 This is the only one that is required. If
388 createOthers is True, then the other bitmaps
389 will be generated on the fly. Currently,
390 only the disabled bitmap is generated.
392 self
.bmpLabel
= bitmap
393 if bitmap
is not None and createOthers
:
394 image
= wx
.ImageFromBitmap(bitmap
)
395 imageutils
.grayOut(image
)
396 self
.SetBitmapDisabled(wx
.BitmapFromImage(image
))
399 def _GetLabelSize(self
):
400 """ used internally """
401 if not self
.bmpLabel
:
403 return self
.bmpLabel
.GetWidth()+2, self
.bmpLabel
.GetHeight()+2, False
405 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
407 if self
.bmpDisabled
and not self
.IsEnabled():
408 bmp
= self
.bmpDisabled
409 if self
.bmpFocus
and self
.hasFocus
:
411 if self
.bmpSelected
and not self
.up
:
412 bmp
= self
.bmpSelected
413 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
415 dw
= dy
= self
.labelDelta
416 hasMask
= bmp
.GetMask() != None
417 dc
.DrawBitmap(bmp
, ((width
-bw
)/2+dw
, (height
-bh
)/2+dy
), hasMask
)
420 #----------------------------------------------------------------------
423 class GenBitmapTextButton(GenBitmapButton
): # generic bitmapped button with Text Label
424 def __init__(self
, parent
, ID
, bitmap
, label
,
425 pos
= wx
.DefaultPosition
, size
= wx
.DefaultSize
,
426 style
= 0, validator
= wx
.DefaultValidator
,
428 GenBitmapButton
.__init
__(self
, parent
, ID
, bitmap
, pos
, size
, style
, validator
, name
)
432 def _GetLabelSize(self
):
433 """ used internally """
434 w
, h
= self
.GetTextExtent(self
.GetLabel())
435 if not self
.bmpLabel
:
436 return w
, h
, True # if there isn't a bitmap use the size of the text
438 w_bmp
= self
.bmpLabel
.GetWidth()+2
439 h_bmp
= self
.bmpLabel
.GetHeight()+2
445 return width
, height
, True
448 def DrawLabel(self
, dc
, width
, height
, dw
=0, dy
=0):
450 if bmp
!= None: # if the bitmap is used
451 if self
.bmpDisabled
and not self
.IsEnabled():
452 bmp
= self
.bmpDisabled
453 if self
.bmpFocus
and self
.hasFocus
:
455 if self
.bmpSelected
and not self
.up
:
456 bmp
= self
.bmpSelected
457 bw
,bh
= bmp
.GetWidth(), bmp
.GetHeight()
459 dw
= dy
= self
.labelDelta
460 hasMask
= bmp
.GetMask() != None
462 bw
= bh
= 0 # no bitmap -> size is zero
464 dc
.SetFont(self
.GetFont())
466 dc
.SetTextForeground(self
.GetForegroundColour())
468 dc
.SetTextForeground(wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_GRAYTEXT
))
470 label
= self
.GetLabel()
471 tw
, th
= dc
.GetTextExtent(label
) # size of text
473 dw
= dy
= self
.labelDelta
475 pos_x
= (width
-bw
-tw
)/2+dw
# adjust for bitmap and text to centre
477 dc
.DrawBitmap(bmp
, (pos_x
, (height
-bh
)/2+dy
), hasMask
) # draw bitmap if available
478 pos_x
= pos_x
+ 2 # extra spacing from bitmap
480 dc
.DrawText(label
, (pos_x
+ dw
+bw
, (height
-th
)/2+dy
)) # draw the text
483 #----------------------------------------------------------------------
487 def SetToggle(self
, flag
):
496 def OnLeftDown(self
, event
):
497 if not self
.IsEnabled():
499 self
.saveUp
= self
.up
500 self
.up
= not self
.up
505 def OnLeftUp(self
, event
):
506 if not self
.IsEnabled() or not self
.HasCapture():
508 if self
.HasCapture():
509 if self
.up
!= self
.saveUp
:
514 def OnKeyDown(self
, event
):
517 def OnMotion(self
, event
):
518 if not self
.IsEnabled():
520 if event
.LeftIsDown() and self
.HasCapture():
521 x
,y
= event
.GetPositionTuple()
522 w
,h
= self
.GetClientSizeTuple()
523 if x
<w
and x
>=0 and y
<h
and y
>=0:
524 self
.up
= not self
.saveUp
527 if (x
<0 or y
<0 or x
>=w
or y
>=h
):
528 self
.up
= self
.saveUp
533 def OnKeyUp(self
, event
):
534 if self
.hasFocus
and event
.KeyCode() == ord(" "):
535 self
.up
= not self
.up
543 class GenToggleButton(__ToggleMixin
, GenButton
):
546 class GenBitmapToggleButton(__ToggleMixin
, GenBitmapButton
):
549 class GenBitmapTextToggleButton(__ToggleMixin
, GenBitmapTextButton
):
552 #----------------------------------------------------------------------