I'd like to redraw some graphics at regular intervals for a dashboard
app, using OnTimer together with OnPaint. When running the example
code below, I get an error message "wxPaintDC may be created only in
EVT_PAINT handler!".
This is basically my first wxPython project - any help appreciated!
Thanks,
Kevin
import random
import wx
import time
# Using Ontimer and OnPaint together to redraw at scheduled intervals
# snippet modified from http://www.daniweb.com/code/snippet216648.html
class MyPanel(wx.Panel):
""" class MyPanel creates a panel to draw on, inherits wx.Panel """
def __init__(self, parent, id):
# create a panel
wx.Panel.__init__(self, parent, id)
self.SetBackgroundColour("white")
# start the paint event for DrawRectangle() and FloodFill()
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.timer = wx.Timer(self)
self.timer.Start(1000) # 1000 milliseconds = 1 second
self.Bind(wx.EVT_TIMER, self.OnTimer)
def OnTimer(self, evt):
self.draw_rectangle()
def OnPaint(self, evt):
self.draw_rectangle()
def draw_rectangle(self):
"""set up the device context (DC) for painting"""
self.dc = wx.PaintDC(self)
self.dc.Clear()
self.dc.BeginDrawing()
self.dc.SetPen(wx.Pen("BLACK",1))
# draw a random rectangle ...
# set random RGB color for brush
r = random.randrange(256)
g = random.randrange(256)
b = random.randrange(256)
self.dc.SetBrush(wx.Brush((r, g, b), wx.SOLID))
# set random x, y, w, h for rectangle
w = random.randint(10, width1/2)
h = random.randint(10, height1/2)
x = random.randint(0, width1 - w)
y = random.randint(0, height1 - h)
self.dc.DrawRectangle(x, y, w, h)
self.dc.EndDrawing()
# free up the device context now
del self.dc
height1 = 350
width1 = 400
app = wx.PySimpleApp()
# create a window/frame, no parent, -1 is default ID
frame = wx.Frame(None, -1, "Draw a rectangle using Timer ...", size =
(width1, height1))
# call the derived class, -1 is default ID
MyPanel(frame,-1)
# show the frame
frame.Show(True)
# start the event loop
app.MainLoop()
right -- listen the message -- don't do that!
The right way to do what you want is to call:
def OnTimer(self, evt):
self.Refresh()
self.Update()
That will force a Paint event -- which can then handle in a Paint handler.
You can also use a wx.ClientDC, but these days it's considered better to
do the above -- it lets the OS handle the timing of screen drawing itself.
Search through the Wiki for various examples of useing teh DCs.
Also, I don't know what you're doing, but you may want to check out:
wx.lib.floatcanvas
It handles a lot of the drawing details for you.
-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
How about that "wxPython in Action" book - any good? Any other resources?
Thanks again,
Kevin
> --
> To unsubscribe, send email to wxPython-user...@googlegroups.com
> or visit http://groups.google.com/group/wxPython-users?hl=en
yes.
> Any other resources?
This list, the Wiki, and assorted Blogs -- Mike Driscoll's has some good
stuff.
Though note something on the Wiki's "how to learn wxPython" page:
"First: Learn Python" -- you need to understand python OO structure to
use wxPython -- so a little more work on general Python docs and
tutorials is a good idea -- I like "Dive into Python", but there are
lots of good ones.
-Chris
Thanks for your help, guys! Coming from a VB background, I'm still
wrapping my head around things like when to use "self" as a parameter
or prefix, manually coding handler bindings, etc...
How about that "wxPython in Action" book - any good? Any other resources?
> How about that "wxPython in Action" book - any good?
Highly recommended!!!
Malcolm
You can easily get the same, or even worse, flicker using a ClientDC. It
is not a magic bullet.
Flicker on MSW typically comes from two things. 1) Clearing the window
before drawing, and 2) allowing partially completed drawing steps to be
visible on the screen before the whole set of drawing operations is
completed.
Number 1 can be eliminated by catching EVT_ERASE_BACKGROUND and doing
nothing in the handler. This prevents the default handler from being
called which will clear the widget. You should also be able to
accomplish the same thing by calling
theWindow.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) although it's not clear
if that works on all platforms in 2.8. BTW, if you are not going to be
drawing the entire contents of the window in your EVT_PAINT then you
probably do not want to prevent the background from being cleared and
you will need to deal with this differently.
Number 2 can be solved by using a double buffered drawing technique.
Essentially you do all your drawing steps into a bitmap the same size as
the window and then when you are all done you draw that bitmap to the
screen. That way there are no partially completed drawing steps that
are visible to the user, there is just one step that they can see and
that is the completed drawing. There are a couple different approaches
that can be used for double buffering. The first is to (re)create the
bitmap each time you need to update the screen and draw the whole
content to it each time and the throw it away when you are done with
that paint event. The other is to keep the bitmap around and only
update the parts of it that have changed. There are also some DC
classes which derive from wx.MemoryDC that help you to do double
buffered drawing: wx.BufferedDC and wx.BufferedPaintDC. There are some
examples in the demo and the wiki.
--
Robin Dunn
Software Craftsman
http://wxPython.org
Thanks to ALL for their good advice... I'll be looking into the
suggested reading and learning all that I can...
Take care,
Kevin