wxWidgets/wxPython/wx/lib/buttons.py

564 lines
17 KiB
Python
Raw Normal View History

#----------------------------------------------------------------------
# Name: wx.lib.buttons
# Purpose: Various kinds of generic buttons, (not native controls but
# self-drawn.)
#
# Author: Robin Dunn
#
# Created: 9-Dec-1999
# RCS-ID: $Id$
# Copyright: (c) 1999 by Total Control Software
# Licence: wxWindows license
#----------------------------------------------------------------------
# 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o Updated for wx namespace
# o Tested with updated demo
#
"""
This module implements various forms of generic buttons, meaning that
they are not built on native controls but are self-drawn.
The GenButton is the base. It acts like a normal button but you
are able to better control how it looks, bevel width, colours, etc.
GenBitmapButton is a button with one or more bitmaps that show
the various states the button can be in.
GenToggleButton stays depressed when clicked, until clicked again.
GenBitmapToggleButton the same but with bitmaps.
"""
import wx
import imageutils
#----------------------------------------------------------------------
class GenButtonEvent(wx.PyCommandEvent):
def __init__(self, eventType, ID):
wx.PyCommandEvent.__init__(self, eventType, ID)
self.isDown = False
self.theButton = None
def SetIsDown(self, isDown):
self.isDown = isDown
def GetIsDown(self):
return self.isDown
def SetButtonObj(self, btn):
self.theButton = btn
def GetButtonObj(self):
return self.theButton
#----------------------------------------------------------------------
class GenButton(wx.PyControl):
labelDelta = 1
def __init__(self, parent, ID, label,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
cstyle = style
if cstyle == 0:
cstyle = wx.NO_BORDER
wx.PyControl.__init__(self, parent, ID, pos, size, cstyle, validator, name)
self.up = True
self.hasFocus = False
if style & wx.NO_BORDER:
self.bezelWidth = 0
self.useFocusInd = False
else:
self.bezelWidth = 2
self.useFocusInd = True
self.SetLabel(label)
self.InheritAttributes()
self.SetBestFittingSize(size)
self.InitColours()
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
if wx.Platform == '__WXMSW__':
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_SET_FOCUS, self.OnGainFocus)
self.Bind(wx.EVT_KILL_FOCUS, self.OnLoseFocus)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def SetBestSize(self, size=None):
"""
Given the current font and bezel width settings, calculate
and set a good size.
"""
if size is None:
size = wx.DefaultSize
wx.PyControl.SetBestFittingSize(self, size)
def DoGetBestSize(self):
"""
Overridden base class virtual. Determines the best size of the
button based on the label and bezel size.
"""
w, h, useMin = self._GetLabelSize()
defSize = wx.Button.GetDefaultSize()
width = 12 + w
if useMin and width < defSize.width:
width = defSize.width
height = 11 + h
if useMin and height < defSize.height:
height = defSize.height
width = width + self.bezelWidth - 1
height = height + self.bezelWidth - 1
return (width, height)
def AcceptsFocus(self):
"""Overridden base class virtual."""
return self.IsShown() and self.IsEnabled()
def GetDefaultAttributes(self):
"""
Overridden base class virtual. By default we should use
the same font/colour attributes as the native Button.
"""
return wx.Button.GetClassDefaultAttributes()
def ShouldInheritColours(self):
"""
Overridden base class virtual. Buttons usually don't inherit
the parent's colours.
"""
return False
def Enable(self, enable=True):
wx.PyControl.Enable(self, enable)
self.Refresh()
def SetBezelWidth(self, width):
"""Set the width of the 3D effect"""
self.bezelWidth = width
def GetBezelWidth(self):
"""Return the width of the 3D effect"""
return self.bezelWidth
def SetUseFocusIndicator(self, flag):
"""Specifiy if a focus indicator (dotted line) should be used"""
self.useFocusInd = flag
def GetUseFocusIndicator(self):
"""Return focus indicator flag"""
return self.useFocusInd
def InitColours(self):
"""
Calculate a new set of highlight and shadow colours based on
the background colour. Works okay if the colour is dark...
"""
faceClr = self.GetBackgroundColour()
r, g, b = faceClr.Get()
fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
self.faceDnClr = wx.Colour(fr, fg, fb)
sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
self.shadowPen = wx.Pen(wx.Colour(sr,sg,sb), 1, wx.SOLID)
hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
self.highlightPen = wx.Pen(wx.Colour(hr,hg,hb), 1, wx.SOLID)
self.focusClr = wx.Colour(hr, hg, hb)
textClr = self.GetForegroundColour()
if wx.Platform == "__WXMAC__":
self.focusIndPen = wx.Pen(textClr, 1, wx.SOLID)
else:
self.focusIndPen = wx.Pen(textClr, 1, wx.USER_DASH)
self.focusIndPen.SetDashes([1,1])
self.focusIndPen.SetCap(wx.CAP_BUTT)
def SetBackgroundColour(self, colour):
wx.PyControl.SetBackgroundColour(self, colour)
self.InitColours()
def SetForegroundColour(self, colour):
wx.PyControl.SetForegroundColour(self, colour)
self.InitColours()
def SetDefault(self):
self.GetParent().SetDefaultItem(self)
def _GetLabelSize(self):
""" used internally """
w, h = self.GetTextExtent(self.GetLabel())
return w, h, True
def Notify(self):
evt = GenButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
evt.SetIsDown(not self.up)
evt.SetButtonObj(self)
evt.SetEventObject(self)
self.GetEventHandler().ProcessEvent(evt)
def DrawBezel(self, dc, x1, y1, x2, y2):
# draw the upper left sides
if self.up:
dc.SetPen(self.highlightPen)
else:
dc.SetPen(self.shadowPen)
for i in range(self.bezelWidth):
dc.DrawLine(x1+i, y1, x1+i, y2-i)
dc.DrawLine(x1, y1+i, x2-i, y1+i)
# draw the lower right sides
if self.up:
dc.SetPen(self.shadowPen)
else:
dc.SetPen(self.highlightPen)
for i in range(self.bezelWidth):
dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
dc.DrawLine(x2-i, y1+i, x2-i, y2)
def DrawLabel(self, dc, width, height, dw=0, dy=0):
dc.SetFont(self.GetFont())
if self.IsEnabled():
dc.SetTextForeground(self.GetForegroundColour())
else:
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
label = self.GetLabel()
tw, th = dc.GetTextExtent(label)
if not self.up:
dw = dy = self.labelDelta
dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy)
def DrawFocusIndicator(self, dc, w, h):
bw = self.bezelWidth
self.focusIndPen.SetColour(self.focusClr)
dc.SetLogicalFunction(wx.INVERT)
dc.SetPen(self.focusIndPen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4)
dc.SetLogicalFunction(wx.COPY)
def OnPaint(self, event):
(width, height) = self.GetClientSizeTuple()
x1 = y1 = 0
x2 = width-1
y2 = height-1
dc = wx.BufferedPaintDC(self)
if self.up:
dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
else:
dc.SetBackground(wx.Brush(self.faceDnClr, wx.SOLID))
dc.Clear()
self.DrawBezel(dc, x1, y1, x2, y2)
self.DrawLabel(dc, width, height)
if self.hasFocus and self.useFocusInd:
self.DrawFocusIndicator(dc, width, height)
def OnEraseBackground(self, event):
pass
def OnLeftDown(self, event):
if not self.IsEnabled():
return
self.up = False
self.CaptureMouse()
self.SetFocus()
self.Refresh()
event.Skip()
def OnLeftUp(self, event):
if not self.IsEnabled() or not self.HasCapture():
return
if self.HasCapture():
self.ReleaseMouse()
if not self.up: # if the button was down when the mouse was released...
self.Notify()
self.up = True
self.Refresh()
event.Skip()
def OnMotion(self, event):
if not self.IsEnabled() or not self.HasCapture():
return
if event.LeftIsDown() and self.HasCapture():
x,y = event.GetPositionTuple()
w,h = self.GetClientSizeTuple()
if self.up and x<w and x>=0 and y<h and y>=0:
self.up = False
self.Refresh()
return
if not self.up and (x<0 or y<0 or x>=w or y>=h):
self.up = True
self.Refresh()
return
event.Skip()
def OnGainFocus(self, event):
self.hasFocus = True
dc = wx.ClientDC(self)
w, h = self.GetClientSizeTuple()
if self.useFocusInd:
self.DrawFocusIndicator(dc, w, h)
def OnLoseFocus(self, event):
self.hasFocus = False
dc = wx.ClientDC(self)
w, h = self.GetClientSizeTuple()
if self.useFocusInd:
self.DrawFocusIndicator(dc, w, h)
def OnKeyDown(self, event):
if self.hasFocus and event.KeyCode() == ord(" "):
self.up = False
self.Refresh()
event.Skip()
def OnKeyUp(self, event):
if self.hasFocus and event.KeyCode() == ord(" "):
self.up = True
self.Notify()
self.Refresh()
event.Skip()
#----------------------------------------------------------------------
class GenBitmapButton(GenButton):
def __init__(self, parent, ID, bitmap,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
self.bmpDisabled = None
self.bmpFocus = None
self.bmpSelected = None
self.SetBitmapLabel(bitmap)
GenButton.__init__(self, parent, ID, "", pos, size, style, validator, name)
def GetBitmapLabel(self):
return self.bmpLabel
def GetBitmapDisabled(self):
return self.bmpDisabled
def GetBitmapFocus(self):
return self.bmpFocus
def GetBitmapSelected(self):
return self.bmpSelected
def SetBitmapDisabled(self, bitmap):
"""Set bitmap to display when the button is disabled"""
self.bmpDisabled = bitmap
def SetBitmapFocus(self, bitmap):
"""Set bitmap to display when the button has the focus"""
self.bmpFocus = bitmap
self.SetUseFocusIndicator(False)
def SetBitmapSelected(self, bitmap):
"""Set bitmap to display when the button is selected (pressed down)"""
self.bmpSelected = bitmap
def SetBitmapLabel(self, bitmap, createOthers=True):
"""
Set the bitmap to display normally.
This is the only one that is required. If
createOthers is True, then the other bitmaps
will be generated on the fly. Currently,
only the disabled bitmap is generated.
"""
self.bmpLabel = bitmap
if bitmap is not None and createOthers:
image = wx.ImageFromBitmap(bitmap)
imageutils.grayOut(image)
self.SetBitmapDisabled(wx.BitmapFromImage(image))
def _GetLabelSize(self):
""" used internally """
if not self.bmpLabel:
return -1, -1, False
return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False
def DrawLabel(self, dc, width, height, dw=0, dy=0):
bmp = self.bmpLabel
if self.bmpDisabled and not self.IsEnabled():
bmp = self.bmpDisabled
if self.bmpFocus and self.hasFocus:
bmp = self.bmpFocus
if self.bmpSelected and not self.up:
bmp = self.bmpSelected
bw,bh = bmp.GetWidth(), bmp.GetHeight()
if not self.up:
dw = dy = self.labelDelta
hasMask = bmp.GetMask() != None
dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask)
#----------------------------------------------------------------------
class GenBitmapTextButton(GenBitmapButton): # generic bitmapped button with Text Label
def __init__(self, parent, ID, bitmap, label,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
GenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name)
self.SetLabel(label)
def _GetLabelSize(self):
""" used internally """
w, h = self.GetTextExtent(self.GetLabel())
if not self.bmpLabel:
return w, h, True # if there isn't a bitmap use the size of the text
w_bmp = self.bmpLabel.GetWidth()+2
h_bmp = self.bmpLabel.GetHeight()+2
width = w + w_bmp
if h_bmp > h:
height = h_bmp
else:
height = h
return width, height, True
def DrawLabel(self, dc, width, height, dw=0, dy=0):
bmp = self.bmpLabel
if bmp != None: # if the bitmap is used
if self.bmpDisabled and not self.IsEnabled():
bmp = self.bmpDisabled
if self.bmpFocus and self.hasFocus:
bmp = self.bmpFocus
if self.bmpSelected and not self.up:
bmp = self.bmpSelected
bw,bh = bmp.GetWidth(), bmp.GetHeight()
if not self.up:
dw = dy = self.labelDelta
hasMask = bmp.GetMask() != None
else:
bw = bh = 0 # no bitmap -> size is zero
dc.SetFont(self.GetFont())
if self.IsEnabled():
dc.SetTextForeground(self.GetForegroundColour())
else:
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
label = self.GetLabel()
tw, th = dc.GetTextExtent(label) # size of text
if not self.up:
dw = dy = self.labelDelta
pos_x = (width-bw-tw)/2+dw # adjust for bitmap and text to centre
if bmp !=None:
dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
pos_x = pos_x + 2 # extra spacing from bitmap
dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy) # draw the text
#----------------------------------------------------------------------
class __ToggleMixin:
def SetToggle(self, flag):
self.up = not flag
self.Refresh()
SetValue = SetToggle
def GetToggle(self):
return not self.up
GetValue = GetToggle
def OnLeftDown(self, event):
if not self.IsEnabled():
return
self.saveUp = self.up
self.up = not self.up
self.CaptureMouse()
self.SetFocus()
self.Refresh()
def OnLeftUp(self, event):
if not self.IsEnabled() or not self.HasCapture():
return
if self.HasCapture():
if self.up != self.saveUp:
self.Notify()
self.ReleaseMouse()
self.Refresh()
def OnKeyDown(self, event):
event.Skip()
def OnMotion(self, event):
if not self.IsEnabled():
return
if event.LeftIsDown() and self.HasCapture():
x,y = event.GetPositionTuple()
w,h = self.GetClientSizeTuple()
if x<w and x>=0 and y<h and y>=0:
self.up = not self.saveUp
self.Refresh()
return
if (x<0 or y<0 or x>=w or y>=h):
self.up = self.saveUp
self.Refresh()
return
event.Skip()
def OnKeyUp(self, event):
if self.hasFocus and event.KeyCode() == ord(" "):
self.up = not self.up
self.Notify()
self.Refresh()
event.Skip()
class GenToggleButton(__ToggleMixin, GenButton):
pass
class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
pass
class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
pass
#----------------------------------------------------------------------