I'm drawing a network graph with wx.PaintDC. For each link in my graph
I draw the source and destination port numbers on a box over the link
30pixels away from the endpoints. This works fine when I'm only
drawing the ports for a single link, but when I draw the ports for all
of the links (I'm testing on tree with 12 links) I get a major
performance hit. What strategies can I use to get my graph back to a
usable state?
# If extra info is desired draw ports
if link.hover or link.info:
dc.SetPen(wx.Pen(wx.Colour(58,58,58), 1))
dc.SetBrush(wx.Brush(wx.Colour(255,255,255)))
# Draw srcport
sx, sy = gmath.PointOnLine(dstpos, srcpos, -30)
ss = str(link.srcport)
sw, sh = dc.GetTextExtent(ss)
dc.DrawRectangle(sx-3, sy-3, sw+6, sh+6)
# Draw dstport
dx, dy = gmath.PointOnLine(srcpos, dstpos, -30)
ds = str(link.dstport)
dw, dh = dc.GetTextExtent(ds)
dc.DrawRectangle(dx-3, dy-3, dw+6, dh+6)
I should note the problem lies in dc.DrawText. Remove these calls and
everything runs smoothly. Is there a limit to how many times
dc.DrawText should be called per OnPaint?
···
On May 23, 12:42 pm, Jonathan <jonauncenso...@gmail.com> wrote:
I'm drawing a network graph with wx.PaintDC. For each link in my graph
I draw the source and destination port numbers on a box over the link
30pixels away from the endpoints. This works fine when I'm only
drawing the ports for a single link, but when I draw the ports for all
of the links (I'm testing on tree with 12 links) I get a major
performance hit. What strategies can I use to get my graph back to a
usable state?
\# If extra info is desired draw ports
if link\.hover or link\.info:
dc\.SetPen\(wx\.Pen\(wx\.Colour\(58,58,58\), 1\)\)
dc\.SetBrush\(wx\.Brush\(wx\.Colour\(255,255,255\)\)\)
\# Draw srcport
sx, sy = gmath\.PointOnLine\(dstpos, srcpos, \-30\)
ss = str\(link\.srcport\)
sw, sh = dc\.GetTextExtent\(ss\)
dc\.DrawRectangle\(sx\-3, sy\-3, sw\+6, sh\+6\)
\# Draw dstport
dx, dy = gmath\.PointOnLine\(srcpos, dstpos, \-30\)
ds = str\(link\.dstport\)
dw, dh = dc\.GetTextExtent\(ds\)
dc\.DrawRectangle\(dx\-3, dy\-3, dw\+6, dh\+6\)
dc\.DrawText\(ss, sx, sy\)
dc\.DrawText\(ds, dx, dy\)
I should note the problem lies in dc.DrawText. Remove these calls and
everything runs smoothly. Is there a limit to how many times
dc.DrawText should be called per OnPaint?
No, there is no "limit". How often is this being called? The mention
of "link.hover" and "link.info" makes me wonder if you are calling this
during mouse move operations, hundreds or thousands of times a second.
Is that the case?
You could save a little time by creating the wx.Pen and wx.Brush ahead
of time and caching them.
···
--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.
You're correct, this is being called under mouse operations. I've seen
the clipping region functions while browsing the documentation, but
I've read some comments talking about drawing performance that said
adding this wouldn't help much. Perhaps it would be better to isolate
this code into a separate drawing function that is only called when
the graph is phyically modified (ie a node is moved), although then I
need to deal with the text not being drawn over when refreshing the
graph.
···
On May 24, 12:42 pm, Tim Roberts <t...@probo.com> wrote:
Jonathan wrote:
> I should note the problem lies in dc.DrawText. Remove these calls and
> everything runs smoothly. Is there a limit to how many times
> dc.DrawText should be called per OnPaint?
No, there is no "limit". How often is this being called? The mention
of "link.hover" and "link.info" makes me wonder if you are calling this
during mouse move operations, hundreds or thousands of times a second.
Is that the case?
You could save a little time by creating the wx.Pen and wx.Brush ahead
of time and caching them.
--
Tim Roberts, t...@probo.com
Providenza & Boekelheide, Inc.
I've found that calling DrawText hundreds of times is usually pretty
fast -- thousands not so much.
You're correct, this is being called under mouse operations.
re-drawing eveyting on mouse_move is pretty sketcy -- you really need
blzingly fast drawing for that to feel smooth (liek OPneGL..).
Is something changing with every mouse move? what?
Is the common case, there is a little bit that might change on a
mouse_move, like a highlight of the object the mouse is over. In this
case, I'd look at using an overlay to draw just that.
or double-buffer and then draw the changing bit yourself (really jsut
a hand-done overlay)
(note: that page was written before wxOverlay existed)
HTH,
-Chris
Perhaps it would be better to isolate
this code into a separate drawing function that is only called when
the graph is phyically modified (ie a node is moved),
probably , yes.
you might want to take a look at wx.lib.floatcanvas -- it does a lot
of this for you.
for an example that might be close to what you need to do.
in any case, FloatCanvas itself may give you ideas about how to handle
double buffering, etc.
HTH,
-Chris
···
On Thu, May 24, 2012 at 10:09 AM, Jonathan <jonauncensored@gmail.com> wrote:
--
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
Also consider a design where only that which is changing is actually drawn, and cache as much of the rest of it as you can in one or more bitmaps.
For example if your window's content fits this model you could have a bitmap for the "background" (grid lines, labels, etc) for things that don't change very often if at all. Then another bitmap that is the background plus the current content. When the current content needs to change you don't have to redraw the entire background, just draw the background bitmap into the content bitmap via a wx.MemoryDC and then redraw the content on top of that. Then for immediate changes, such as in mouse events you can either manipulate just the section of the content bitmap that is changing or simply draw on top of it and then adjust the actual content bitmap in idle time or something. When paint events happen you just draw the content bitmap and you are done, so they are very efficient.
···
On 5/24/12 10:09 AM, Jonathan wrote:
You're correct, this is being called under mouse operations. I've seen
the clipping region functions while browsing the documentation, but
I've read some comments talking about drawing performance that said
adding this wouldn't help much. Perhaps it would be better to isolate
this code into a separate drawing function that is only called when
the graph is phyically modified (ie a node is moved), although then I
need to deal with the text not being drawn over when refreshing the
graph.
You're correct, this is being called under mouse operations. I've seen
the clipping region functions while browsing the documentation, but
I've read some comments talking about drawing performance that said
adding this wouldn't help much. Perhaps it would be better to isolate
this code into a separate drawing function that is only called when
the graph is phyically modified (ie a node is moved), although then I
need to deal with the text not being drawn over when refreshing the
graph.
Also consider a design where only that which is changing is actually drawn, and cache as much of the rest of it as you can in one or more bitmaps.
For example if your window's content fits this model you could have a bitmap for the "background" (grid lines, labels, etc) for things that don't change very often if at all. Then another bitmap that is the background plus the current content. When the current content needs to change you don't have to redraw the entire background, just draw the background bitmap into the content bitmap via a wx.MemoryDC and then redraw the content on top of that. Then for immediate changes, such as in mouse events you can either manipulate just the section of the content bitmap that is changing or simply draw on top of it and then adjust the actual content bitmap in idle time or something. When paint events happen you just draw the content bitmap and you are done, so they are very efficient.
Another thing that would help is to call RefreshRect() instead of Refresh, passing in the rect of whatever part of the canvas that needs redrawn, and clipping your drawing to it. (You can get the rect via window.GetUpdateRegion().GetBox()) Clipping does indeed affect performance quite considerably, because the calls to DrawText and such will do no drawing if they're outside the clipping rect.
Also, you can make sure to call RefreshRect only if something in the canvas needs redrawn, e.g. when the .hover or .info state of one of the links changes. This would probably significantly reduce the number of paint events you're sending.