Help drawing on a button

Hi everybody,

I was hoping you could help me with my current problem: drawing on a button.
Basically I’d like to use a device context to draw on a simple wxPython bitmapped button (be it lines, ellipses, rectangles - you name it).
Here’s what I’ve tried first:

------------------------------------------------ Begin code ----------------------------------------------------

#!/usr/bin/env python
import wx

class MyFrame(wx.Frame):
def init(self, parent = None, id = -1, pos = (10,10), title = “HelloWorld”, size = (800,600)):
wx.Frame.init(self, parent,id,title, pos, size)
self.CreateStatusBar()
self.pnl = wx.Panel(self)
self.pnl.SetBackgroundColour(“blue”)
self.btn1 = wx.BitmapButton(self.pnl, pos = (50,50), size=(50,50))
dc = wx.ClientDC(self)
dc.SetBrush(wx.Brush(“black”))
dc.SetPen(wx.Pen(“black”,1))
dc.DrawLine(1,1,200,200)

class App(wx.App):
def OnInit(self):
self.frame = MyFrame()
self.frame.Show()
self.SetTopWindow(self.frame)
return True

def main():
app = App(False)
app.MainLoop()

if name == ‘main’:
main()

------------------------------------------------ End code ----------------------------------------------------

This doesn’t work - it doesn’t draw anything. I’m assuming it’s because it DOES actually draw something, but whatever IS drawn gets quickly overwritten by some
“maintenance” paint event of the button. Is this true?

With this in mind, I tried creating a buffered DC and drawing to the button upon catching an EVT_PAINT event. This is the code I’ve changed in my
init method:

------------------------------------------------ Begin code ----------------------------------------------------
def init(self, parent = None, id = -1, pos = (10,10), title = “HelloWorld”, size = (800,600)):
wx.Frame.init(self, parent,id,title, pos, size)
self.CreateStatusBar()
self.pnl = wx.Panel(self)
self.pnl.SetBackgroundColour(“blue”)
self.btn1 = wx.BitmapButton(self.pnl, pos = (50,50), size=(50,50))
self.btn1.Bind(wx.EVT_PAINT, self.PaintButton)
self.buffer=wx.EmptyBitmap(100,100)
dc=wx.BufferedDC(None, self.buffer)
dc.Clear()
dc.SetBrush(wx.Brush(“black”))
dc.SetPen(wx.Pen(“black”,1))
dc.DrawRectangle(10,10,100,100)

def PaintButton(self, event):
    dc = wx.BufferedPaintDC(self, self.buffer)

------------------------------------------------ End code ----------------------------------------------------

Not only does this not work, it also completely brings the program to a halt - probably trying to draw over and over again.
When I bind the EVT_PAINT to the panel in which the button is contained, the program doesn’t come to a halt, but still nothing is drawn.

So, my questions are (in addition to any guidance you can offer on the matter of drawing on buttons):
1.) Why doesn’t program #1 work?
2.) This is a bit off-topic, but I’ll ask it anyway: why does program #2 become hopelessly stuck when I bind the EVT_PAINT to the button? And why doesn’t it when I bind it to the panel?
3.) Why doesn’t program #2 (with self.Bind instead of self.btn1.Bind) work? Isn’t “catching” the EVT_PAINT enough?

Your help is most kindly appreciated :).

Assaf.

Hello,

Not only does this not work, it also completely brings the program to a halt - probably trying to draw over and over again.
When I bind the EVT_PAINT to the panel in which the button is contained, the program doesn't come to a halt, but still nothing is drawn.

So, my questions are (in addition to any guidance you can offer on the matter of drawing on buttons):
1.) Why doesn't program #1 work?

Try drawing into a MemoryDC to create a new bitmap and then call BitmapButton.SetBitmap to change the bitmap instead of drawing directly on the button. Doing this will likely clear up all your problems.

2.) This is a bit off-topic, but I'll ask it anyway: why does program #2 become hopelessly stuck when I bind the EVT_PAINT to the button? And why doesn't it when I bind it to the panel?

The native button widget may not allow user code to do drawing on it and when you try to do so there is likely some collision in the main loop.

3.) Why doesn't program #2 (with self.Bind instead of self.btn1.Bind) work? Isn't "catching" the EVT_PAINT enough?

With 'self.Bind' the event handler is going to receive the PaintEvent's for the panel, with 'self.btn1.Bind' its receiving the paint events for the Button.

For more information about this topic see the wiki: self.Bind vs. self.button.Bind - wxPyWiki

Your help is most kindly appreciated :).

Assaf.

Cody

···

On Jan 30, 2008, at 6:35 PM, Assaf Tal wrote:

Assaf Tal wrote:

Hi everybody,

I was hoping you could help me with my current problem: drawing on a button.
Basically I'd like to use a device context to draw on a simple wxPython bitmapped button (be it lines, ellipses, rectangles - you name it).

Have you seen the wx.lib.buttons module? It implements some generic (non-native) button classes.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Added note: just updated to wxPython 2.8.7.1, didn’t change anything.

Hey, I don't get any flicker here with your two sample programs.

Python is 2.5.1, wxPython 2.8.7.1. Difference is I'm running Vista here. Thought it'd be worth a note nonetheless.

Assaf Tal schrieb:

···

Robin,

Yes, I am aware of generic buttons - I've got your book and you seem to be advocating them pretty strongly, so I'll probably
switch over to them, but I need to resolve my problems with the regular buttons first for the sake of my understanding of how wxPython
works.

Ok, I've implemented Cody's suggestion. It works, but is has another problem :). The button flickers all the time!
It's worst when I move the mouse cursor over the button. At the risk of diverging, may I ask for your knowledgeable assistance
once again? :slight_smile:

First of all, here is the code, for the sake of whoever finds this thread asking the same questions (note that I just chose
to draw a simple line, nothing fancy. I also made the button very large to make the flickering obvious):

------------------------------------------------------------------- Begin Code -------------------------------------------------------------

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent = None, id = -1, pos = (10,10), title = "HelloWorld", size = (800,600)):
        wx.Frame.__init__(self, parent,id,title, pos, size)
        self.pnl = wx.Panel(self)
        self.pnl.SetBackgroundColour("blue")
        self.btn1 = wx.BitmapButton(self.pnl, pos = (50,50), size=(300,400))
        self.buffer = wx.EmptyBitmap(300,400)
        dc = wx.MemoryDC()
        dc.SelectObject(self.buffer)
        dc.SetPen(wx.Pen("green",1))
        dc.Clear()
        dc.DrawLine(1,1,200,200)
        self.btn1.SetBitmapLabel(self.buffer)

class App(wx.App):
    def OnInit(self):
        self.frame = MyFrame()
        self.frame.Show()
        self.SetTopWindow(self.frame)
        return True
   def main():
    app = App(False)
    app.MainLoop()
   if __name__ == '__main__':
    main()

--------------------------------------------------------------------- End Code ---------------------------------------------------------

I've done some experimenting with BitmapButton and found that the flickering phenomenon isn't a result of
using a device context directly. It happens even when I assign any bitmap image to the bitmap button. Here
is a simple example showing flicker (the file "tut3img1.GIF" is a 370x395 GIF file - you can substitute any
large file of your own):

------------------------------------------------------------------- Begin Code -------------------------------------------------------------

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent = None, id = -1, pos = (10,10), title = "HelloWorld", size = (800,600)):
        wx.Frame.__init__(self, parent,id,title, pos, size)
        panel = wx.Panel(self, -1)
        self.bmp = wx.Image("tut3img1.GIF", wx.BITMAP_TYPE_GIF).ConvertToBitmap()
        self.btn1 = wx.BitmapButton(panel, -1, self.bmp, pos = (50,50))

class App(wx.App):
    def OnInit(self):
        self.frame = MyFrame()
        self.frame.Show()
        #self.SetTopWindow(self.frame)
        return True
   def main():
    app = App(False)
    app.MainLoop()
   if __name__ == '__main__':
    main()

--------------------------------------------------------------------- End Code ---------------------------------------------------------

Any insights? Is this something to be expected of wx.BitmapButton? Is it a bug? Or am I doing something wrong?

Thank you.
Assaf.

PS
I'm using wxPython 2.6.4.0 <http://2.6.4.0> on windows XP.

On Jan 31, 2008 7:22 PM, Robin Dunn <robin@alldunn.com > <mailto:robin@alldunn.com>> wrote:

    Assaf Tal wrote:
     > Hi everybody,
     >
     > I was hoping you could help me with my current problem: drawing
    on a button.
     > Basically I'd like to use a device context to draw on a simple
    wxPython
     > bitmapped button (be it lines, ellipses, rectangles - you name it).

    Have you seen the wx.lib.buttons module? It implements some generic
    (non-native) button classes.

    --
    Robin Dunn
    Software Craftsman
    http://wxPython.org Java give you jitters? Relax with wxPython!

    ---------------------------------------------------------------------
    To unsubscribe, e-mail:
    wxPython-users-unsubscribe@lists.wxwidgets.org
    <mailto:wxPython-users-unsubscribe@lists.wxwidgets.org>
    For additional commands, e-mail:
    wxPython-users-help@lists.wxwidgets.org
    <mailto:wxPython-users-help@lists.wxwidgets.org>

Assaf Tal wrote:

Robin,

Yes, I am aware of generic buttons - I've got your book and you seem to be advocating them pretty strongly, so I'll probably
switch over to them, but I need to resolve my problems with the regular buttons first for the sake of my understanding of how wxPython
works.

Ok, I've implemented Cody's suggestion. It works, but is has another problem :). The button flickers all the time!
It's worst when I move the mouse cursor over the button. At the risk of diverging, may I ask for your knowledgeable assistance
once again? :slight_smile:

First of all, here is the code, for the sake of whoever finds this thread asking the same questions (note that I just chose
to draw a simple line, nothing fancy. I also made the button very large to make the flickering obvious):

My guess is that it is a bug in the native control on Windows as it is redrawing itself for the mouse-over events. It will become a bit more obvious that it is changing itself if you use the wx.BU_AUTODRAW style.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!