I’ve been looking at sample apps, and don’t see this illustrated. I want to have a very long canvas (e.g. 50,000 pixels), with the window only showing a small part at any one time. When the window scrolls (or there is some other redraw request), I don’t want to draw everything – only the bits showing.
All the examples I found redraw everything on a Paint event.
Here are the stages in which I would like to proceed:
-
in the Paint event handler, how do I find out what part of the window is exposed and redraw that.
-
Further, for scrolling, how can I find the part of the window which has scrolled in and only update that.
-
Further, if scrolling is happening faster than drawing, throw out all Paint events except the last, so I don’t redraw a view which is already obsolete.
-
Finally, how do I draw invisibly, the suddenly paste the drawing in so it does not flicker? For my example, this might not be necessary, but I have more intense things in mind.
Answers to any appreciated.
Mark Fanty
P.S. I’m including my 115 line sample program derived from examples on the web. It has one scrollable drawing window which shows a wide graph. If done right, scrolling should be very fast. Right now, it redraws everything and is slow.
“”" sample3
“”"
import sys
import wx
import random
=================================
== code to make a random graph ==
···
#==================================
class Node(object):
def init(self, frame,index):
self.frame = frame
self.index = index
self.arcsIn = []
self.arcsOut = []
def addArc(n1, n2):
n1.arcsOut.append(n2)
n2.arcsIn.append(n1)
class Graph(object):
def init(self, numFrames = 1000, nodesPerFrame = 10, arcsPerNode = 3):
# make nodes
self.frames = [None]*numFrames
for f in xrange(0,numFrames):
nodelist = []
for i in xrange(0,nodesPerFrame):
nodelist.append(Node(f,i))
self.frames[f] = nodelist
# make arcs
for f in xrange(0,numFrames-1):
for node in self.frames[f]:
next = random.sample(self.frames[f+1], arcsPerNode)
for x in next:
addArc(node,x)
======================
== code for the GUI ==
#=======================
Size of the drawing page, in pixels.
PAGE_WIDTH = 52000
PAGE_HEIGHT = 500
#-----------------------------------------------------------
class DrawingFrame(wx.Frame):
“”" A frame to hold a drawing “”"
def __init__(self, parent, id, title):
""" Standard constructor.
"""
wx.Frame.__init__(self, parent, id, title,
style = wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS |
wx.NO_FULL_REPAINT_ON_RESIZE)
self.graph = Graph()
# topPanel can hold some controls later
self.topPanel = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
# drawPanel is the canvas
self.drawPanel = wx.ScrolledWindow(self.topPanel, -1,
style=wx.SUNKEN_BORDER)
self.drawPanel.SetBackgroundColour(wx.WHITE)
self.drawPanel.EnableScrolling(True, True)
self.drawPanel.SetScrollbars(20, 20, PAGE_WIDTH / 20, PAGE_HEIGHT / 20)
self.drawPanel.Bind(wx.EVT_PAINT, self.onPaintEvent)
# Position everything in the window.
topSizer = wx.BoxSizer(wx.VERTICAL)
topSizer.Add(self.drawPanel, 1, wx.EXPAND)
self.topPanel.SetAutoLayout(True)
self.topPanel.SetSizer(topSizer)
def onPaintEvent(self, event):
""" Not good. This redraws everything.
"""
dc = wx.PaintDC(self.drawPanel)
self.drawPanel.PrepareDC(dc)
dc.BeginDrawing()
for f in self.graph.frames:
for n in f:
xb = (n.frame+1)*50
yb = (n.index+1)*50
dc.DrawRectangle(xb-5, yb-5, 10, 10)
for a in n.arcsOut:
dc.DrawLine(xb,yb, (a.frame+1)*50, (a.index+1)*50)
dc.EndDrawing()
class GraphApp(wx.App):
“”" The main application object.
“”"
def OnInit(self):
“”" Initialise the application.
“”"
frame = DrawingFrame(None, -1, “Untitled”)
frame.Centre()
frame.Show(True)
return True
#--------------------------------------------------------------------
def main():
“”" Start up
“”"
global _app
_app = GraphApp(0)
_app.MainLoop()
if name == “main”:
main()