Looped Canvas Redraw

In the following sample app, I create a main frame in MainGUI ans set
two panels within it. ButtonPanel() has a single blank button, and
PlotPanel()
is supposed to redraw a rectangle on each pass through the given
range. Using glcanvas in PlotPanel draws each one, but won't
Refresh(), or Update(). If I replace the glcanvas with a panel
instead, it looks like it does what I want, which is to redraw each
pass through, but it shows some, and not all of the redraws, no matter
how much I slow it down.

Is there a way to get a steady redraw rate, and if so how?

import wx
import random
import time
import wx.glcanvas

class MainGUI(wx.Frame):
  def __init__(self, parent, id, title):
    wx.Frame.__init__(self, parent, id, title, size=(600, 600))

    self.ppanel = PlotPanel(self, -1)
    self.bpanel = ButtonPanel(self, -1)
    self.hbox = wx.BoxSizer()
    self.hbox.Add(self.ppanel)
    self.hbox.Add(self.bpanel)
    self.SetSizer(self.hbox)
    
class ButtonPanel(wx.Panel):
  def __init__(self, parent, id):
    wx.Panel.__init__(self, parent, id, style = wx.BORDER_SUNKEN)
    self.changeb = wx.Button(self, -1)
    self.Bind(wx.EVT_BUTTON, self.GetParent().ppanel.OnClick, id =
self.changeb.GetId())

class PlotPanel(wx.Panel):
  def __init__(self, parent, id):
    wx.Panel.__init__(self, parent, id, style = wx.BORDER_SUNKEN)
    self.canvas = wx.glcanvas.GLCanvas(self, -1, size = (250,250))

  def OnClick(self, event):
    self.dc = wx.PaintDC(self.canvas)
    self.dc.SetPen(wx.Pen('#d4d4d4'))
    for i in range(1,20):
      self.random = random.randint(10,99)
      self.dc.SetBrush(wx.Brush('#c%ic%i' % (self.random,self.random)))
      self.dc.DrawRectangle(self.random, self.random, self.random, self.random)
      time.sleep(.175)
      self.Update()
      self.Refresh()
      self.canvas.GetGrandParent().UpdateWindowUI()

class MainApp(wx.App):
  '''Class Main App.'''
  def __init__(self, redirect = False):
    wx.App.__init__(self)

  def OnInit(self):
    '''Init Main App.'''
    #MainFrame Instance For MainGui
    self.frame = MainGUI(None, -1, 'Primary')

    self.frame.Show(True)
    self.SetTopWindow(self.frame)
    return True

#Main app instance
app = MainApp()
app.MainLoop()

Thanks,
David

You have a number of things wrong with your sample:

1. You are using a PaintDC in a button event. PaintDCs can only be used in Paint events.

2. The window passed to the DC must be the widget that paint event was generated for, not some other widget.

3. You should not use blocking functions, like time.sleep, in a loop to manage the timing of an animation. While the application is sleeping and not returning from the event handler it is not able to send or respond to other events, such as painting the next frame of your animation, or even painting other windows in your application. You should instead only paint one frame of the animation at a time and let the function return to the mainloop. Use a timer to trigger the drawing of the next animation frame.

···

On 10/22/10 6:42 AM, David Hutto wrote:

In the following sample app, I create a main frame in MainGUI ans set
two panels within it. ButtonPanel() has a single blank button, and
PlotPanel()
is supposed to redraw a rectangle on each pass through the given
range. Using glcanvas in PlotPanel draws each one, but won't
Refresh(), or Update(). If I replace the glcanvas with a panel
instead, it looks like it does what I want, which is to redraw each
pass through, but it shows some, and not all of the redraws, no matter
how much I slow it down.

Is there a way to get a steady redraw rate, and if so how?

--
Robin Dunn
Software Craftsman

Is there a way to get a steady redraw rate, and if so how?

Thanks,
David

After you make Robin's changes, use a wx.Timer to generate repetitive
timer events. You must know how often is "often enough, but not too
often" so that your app doesn't overwhelm your platform's processing
capability. I'm guessing you are using a MSW platform which is very
tolerant with fast wxPython refreshing.

Your timer handler code can then generate Paint events in which your
Paint handler code can redraw what you want to do.

Ray

After you make Robin's changes, use a wx.Timer to generate repetitive
timer events. You must know how often is "often enough, but not too
often" so that your app doesn't overwhelm your platform's processing
capability. I'm guessing you are using a MSW platform which is very
tolerant with fast wxPython refreshing.

Actually on Ubuntu 9.10. Thanks both to you and Robin for replying.
I'll be trying the recommendations in a little bit.

···

Your timer handler code can then generate Paint events in which your
Paint handler code can redraw what you want to do.

Ray

Actually on Ubuntu 9.10. Thanks both to you and Robin for replying.
I'll be trying the recommendations in a little bit.

Now that I know your platform isn't MSW, take a close look at this
thread. Robin pointed out some DC fast redrawing guideline that I
haven't found anywhere else. I'm not sure if the wx world, in general,
understands these things.

Thread "Using OnTimer and OnPaint together " @

http://groups.google.com/group/wxpython-users/browse_thread/thread/7f80521aca717b94/8a59abd406c70930?lnk=gst&q=Using+OnTimer+and+OnPaint+together#8a59abd406c70930

I haven't actually tried these things out, but I've gotten stung by my
own attempts that work great on MSW that fail on other platforms and
vice-versa. The nature of all the failures is either bright screen
flashing or black images in the areas under the DC's control.

If (when !) you get the timed redraws to work properly I'd like to
test it on MSW and other platforms. If all the test are successful I'd
be happy to write a wiki page on this subject. Documentation on wx
cross-platform-safe techniques seems to be sorely needed.

Ray