How to draw a rectangle on a screen

4,126 views
Skip to first unread message

Yuan-Liang Tang

unread,
Mar 23, 2011, 4:15:53 AM3/23/11
to wxPython-users
Hi there,
Could anybody tell me how to display an empty rectangle on the
screen (not within an window) with the specified position and size?
Thanks in advance.

Robin Dunn

unread,
Mar 23, 2011, 12:55:58 PM3/23/11
to wxpytho...@googlegroups.com
On 3/23/11 1:15 AM, Yuan-Liang Tang wrote:
> Hi there,
> Could anybody tell me how to display an empty rectangle on the
> screen (not within an window) with the specified position and size?

You can draw on a wx.ScreenDC, although it will be wiped out as soon as
the windows you are drawing over refresh themselves. Another option
would be to use a shaped frame such that only the areas covered by the
lines of the rectangle are not transparent. See the ShapedWindow sample
in the demo.

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

Tim Roberts

unread,
Mar 23, 2011, 1:18:02 PM3/23/11
to wxpytho...@googlegroups.com
Yuan-Liang Tang wrote:
> Hi there,
> Could anybody tell me how to display an empty rectangle on the
> screen (not within an window) with the specified position and size?

You just need to create a DC for the desktop window. Then, you can draw
on that just like you would in any of your own windows. It might be
easier to do this by using the GDI calls in PyWin32 directly, but if you
really want to do it in wx, you can. You just need to create a fake
window and make it think it is the desktop.

import wx
import win32gui

class Desktop(wx.Frame):
def __init__(self):
super(Desktop,self).__init__(None,-1,'')
self.AssociateHandle(win32gui.GetDesktopWindow())
dc = wx.WindowDC(self)
dc.DrawRectangle( 100, 100, 800, 800 )


app = wx.App(0)
mf = Desktop()
app.MainLoop()

This is not a very good example, because (1) it draws a filled
rectangle, not an empty rectangle, (2) there's no way to kill it except
by using Ctrl-C, but it shows you the general idea.

Users won't like this, of course, because you'll be drawing on top of
other windows that won't know they've been disturbed.

--
Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

Tim Roberts

unread,
Mar 23, 2011, 1:19:20 PM3/23/11
to wxpytho...@googlegroups.com
Tim Roberts wrote:
> Yuan-Liang Tang wrote:
>> Hi there,
>> Could anybody tell me how to display an empty rectangle on the
>> screen (not within an window) with the specified position and size?
> You just need to create a DC for the desktop window. ...

>
> class Desktop(wx.Frame):
> def __init__(self):
> super(Desktop,self).__init__(None,-1,'')
> self.AssociateHandle(win32gui.GetDesktopWindow())
> dc = wx.WindowDC(self)

Now I'm embarrassed, after seeing Robin's post. I didn't know about
wx.ScreenDC.

Ray Pasco

unread,
Mar 23, 2011, 3:02:44 PM3/23/11
to wxpytho...@googlegroups.com

"Windows" refers to "Microsoft Windows", not the wxPython kind.

Yuan-Liang Tang

unread,
Apr 20, 2011, 2:12:59 AM4/20/11
to wxPython-users
Thanks a lot, Robin. I used ScreenDC as you suggested and it worked.
One more question. What I intend to do is to repeatedly draw empty
rectangles on the screen. To be specific: the screen is divided into 4
areas (upper-left, upper-right, bottom-left, bottom-right), and I need
to draw, in turn, the 4 rectangles of the areas (actually, I draw 4
lines to represent a rectangle). So, how do I do the following: Draw 4
lines for one area, erase them, draw another 4 lines for the next
area, erase them, ... , and so on?

Thanks in advance.

Yuan-Liang Tang
-----------------------

On 3月24日, 上午12時55分, Robin Dunn <ro...@alldunn.com> wrote:
> On 3/23/11 1:15 AM, Yuan-Liang Tang wrote:
>
> > Hi there,
> >      Could anybody tell me how to display an emptyrectangleon the
> > screen (not within an window) with the specified position and size?
>
> You can draw on a wx.ScreenDC, although it will be wiped out as soon as
> the windows you are drawing over refresh themselves.  Another option
> would be to use a shaped frame such that only the areas covered by the
> lines of therectangleare not transparent.  See the ShapedWindow sample

Robin Dunn

unread,
Apr 20, 2011, 3:42:47 PM4/20/11
to wxpytho...@googlegroups.com
On 4/19/11 11:12 PM, Yuan-Liang Tang wrote:
> Thanks a lot, Robin. I used ScreenDC as you suggested and it worked.
> One more question. What I intend to do is to repeatedly draw empty
> rectangles on the screen. To be specific: the screen is divided into 4
> areas (upper-left, upper-right, bottom-left, bottom-right), and I need
> to draw, in turn, the 4 rectangles of the areas (actually, I draw 4
> lines to represent a rectangle).

If you set the DC's brush to wx.TRANSPARENT_BRUSH then you can use
DrawRectangle instead.

So, how do I do the following: Draw 4
> lines for one area, erase them, draw another 4 lines for the next
> area, erase them, ... , and so on?

Try using dc.SetLogicalFunction(wx.INVERT) or wx.XOR. Then you should
be able to draw the rectangle once to make it visible, and then draw it
again to restore what was there before. Keep in mind however that if
anything on the affected area of the screen changes due to another
program redrawing itself, or the user interacting with it, or etc. then
you'll probably end up with a bit of garbage left on the screen.

Yuan-Liang Tang

unread,
Apr 20, 2011, 11:15:43 PM4/20/11
to wxPython-users
It worked! Thanks a million.
One small problem though: for the following code, I was not able to
draw red rectangles; the borders are black. How should I fix it?

------------------

import wx
import time

app = wx.PySimpleApp()
dc = wx.ScreenDC()
screenWidth, screenHeight = wx.DisplaySize()
boxWidth = int(screenWidth/3+0.5)
boxHeight = int(screenHeight/3+0.5)
dc.StartDrawingOnTop(None)
dc.SetPen(wx.Pen('red', 2))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.INVERT)
for y in range(3):
y1 = boxHeight*y
for x in range(3):
x1 = boxWidth*x
dc.DrawRectangle(x1, y1, boxWidth, boxHeight)
time.sleep(1)
dc.DrawRectangle(x1, y1, boxWidth, boxHeight)

--------------

Tim Roberts

unread,
Apr 21, 2011, 12:43:44 PM4/21/11
to wxpytho...@googlegroups.com
Yuan-Liang Tang wrote:
> It worked! Thanks a million.
> One small problem though: for the following code, I was not able to
> draw red rectangles; the borders are black. How should I fix it?

When you set the ROP to wx.INVERT, the pen color is ignored. It just
negates every pixel.

You need to make a choice here. If you draw the rectangle in solid red,
then it isn't reversible. You will have lost the pixels underneath.
The only way to restore them would be to grab a copy before you draw,
and that's expensive.

As an alternative, you can try wx.XOR instead of wx.INVERT. That turns
black into red, although it will turn white into cyan. See if you like
that better.

If you really, really need solid red, then you have no choice but to
grab a copy of the untouched screen, and copy the pixels back when it's
time to erase the rectangle.

Yuan-Liang Tang

unread,
Apr 21, 2011, 10:45:06 PM4/21/11
to wxPython-users
I got the idea, thanks. Another alternative would be to create a
transparent frame using the "SetTransparent(0)" attribute. However,
its border will also be transparent. Is it possible to have a
transparent frame with visible borders?

On 4月22日, 上午12時43分, Tim Roberts <t...@probo.com> wrote:
> Yuan-Liang Tang wrote:
> > It worked! Thanks a million.
> > One small problem though: for the following code, I was not able to
> >drawred rectangles; the borders are black. How should I fix it?
>
> When you set the ROP to wx.INVERT, the pen color is ignored.  It just
> negates every pixel.
>
> You need to make a choice here.  If youdrawtherectanglein solid red,
> then it isn't reversible.  You will have lost the pixels underneath.
> The only way to restore them would be to grab a copy before youdraw,
> and that's expensive.
>
> As an alternative, you can try wx.XOR instead of wx.INVERT.  That turns
> black into red, although it will turn white into cyan.  See if you like
> that better.
>
> If you really, really need solid red, then you have no choice but to
> grab a copy of the untouched screen, and copy the pixels back when it's
> time to erase therectangle.
>
> --
> Tim Roberts, t...@probo.com
> Providenza & Boekelheide, Inc.

Robin Dunn

unread,
Apr 22, 2011, 12:28:57 PM4/22/11
to wxpytho...@googlegroups.com
On 4/21/11 7:45 PM, Yuan-Liang Tang wrote:
> I got the idea, thanks. Another alternative would be to create a
> transparent frame using the "SetTransparent(0)" attribute. However,
> its border will also be transparent. Is it possible to have a
> transparent frame with visible borders?

You can use wx.FRAME_SHAPED and make all but the desired rectangle
borders be fully transparent. The demo (see ShapedWindow.py) shows
using wx.FRAME_SHAPED with an image, but it can also be done with a
wx.Region constructed in other ways if needed.

yltang

unread,
Apr 25, 2011, 8:30:28 PM4/25/11
to wxPython-users
Thanks a lot, Robin. I've written some code as follows based on
hasenj's work (http://hasenj.wordpress.com/2009/04/14/making-a-fancy-
window-in-wxpython/). However, I still couldn't create a transparent
rectangle with visible red borders. I must have missed something.
Would you please tell me what the problem is with the program. Thanks.

--------------------------

import wx

class FancyFrame(wx.Frame):
def __init__(self, width, height):
wx.Frame.__init__(self, None, style = wx.STAY_ON_TOP |
wx.FRAME_NO_TASKBAR |
wx.FRAME_SHAPED, size=(width, height))
self.SetTransparent(100)
b = wx.EmptyBitmap(width, height)
dc = wx.MemoryDC()
dc.SelectObject(b)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetPen(wx.Pen('red', 2))
dc.DrawRectangle(10, 10, width-10, height-10)
dc.SelectObject(wx.NullBitmap)
self.SetShape(wx.RegionFromBitmap(b))
self.Bind(wx.EVT_KEY_UP, self.OnKeyDown)
self.Show(True)

def OnKeyDown(self, event):
"""quit if user press Esc"""
if event.GetKeyCode() == 27
self.Close(force=True)
else:
event.Skip()

if __name__ == "__main__":
app = wx.App()
f = FancyFrame(300, 300)
app.MainLoop()

-------------------

On 4月23日, 上午12時28分, Robin Dunn <ro...@alldunn.com> wrote:

Robin Dunn

unread,
Apr 28, 2011, 1:16:26 PM4/28/11
to wxpytho...@googlegroups.com
On 4/25/11 5:30 PM, yltang wrote:
> Thanks a lot, Robin. I've written some code as follows based on
> hasenj's work (http://hasenj.wordpress.com/2009/04/14/making-a-fancy-
> window-in-wxpython/). However, I still couldn't create a transparent
> rectangle with visible red borders. I must have missed something.
> Would you please tell me what the problem is with the program. Thanks.

wx.RegionFromBitmap uses the bitmap's mask to know where to set the
region, so you need to give the bitmap you are using a mask for it to
work. I've done it in the attached version of your sample code by
initializing the bitmap to black, and then after drawing the rectangle I
tell the bitmap to set its mask to the area covered by the black pixels.

rectframe.py

yltang

unread,
Apr 29, 2011, 1:21:37 AM4/29/11
to wxPython-users
Thanks a million, Robin. This is exactly what I needed. One last
question, if I change the statement "dc.DrawRectangle(10, 10,
width-10, height-10)" to dc.DrawRectangle(0, 0, width, height)", there
would be a thin black line at the right and bottom borders of the
frame. Is there a way to get rid of them? What I intend to do is to
draw the exact border of the transparent frame.
>  rectframe.py
> 1K檢視下載

Robin Dunn

unread,
Apr 29, 2011, 4:15:52 AM4/29/11
to wxpytho...@googlegroups.com
On 4/28/11 10:21 PM, yltang wrote:
> Thanks a million, Robin. This is exactly what I needed. One last
> question, if I change the statement "dc.DrawRectangle(10, 10,
> width-10, height-10)" to dc.DrawRectangle(0, 0, width, height)", there
> would be a thin black line at the right and bottom borders of the
> frame. Is there a way to get rid of them? What I intend to do is to
> draw the exact border of the transparent frame.

Try using the client size of the frame instead of the total size. Then
you'll be making only the areas that you can draw on visible instead of
including the border of the frame.

yltang

unread,
Apr 29, 2011, 5:19:50 AM4/29/11
to wxPython-users
Git it. Thanks a gain.

On 4月29日, 下午4時15分, Robin Dunn <ro...@alldunn.com> wrote:
> On 4/28/11 10:21 PM, yltang wrote:
>
> > Thanks a million, Robin. This is exactly what I needed. One last
> > question, if I change the statement "dc.DrawRectangle(10, 10,
> > width-10, height-10)" to dc.DrawRectangle(0, 0, width, height)", there
> > would be a thin black line at the right and bottom borders of the
> > frame. Is there a way to get rid of them? What I intend to do is to
> >drawthe exact border of the transparent frame.
>
> Try using the client size of the frame instead of the total size. Then
> you'll be making only the areas that you candrawon visible instead of
Reply all
Reply to author
Forward
0 new messages