drawing on top of widgets

Hi all,

Does wxpython support anything like Java Swing's "GlassPane", which
allows you to draw directly in front of a window's existing widgets?
This is common in GUI design code, for example, drawing controls in
front of the widgets being laid out.

The task I need to do is similar to GUI design; I need to draw in
front of a bunch of widgets, crossing widget boundaries (so I can't
really update the drawing code for each widget or something). I also
need to capture events going to the widgets while in this mode so
widget events are not activated.

I have no idea how to do this in wxPython. I've been thinking about
the possibility of somehow creating a transparent transient window in
front of the existing window somehow, but I don't know exactly how to
go about this, or if its the correct method.

Interestingly, this is one thing that is very easy in Java. But I
don't want to code in Java, this isn't the only part of the
application I have to make work, and we all know that Python is easier
in the general case. :slight_smile:

Dusty

Dusty Phillips wrote:

Does wxpython support anything like Java Swing's "GlassPane", which
allows you to draw directly in front of a window's existing widgets?
This is common in GUI design code, for example, drawing controls in
front of the widgets being laid out.

I don't know about GlassPane, but would a wx.WindowsDC work for you? The C++ help suggests it is available only on Windows, however...

-Peter

From what I understand, windows (subclasses of wx.Window) are drawn
after their parents. So, if you want to draw on top of some previously
existing window/widget, you could make a control that is a child of the
window/widget, and override its drawing behavior.

The following doesn't quite work the way I expect it to, but it should
give you an idea...

- Josiah

import wx

class drawover(wx.Window):
    def __init__(self, parent):
        wx.Window.__init__(self, parent)
        
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
        self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
    
    def OnFocus(self, evt):
        self.GetParent().SetFocus()
    
    def OnErase(self, evt):
        pass
    
    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        dc.BeginDrawing()
        dc.SetPen(wx.Pen("RED", 1))
        dc.DrawLineList([(0,0,100,100), (100,0,0,100)])
        dc.EndDrawing()

class frame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, size=(600,600))
        
        s = wx.BoxSizer(wx.VERTICAL)
        
        self.tc1 = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        s.Add(self.tc1, 1, wx.EXPAND)
        self.tc2 = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        s.Add(self.tc2, 1, wx.EXPAND)
        
        self.d = drawover(self.tc2)
        
        self.tc2.Bind(wx.EVT_SIZE, self.OnSize2)
        
        self.SetSizer(s)
        self.Layout()
    
    def OnSize2(self, evt):
        self.d.SetSize((101,101))

if __name__ == '__main__':
    a = wx.App(0)
    b = frame()
    b.Show(1)
    a.MainLoop()

···

"Dusty Phillips" <buchuki@gmail.com> wrote:

Hi all,

Does wxpython support anything like Java Swing's "GlassPane", which
allows you to draw directly in front of a window's existing widgets?
This is common in GUI design code, for example, drawing controls in
front of the widgets being laid out.

From what I understand, windows (subclasses of wx.Window) are drawn
after their parents. So, if you want to draw on top of some previously
existing window/widget, you could make a control that is a child of the
window/widget, and override its drawing behavior.

The following doesn't quite work the way I expect it to, but it should
give you an idea...

Your code segfaults without error, but changing the self.d =
drawover(self.tc2) to a self.d = drawover(self) does perform mostly
correctly; apparently the sizers are drawn first in this case; maybe
textctrls can't have children?

The problem if I do this is that the wx.Window has a grey background.
To function as I want, it has to be transparent. Is there an easy way
to clear the background?

Dusty

I /think/ that what you want to do is capture the draw background event and do nothing.
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) ...

grep the demo for many examples of use.

Phil

···

At 06:54 PM 10/31/2006, you wrote:

From what I understand, windows (subclasses of wx.Window) are drawn
after their parents. So, if you want to draw on top of some previously
existing window/widget, you could make a control that is a child of the
window/widget, and override its drawing behavior.

The following doesn't quite work the way I expect it to, but it should
give you an idea...

Your code segfaults without error, but changing the self.d =
drawover(self.tc2) to a self.d = drawover(self) does perform mostly
correctly; apparently the sizers are drawn first in this case; maybe
textctrls can't have children?

The problem if I do this is that the wx.Window has a grey background.
To function as I want, it has to be transparent. Is there an easy way
to clear the background?

Dusty

I /think/ that what you want to do is capture the draw background event and
do nothing.
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) ...

Hmm... Josiah's code already connects this to an empty (pass)
function. Must be something else required. I'll keep exploring; maybe
I need to enable an alpha channel somewhere.

Thanks again,
Dusty

> From what I understand, windows (subclasses of wx.Window) are drawn
> after their parents. So, if you want to draw on top of some previously
> existing window/widget, you could make a control that is a child of the
> window/widget, and override its drawing behavior.
>
>
> The following doesn't quite work the way I expect it to, but it should
> give you an idea...

Your code segfaults without error, but changing the self.d =
drawover(self.tc2) to a self.d = drawover(self) does perform mostly
correctly; apparently the sizers are drawn first in this case; maybe
textctrls can't have children?

It doesn't segfault using 2.6.3.3 or 2.7.1.3 on Windows, but what I was
trying to do is to have a child of the text control draw over the text
control without blocking what is underneath it. It still segfaults on
Linux, and trying to reparent the drawover window doesn't seem to do the
drawing right.

The problem if I do this is that the wx.Window has a grey background.
To function as I want, it has to be transparent. Is there an easy way
to clear the background?

Looking back in the archives, I remembered a thread with the subject of
"Custom red X control and wx.window styles documentation", that uses the
wx.TRANSPARENT_WINDOW style. Adding that to the class that I provided
seems to work, at least on Windows...

- Josiah

import wx

class drawover(wx.Window):
    def __init__(self, parent):
        wx.Window.__init__(self, parent, style=wx.TRANSPARENT_WINDOW)
        
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
        self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
        
        wx.FutureCall(100, self.callrefresh)
    
    def callrefresh(self):
        self.Refresh()
        wx.FutureCall(100, self.callrefresh)
    
    def OnFocus(self, evt):
        self.GetParent().SetFocus()
    
    def OnErase(self, evt):
        pass
    
    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        dc.BeginDrawing()
        dc.SetPen(wx.Pen("RED", 1))
        size = self.GetSize()
        dc.DrawLine(0, 0, size[0], size[1])
        dc.DrawLine(size[0], 0, 0, size[1])
        dc.EndDrawing()

class frame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, size=(600,600))
        
        s = wx.BoxSizer(wx.VERTICAL)
        
        self.tc1 = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        s.Add(self.tc1, 1, wx.EXPAND)
        self.tc2 = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        s.Add(self.tc2, 1, wx.EXPAND)
        
        self.d = drawover(self.tc2)
        
        self.tc2.Bind(wx.EVT_SIZE, self.OnSize2)
        
        self.SetSizer(s)
        self.Layout()
    
    def OnSize2(self, evt):
        self.d.SetSize(self.tc2.GetSize())
        evt.Skip()

if __name__ == '__main__':
    a = wx.App(0)
    b = frame()
    b.Show(1)
    a.MainLoop()

···

"Dusty Phillips" <buchuki@gmail.com> wrote:

Dusty Phillips wrote:

Hi all,

Does wxpython support anything like Java Swing's "GlassPane", which
allows you to draw directly in front of a window's existing widgets?
This is common in GUI design code, for example, drawing controls in
front of the widgets being laid out.

2.7 has the beginnings of support for this, with the wx.Overlay class. I haven't used it yet so I don't know how well it works. On OSX it is a true overlay, but on the other platforms it is implemented using a wxClientDC so it won't be drawn over the top of the child widgets.

···

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

Looking back in the archives, I remembered a thread with the subject of
"Custom red X control and wx.window styles documentation", that uses the
wx.TRANSPARENT_WINDOW style. Adding that to the class that I provided
seems to work, at least on Windows...

Hmmm, unfortunately, this "at least on Windows" clause is a bit of an
issue. On my linux system (wxpython 2.6.3.3 Arch Linux), its still
giving me a grey box behind the cross. :-/

I think I'll go with Java and its glass pane, or maybe even groovy. I
hate Java, but this project isn't important to me and I'm not going to
be the one maintaining it. :smiley: Write once, forgetaboudit.

Dusty

You know...you could use a wx.Grid with a single entry, use the
multi-line editor and renderer (if necessary), and add the over-draw on
the rendering yourself. Some hoops to jump through, but it may work for
you.

- Josiah

···

"Dusty Phillips" <buchuki@gmail.com> wrote:

> Looking back in the archives, I remembered a thread with the subject of
> "Custom red X control and wx.window styles documentation", that uses the
> wx.TRANSPARENT_WINDOW style. Adding that to the class that I provided
> seems to work, at least on Windows...

Hmmm, unfortunately, this "at least on Windows" clause is a bit of an
issue. On my linux system (wxpython 2.6.3.3 Arch Linux), its still
giving me a grey box behind the cross. :-/

I think I'll go with Java and its glass pane, or maybe even groovy. I
hate Java, but this project isn't important to me and I'm not going to
be the one maintaining it. :smiley: Write once, forgetaboudit.