How do you draw on client (wx.ClientDC) using wx.GCDC for alpha channel

323 views
Skip to first unread message

Steve Freedenburg

unread,
Mar 12, 2012, 1:36:27 AM3/12/12
to wxpytho...@googlegroups.com
Windows Vista box
Python 2.7.2
wxPython 2.8.12.0

Looking at the wxPython demo for Alpha Drawing I thought I could adapt my code to include a to use the wx.GCDC so I could use the alpha channel for blending my drawing with the background and other elements.  

In the demo the wx.GCDC is built in the OnPaint method which is bound to the EVT_PAINT event.  When I do this I get funky artifacts and the program breaks.
If I just use the wx.ClientDC everything works great (except there is no alpha element which would be nice.)

When I do it, nothing "breaks" perse'...  Just nothing draws on the client so I was under the impression GCDC only works with wx.PaintDC or I'm just not doing something right.

Could someone show me a way (if possible) to use the wx.ClientDC and wx.GCDC together?  




Kevin Ollivier

unread,
Mar 12, 2012, 11:32:22 AM3/12/12
to wxpytho...@googlegroups.com
Hi Steve,

On Mar 11, 2012, at 10:36 PM, Steve Freedenburg wrote:

Windows Vista box
Python 2.7.2
wxPython 2.8.12.0

Looking at the wxPython demo for Alpha Drawing I thought I could adapt my code to include a to use the wx.GCDC so I could use the alpha channel for blending my drawing with the background and other elements.  

In the demo the wx.GCDC is built in the OnPaint method which is bound to the EVT_PAINT event.  When I do this I get funky artifacts and the program breaks.
If I just use the wx.ClientDC everything works great (except there is no alpha element which would be nice.)

You don't explain how things break. My guess is that at least part of the issue is that you want to use wx.AutoBufferedPaintDC instead of wx.PaintDC, though.

When I do it, nothing "breaks" perse'...  Just nothing draws on the client so I was under the impression GCDC only works with wx.PaintDC or I'm just not doing something right.

Could someone show me a way (if possible) to use the wx.ClientDC and wx.GCDC together?

It's doubtful that wx.GCDC has been well-tested with wx.ClientDC, wx.ClientDC is made for a few special cases where you want to draw something over your UI temporarily (like a selection rect), and is not for drawing your program's primary UI. 

Regards,

Kevin

Robin Dunn

unread,
Mar 12, 2012, 11:58:24 AM3/12/12
to wxpytho...@googlegroups.com

Did you remove the EVT_PAINT handler? If so then it is probably
repainting the default window content (the background) over whatever you
drew with the client DC.

Please make a small runnable sample that shows what you are trying to do
so we can see what is going wrong.
http://wiki.wxpython.org/MakingSampleApps


--
Robin Dunn
Software Craftsman
http://wxPython.org

Steve Freedenburg

unread,
Mar 12, 2012, 7:40:23 PM3/12/12
to wxpytho...@googlegroups.com
Working on making the sample app now... should have it posted shortly.  Trying to add worth while comments as well.

BTW, Thanks so much for being polite and gracious hosts.  I know it can be hard to tolerate the novice. 

Steve Freedenburg

unread,
Mar 12, 2012, 8:22:02 PM3/12/12
to wxpytho...@googlegroups.com

I Read Andrea's reply about using a thread for GUI manipulation.  If I can do what I want to do in the main thread and not lock up the GUI in the mean time that would be great, but I'm shooting for some type of "animation" on a mouse over, and I'd like those animations to have an alpha channel.

I've come a long way from where I first started, but I am aware I have much farther to go before I can call myself anything other than a novice.  Thanks in advance!

Code below.

 import wx
import time
import thread
import wx.lib.inspection
 

print wx.version() 

# Thread that color changing boxes around the event object on the client.
# This works.
class MouseOverThread: 
    def __init__(self, EventObj, DC):
        self.EventObj = EventObj        
        self.DC = DC
    def Start(self):
        self.Working = self.running = True
        thread.start_new_thread(self.Run, ())
        
    def Stop(self):
        self.Working = False
        
    def IsRunning(self):
        return self.running
    
    def Run(self):
        while self.Working:
            self.ObjSize = self.EventObj.GetSize()
            self.ObjPos = self.EventObj.GetPosition()
            
            RGB = [(0, 255, 255), (0, 200, 255), (0, 150, 255), (0, 100, 255), (0, 50, 255), (0, 0, 255)]

            for Color in RGB:
                R, G, B = Color
                self.DC.SetPen(wx.Pen(wx.Colour(R, G, B)))
            
                LConstX = self.ObjPos[0] - 1
                LStartY = self.ObjPos[1]
                LEndY = self.ObjPos[1] + self.ObjSize[1]
                self.DC.DrawLine(LConstX, LStartY, LConstX, LEndY)
            
                RConstX = self.ObjPos[0] + self.ObjSize[0] 
                RStartY = self.ObjPos[1]
                REndY = self.ObjPos[1] + self.ObjSize[1]
                self.DC.DrawLine(RConstX, RStartY, RConstX, REndY)
        
                TStartX = self.ObjPos[0] - 1
                TEndX = self.ObjPos[0] + self.ObjSize[0] + 1
                TConstY = self.ObjPos[1] - 1
                self.DC.DrawLine(TStartX, TConstY, TEndX, TConstY)                
        
                BStartX = self.ObjPos[0] - 1
                BEndX = self.ObjPos[0] + self.ObjSize[0] + 1
                BConstY = self.ObjPos[1] + self.ObjSize[1] 
                self.DC.DrawLine(BStartX, BConstY, BEndX, BConstY)
                time.sleep(0.05)
                
                LConstX = self.ObjPos[0] - 2
                LStartY = self.ObjPos[1] - 1
                LEndY = self.ObjPos[1] + self.ObjSize[1] + 1
                self.DC.DrawLine(LConstX, LStartY, LConstX, LEndY)                

                RConstX = self.ObjPos[0] + self.ObjSize[0] + 1 
                RStartY = self.ObjPos[1] - 1
                REndY = self.ObjPos[1] + self.ObjSize[1] + 1
                self.DC.DrawLine(RConstX, RStartY, RConstX, REndY)

                TStartX = self.ObjPos[0] - 2
                TEndX = self.ObjPos[0] + self.ObjSize[0] + 2
                TConstY = self.ObjPos[1] - 2
                self.DC.DrawLine(TStartX, TConstY, TEndX, TConstY)

                BStartX = self.ObjPos[0] - 2
                BEndX = self.ObjPos[0] + self.ObjSize[0] + 2
                BConstY = self.ObjPos[1] + self.ObjSize[1] + 1
                self.DC.DrawLine(BStartX, BConstY, BEndX, BConstY)
                time.sleep(0.05)
                
                LConstX = self.ObjPos[0] - 3
                LStartY = self.ObjPos[1] - 2
                LEndY = self.ObjPos[1] + self.ObjSize[1] + 2
                self.DC.DrawLine(LConstX, LStartY, LConstX, LEndY)

                RConstX = self.ObjPos[0] + self.ObjSize[0] + 2 
                RStartY = self.ObjPos[1] - 2
                REndY = self.ObjPos[1] + self.ObjSize[1] + 2
                self.DC.DrawLine(RConstX, RStartY, RConstX, REndY)
                
                TStartX = self.ObjPos[0] - 3
                TEndX = self.ObjPos[0] + self.ObjSize[0] + 3
                TConstY = self.ObjPos[1] - 3
                self.DC.DrawLine(TStartX, TConstY, TEndX, TConstY)
                
                BStartX = self.ObjPos[0] - 3
                BEndX = self.ObjPos[0] + self.ObjSize[0] + 3
                BConstY = self.ObjPos[1] + self.ObjSize[1] + 2
                self.DC.DrawLine(BStartX, BConstY, BEndX, BConstY)
                time.sleep(0.05)
                
        self.running = False

      
class BasePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)

        self.SetBackgroundColour("yellow")        
        self.GP = self.GetParent()
        self.DC = wx.ClientDC(self)
        
        wx.StaticText(self, -1, "Hover over a button to trigger the EVT_ENTER_WINDOW event.", (10, 10))
        wx.StaticText(self, -1, "The event starts a thread that paints colored boxes on the",(10, 30))
        wx.StaticText(self, -1, "client, but around the event object no matter it's position",(10, 50))
        wx.StaticText(self, -1, "or size.  Leave the window to trigger the EVT_LEAVE_WINDOW", (10,70))
        wx.StaticText(self, -1, "even.  This stops the thread.", (10, 90))
        wx.StaticText(self, -1, "Help on making the exact same thing happen but with a GCDC", (10, 130))
        wx.StaticText(self, -1, "so the colored boxes can have an alpha channel would be great!", (10, 150))
        
        
        TestButton1 = wx.Button(self, -1, "Thanks", (40,200))
        TestButton2 = wx.Button(self, -1,  "For The", (130,200))
        TestButton3 = wx.Button(self, -1, "Help!!!!!!!!", (220,200))

        TestButton1.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)        
        TestButton1.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
        TestButton2.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)        
        TestButton2.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
        TestButton3.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)        
        TestButton3.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)        
    
    def OnGUIThreadStop(self):
        self.Refresh()
        self.t.Stop()
        
        running = 1
        
        while running:
            running = 0
            
            running = running + self.t.IsRunning()
            
            time.sleep(0.05)
        
    def OnWindowEnter(self, event):
        self.EventObj = event.GetEventObject()
        self.t = MouseOverThread(self.EventObj, self.DC)
        self.t.Start()

    def OnWindowLeave(self, event):
        self.OnGUIThreadStop()

        
    def OnCloseWindowClick(self, event):
        self.OnGUIThreadStop()
        self.GP.Close()
        
    def OnMinimizeWindowClick(self, event):
        self.GP.Iconize(True)

class BaseFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "wx.ClientDC test.py", size=(350,300), pos=(100,100))
        self.BP = BasePanel(self)
        
        wx.lib.inspection.InspectionTool().Show()

        
class SomaApp(wx.App):
    def OnInit(self):
        BF = BaseFrame()
        BF.Show(True)  
        return True

App = SomaApp(False)
App.MainLoop()    

Robin Dunn

unread,
Mar 13, 2012, 12:44:20 PM3/13/12
to wxpytho...@googlegroups.com
On 3/12/12 5:22 PM, Steve Freedenburg wrote:
>
> I Read Andrea's reply about using a thread for GUI manipulation. If
> I can do what I want to do in the main thread and not lock up the
> GUI in the mean time that would be great, but I'm shooting for some
> type of "animation" on a mouse over, and I'd like those animations
> to have an alpha channel.
>
>
> I've come a long way from where I first started, but I am aware I have
> much farther to go before I can call myself anything other than a
> novice. Thanks in advance!

For animation it is best to forget about using threads and simply use a
timer. Each iteration of the animation is performed in a single timer
event handler, and then you wait until the next timer event to do the
next iteration of the animation "loop". That keeps all UI interactions
in the main thread and still allows other events to be processed between
the animation iterations.

A basic flow could be something like this:

1. Something happens in the UI that you want to trigger some animation
2. Do any setup code needed, like getting/saving any parameters needed
for the animation.
3. Start() the timer
4. In the EVT_TIMER handler do one iteration of your animation loop
5. Setup for the next iteration of the animation loop if needed
6. If the animation is finished then Stop() the timer.
7. Return from the EVT_TIMER handler.


Depending on the platform and the nature of the animation it may be best
to not do any drawing at all in the timer handler, but rather just
change state as needed and then do a Refresh(rect=SomeRectangle) and
then do the drawing in the EVT_PAINT handler.

Chris Barker

unread,
Mar 13, 2012, 1:19:47 PM3/13/12
to wxpytho...@googlegroups.com
A few extra notes:

On Sun, Mar 11, 2012 at 10:36 PM, Steve Freedenburg
<stevefre...@gmail.com> wrote:
> In the demo the wx.GCDC is built in the OnPaint method which is bound to the
> EVT_PAINT event.  When I do this I get funky artifacts and the program
> breaks.
> If I just use the wx.ClientDC everything works great (except there is no
> alpha element which would be nice.)

As Kevin mentioned, this isn't what wx.ClientDC is meant for. You may
find that it works well on Windows, but it could work really badly on
OS-X (and maybe GTK?) and perhaps even newer versions of Windows.

And I've been able to use wxGCDC just fine in Paint handlers, so there
is probably something else wrong.

> Please make a small runnable sample that shows what you are trying to do
> so we can see what is going wrong.
> http://wiki.wxpython.org/MakingSampleApps

you sent a sample, but;

- it wasn't nearly as small as it should be.
- it's better to enclose the code, rather than pasting into your email
-- email clinets can make a mess of Pyhton code.

the goal is to make it really, really easy for us to download, test
and debug your example -- in this case, I took one look, and decided
it was going to be too much effort to bother -- though I did notice
the use of threads, which as Robin pointed out is probably not worth
it (if it's even possible). But in any case, you seem to mixing issues
here:

- using GCDC to do your main drawing
- animation

I'd get the first one working first.

-Chris

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

Chris....@noaa.gov

Reply all
Reply to author
Forward
0 new messages