Hi,
I'm using a wxScrolledWindow to create a zoomable drawing canvas (it is
to look at maps, but that's no relevant to the problem). I do this by
creating a zoom method, that changes the maxWidth and maxHeight of the
scrolled window, rescaled the stuff I'm drawing, then scrolls the window
to where I want it.
The problem is that the window re-paints itself a hwole bunch of times,
makeing it flash, and be kind of slow when there is lots of stuff in the
window. I put a few diagnostic "prints" in the code, and found that the
PAINT event is called four times when I zoom. How do I stop the window
from repainting until I'm done with all the zooming and scrolling?
I've enclosed the code, trimmed down as much as I could do easily. You
zoom in by clicking with the right mouse button, or using the menu.
(zoom out with the left button)
By the way, Robin, what changed with the drawing code in version 2.1.16?
I now have to assign EVT_PAINT(no big deal), and it seems to be quite a
bit faster! (Linux, wxGTK)
-Chris
--
Christopher Barker,
Ph.D.
cbarker@jps.net --- --- ---
http://www.jps.net/cbarker -----@@ -----@@ -----@@
------@@@ ------@@@ ------@@@
Water Resources Engineering ------ @ ------ @ ------ @
Coastal and Fluvial Hydrodynamics ------- --------- --------
------------------------------------------------------------------------
------------------------------------------------------------------------#!/u
sr/bin/env python
from wxPython.wx import *
import random
ID_ABOUT = 101
ID_EXIT = 102
ID_ZOOM_IN = 104
ID_ZOOM_OUT = 105
ID_ZOOM_TO_FIT = 106
class AppWindow(wxFrame): # The Main Window of the Application
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(500, 500))
self.CreateStatusBar()
self.SetStatusText("")
file_menu = wxMenu()
file_menu.Append(ID_EXIT, "E&xit","Terminate the program")
view_menu = wxMenu()
view_menu.Append(ID_ZOOM_IN, "Zoom &In","Zoom in")
view_menu.Append(ID_ZOOM_OUT, "Zoom &Out","Zoom out")
view_menu.Append(ID_ZOOM_TO_FIT, "Zoom to &Fit","Zoom to fit the window")
help_menu = wxMenu()
help_menu.Append(ID_ABOUT, "&About",
"More information About this program")
menuBar = wxMenuBar()
menuBar.Append(file_menu, "&File")
menuBar.Append(view_menu, "&View")
menuBar.Append(help_menu, "&Help")
self.SetMenuBar(menuBar)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
EVT_MENU(self, ID_ZOOM_IN, self.zoom_in)
EVT_MENU(self, ID_ZOOM_OUT, self.zoom_out)
EVT_MENU(self, ID_ZOOM_TO_FIT, self.zoom_to_fit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "This is a test program that tests\n"
"out a zooming scrolled window\n",
"About Me", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def zoom_in(self,event):
self.map_win.Zoom(event,2.0)
def zoom_out(self,event):
self.map_win.Zoom(event,0.5)
def zoom_to_fit(self,event):
self.map_win.Zoom_to_fit()
def TimeToQuit(self, event):
self.Close(true)
class Map_win(wxScrolledWindow):
def __init__(self, parent, id = -1, size = wxDefaultSize):
wxScrolledWindow.__init__(self, parent, id, wxPoint(0, 0), size,
wxSUNKEN_BORDER)
self.maxWidth = self.GetClientSize().width
self.maxHeight = self.GetClientSize().height
EVT_PAINT(self, self.OnPaint)
EVT_LEFT_DOWN(self, self.OnLeftButtonEvent)
EVT_RIGHT_DOWN(self, self.OnRightButtonEvent)
self.SetBackgroundColour(wxNamedColor("BLACK"))
self.SetCursor(wxStockCursor(wxCURSOR_CROSS))
self.SetScrollbars(20, 20, self.maxWidth/20, self.maxHeight/20)
def OnPaint(self, event=0):
print "\nOnPaint has been called"
dc = wxPaintDC(self)
self.PrepareDC(dc)
self.DoDrawing(dc)
def DoDrawing(self, dc):
print "DoDrawing has been called"
dc.BeginDrawing() # I think this only makes a difference in MSW
dc.SetBackground(wxCYAN_BRUSH)
dc.Clear()
dc.SetPen(wxBLACK_PEN)
#check aspect ratio
if (self.maxWidth/self.maxHeight < bb_delta_x / bb_delta_y):
convert = self.maxWidth / float((bb_delta_x))
else:
convert = self.maxHeight / float((bb_delta_y))
for polygon in polygons:
#convert to pixel coordinates
polygon_p =
for point in polygon:
polygon_p.append((int(convert * point[0]),int(convert*point[1])))
dc.SetBrush(wxBrush(wxNamedColour('RED')))
dc.DrawPolygon(polygon_p)
dc.EndDrawing()
def ConvertCoords(self,x,y):
xView, yView = self.ViewStart()
xDelta, yDelta = self.GetScrollPixelsPerUnit()
return (x + (xView * xDelta),y + (yView * yDelta))
def Zoom(self,event,factor):
print "\nZoom has been called"
zoom_limit = 4000.0
width = self.GetClientSize().width
height = self.GetClientSize().height
try:
if event.IsButton: # Mouse event, center on position
center_x,center_y = self.ConvertCoords(event.GetX(),event.GetY())
except AttributeError: # probably a menu event, center on window
center_x,center_y = self.ConvertCoords(width/2,height/2)
if factor > 1: #zooming in
if (self.maxWidth*factor > zoom_limit or
self.maxHeight*factor > zoom_limit):
#reset factor to limit
factor = zoom_limit/self.maxWidth
self.maxWidth = int(self.maxWidth*factor)
self.maxHeight = int(self.maxHeight*factor)
center_x,center_y = center_x*factor,center_y*factor
else:
self.maxWidth = int(self.maxWidth*factor)
self.maxHeight = int(self.maxHeight*factor)
center_x,center_y = center_x*factor,center_y*factor
else: # zooming out
if (self.maxWidth*factor < width and
self.maxHeight*factor < height):
self.maxWidth = width
self.maxHeight = height
center_x,center_y = self.ConvertCoords(width/2,
height/2)
else:
self.maxWidth = int(self.maxWidth*factor)
self.maxHeight = int(self.maxHeight*factor)
center_x,center_y = center_x*factor,center_y*factor
# compute where to scroll to center image
xDelta, yDelta = self.GetScrollPixelsPerUnit()
scroll_x = (center_x - width/2)/xDelta
scroll_y = (center_y - width/2)/yDelta
self.SetScrollbars(20, 20, self.maxWidth/20, self.maxHeight/20)
#dc = wxClientDC(self)
#self.DoDrawing(wxClientDC(self))
self.Scroll(scroll_x,scroll_y)
self.Refresh()
def Zoom_to_fit(self):
self.maxWidth = self.GetClientSize().width
self.maxHeight = self.GetClientSize().height
self.DoDrawing(wxClientDC(self))
self.SetScrollbars(20, 20, self.maxWidth/20, self.maxHeight/20)
self.Refresh()
def OnLeftButtonEvent(self,event):
self.Zoom(event,2.0)
def OnRightButtonEvent(self,event):
self.Zoom(event,0.5)
class MyApp(wxApp):
def OnInit(self):
frame = AppWindow(NULL, -1, "Map Display Tool")
self.map_win = Map_win(frame,-1,frame.GetClientSize())
frame.map_win = self.map_win
frame.Show(true)
self.SetTopWindow(frame)
return true
# Build a list of polygons
polygon = [(20,15),
(30,32.5),
(20,50),
(40,50),
(50,65),
(60,50),
(80,50),
(70,32.5),
(80,15),
(60,15),
(50,0),
(40,15)]
polygons =
for i in range(100):
dxdy = (random.randint(0,500),random.randint(0,500))
polygons.append(map(lambda xy,dxdy: (xy[0]+dxdy[0],xy[1]+dxdy[1]),
polygon,[dxdy]*len(polygon)))
bb_delta_x = 500
bb_delta_y = 500
app = MyApp(0)
app.MainLoop()