Hi I'm sort new to wxPython and still in the process of learning. I'm trying to make a bitmap button using a particular image like this for example: http://i.min.us/idk3Uy.png
The catch is I want to retain the original shape of the image in the button, like a circular button for example instead of a rectangular one (which is default).
I want to know how to do that exactly or if it is possible to do that at all; I did take a look at the documentation and I've found the style constant wx.BU_EXACTFIT removes the unnecessary borders...but it's still not in the desirable shape that I want it to be.
Thanks.
You're probably going to have to implement a custom control for this. I've done my fair share of custom wxPython controls, so I went ahead and wrote a ShapedButton
class for you. =)
To run this demo, you just need three images:
- button-normal.png
- button-pressed.png
- button-disabled.png
The three images are used depending on the state of the button. Only the "normal" one is required, but you probably want to at least provide "normal" and "pressed" so the user gets feedback when clicking.
The control only responds to clicks in non-transparent areas of the normal bitmap. It properly fires the EVT_BUTTON
event when clicked and released.
Enjoy!
import wx
class ShapedButton(wx.PyControl):
def __init__(self, parent, normal, pressed=None, disabled=None):
super(ShapedButton, self).__init__(parent, -1, style=wx.BORDER_NONE)
self.normal = normal
self.pressed = pressed
self.disabled = disabled
self.region = wx.RegionFromBitmapColour(normal, wx.Color(0, 0, 0, 0))
self._clicked = False
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
self.Bind(wx.EVT_LEFT_DCLICK, self.on_left_dclick)
self.Bind(wx.EVT_LEFT_UP, self.on_left_up)
self.Bind(wx.EVT_MOTION, self.on_motion)
self.Bind(wx.EVT_LEAVE_WINDOW, self.on_leave_window)
def DoGetBestSize(self):
return self.normal.GetSize()
def Enable(self, *args, **kwargs):
super(ShapedButton, self).Enable(*args, **kwargs)
self.Refresh()
def Disable(self, *args, **kwargs):
super(ShapedButton, self).Disable(*args, **kwargs)
self.Refresh()
def post_event(self):
event = wx.CommandEvent()
event.SetEventObject(self)
event.SetEventType(wx.EVT_BUTTON.typeId)
wx.PostEvent(self, event)
def on_size(self, event):
event.Skip()
self.Refresh()
def on_paint(self, event):
dc = wx.AutoBufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetParent().GetBackgroundColour()))
dc.Clear()
bitmap = self.normal
if self.clicked:
bitmap = self.pressed or bitmap
if not self.IsEnabled():
bitmap = self.disabled or bitmap
dc.DrawBitmap(bitmap, 0, 0)
def set_clicked(self, clicked):
if clicked != self._clicked:
self._clicked = clicked
self.Refresh()
def get_clicked(self):
return self._clicked
clicked = property(get_clicked, set_clicked)
def on_left_down(self, event):
x, y = event.GetPosition()
if self.region.Contains(x, y):
self.clicked = True
def on_left_dclick(self, event):
self.on_left_down(event)
def on_left_up(self, event):
if self.clicked:
x, y = event.GetPosition()
if self.region.Contains(x, y):
self.post_event()
self.clicked = False
def on_motion(self, event):
if self.clicked:
x, y = event.GetPosition()
if not self.region.Contains(x, y):
self.clicked = False
def on_leave_window(self, event):
self.clicked = False
def main():
def on_button(event):
print 'Button was clicked.'
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, 'Shaped Button Demo')
panel = wx.Panel(frame, -1)
button = ShapedButton(panel,
wx.Bitmap('button-normal.png'),
wx.Bitmap('button-pressed.png'),
wx.Bitmap('button-disabled.png'))
button.Bind(wx.EVT_BUTTON, on_button)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.AddStretchSpacer(1)
sizer.Add(button, 0, wx.ALIGN_CENTER)
sizer.AddStretchSpacer(1)
panel.SetSizer(sizer)
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()