Changes in ClientDC on OS-X

It's not well documented in wx, but this sort of drawing isn't well
supported in OS X, and there's special mechanisms for doing it. These
special mechanisms are implemented by the wxOverlay class. I don't
know if it's documented except in the source yet.

···

On Dec 10, 2007 2:34 PM, Christopher Barker <Chris.Barker@noaa.gov> wrote:

Hi all,

I've just noticed that some of my use of wx.ClientDC is now broken in OS-X.

I use it to draw stuff temporarily with the mouse on top of whatever is
there, usually using wx.XOR, so that I can draw twice to get the
original image back. I haven't had a chance yet to make a simple sample
to examine it, but it's broken a bunch of stuff in FloatCanvas.

For example, in almost all FloatCanvas samples, when you click on the
Zoom in button, then drag with the left mouse button, you should get a
dotted-line rubber-band box. It works on Windows and GTK, but I no
longer get the rubber band box on OS-X. I'm not entirely sure when this
broke -- I'm running 2.8.7.1 (mac-unicode) now (the latest)

Sound familiar? Anyone know how to fix it?

Hi Chris,

<snip>
<snap>

Alternative implementation enclosed

I hope the implementation is Mac-specific, because on Windows it
flickers like mad :smiley:
However, it seems like (at least in the DC area), wxMac is parting
from the platform-indipendent approach which is the core of wxWidgets.
I had related problems with FlatNotebook on Mac and it looks like I am
going to have some more with the other controls as well :frowning:

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/

···

On Dec 11, 2007 1:52 AM, Christopher Barker wrote:

Andrea Gavana wrote:

Alternative implementation enclosed

I hope the implementation is Mac-specific, because on Windows it
flickers like mad :smiley:

Darn -- no I was hoping it was not platform specific. I think we could clean it up on Windows though -- but I only tested in on a Mac so far. Doing something with EVT_ERASE_BACKGROUND or something might fix it.

However, it seems like (at least in the DC area), wxMac is parting
from the platform-indipendent approach which is the core of wxWidgets.

Well, yes and no. The newer versions of wxMac use CoreGraphics, which is a probably better drawing paradigm. It's what's causing these issues. However, In poking around the wxOverlay stuff (which I still don't get at all) -- there are comments referring to both wxMac and Windows XP -- so I think once everything is moved to the more modern interfaces, we'll be better off. But the transition is painful.

Any know of any more docs or examples for wxOverlay? The C++ headers are useless to me. -- have I know idea what methods it's supposed to have or anything.

-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

Chris.Barker@noaa.gov

Christopher Barker wrote:

Christopher Barker wrote:

Chris Mellon wrote:

It's not well documented in wx, but this sort of drawing isn't well
supported in OS X, and there's special mechanisms for doing it.

Well, more poking around. I was thinking that there were issues with wxClientDC, but it's not that. The problem is with SetLogicalMode. It turns out that if I turn that off, the drawing works fine.

However, how can I draw something, then get the original back without it? I though the while XOR trick was really, really nifty!

I did do another implementation where I put the drawing of the line in method called in OnPaint, then called Refresh() when I wanted it drawn. This re-blits the window with every mouse move, but it does seem to be blazingly fast. However, I still have a problem:

I want to be able to draw a dotted line that I know the user will be able to see regardless of the colors that are on the screen. Drawing on top with dc.SetLogicalFunction(wx.XOR) did this beautifully. How else can I do it?

Try wx.INVERT instead of wx.XOR.

BTW, what version of OS X do you have? Your first sample worked fine for me on both 10.5 and 10.4.

My understanding of the changes related to wx.ClientDC are that it is intended to still work (although I know there are some situations that don't) but that it will be much more efficient to use a different drawing model. This is because when using the Quartz (aka CoreGraphics) APIs there is a rendering pipeline that is used by the system to help the system optimize and streamline what gets put on the screen. When you use a wx.ClientDC it has to interrupt the rendering pipeline, do the drawing you want, and then restart the rendering pipeline again. Although the real time needed for this overhead is pretty small, it can add up quickly when there are many refreshes per second. And it's my understanding that the rendering pipeline is for the whole system, not just your window (although I'm not 100% sure on that one) so suspending/restarting it a large amount can have consequences.

Anyway, the new recommended model to use is to do all drawing, if possible, from the EVT_PAINT handler. I know in the past it's been convenient for doing drawing directly from mouse event handlers with a wx.ClientDC to deal with things like doodles, or rubber-banding selection boxes, etc. In the past I broke it down into two different kinds of drawing, things that were temporary or needed done "right now" and things that were permanent, and I would use client DCs for the first category and a paint DC from the EVT_PAINT handler for the second. But if you shift your perspective a little you can see that there really isn't any such thing as temporary drawing since you'll want to be able to reproduce that same view from the EVT_PAINT handler in case the window gets damaged and refreshed while you want that temporary drawing to stay visible. And for the "right now" stuff I found that "real soon" was good enough and still quicker than my eye can detect any delay. The "real soon" essentially just translates to a self.Refresh() call, so instead of this in the old model:

  some event -> draw with wx.ClientDC

you would have this in the new style:

  some event -> set some data members -> call self.Refresh
  paint event -> draw with wx.PaintDC

On Mac using the Refresh adds an update to the rendering pipeline, and if there are other refreshes in the meantime, or portions of the window damaged by others, then they are coalesced into a single event so when it is time for your window to be updated your paint handler won't have to work as much. You can also use RefreshRect to clip the update to just the portion of the window you are changing, and if there are more updates before the paint event is sent then the rects get Unioned into a single update region that can be used in the paint handler to optimize your drawing further.

I'll try to come up with a sample showing the use of wx.Overlay...

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

Try wx.INVERT instead of wx.XOR.

Will do, when I get back to my Mac.

BTW, what version of OS X do you have? Your first sample worked fine for me on both 10.5 and 10.4.

I'm running 10.4 ( I think the next to last update) Python 2.5, but he way.

My understanding of the changes related to wx.ClientDC are that it is intended to still work (although I know there are some situations that don't)

It does seem to work -- I"m having an XOR problem, and that's an issue with PaintDC too.

but that it will be much more efficient to use a different drawing model.

I've actually got it in m y todo to re-factor this -- from a long conversation with Stefan et al a while back, but it's a fair bit of re-factoring -- I'm thinking of a pretty easy way to do it now though -- hmmm.

I would use client DCs for the first category and a paint DC from the EVT_PAINT handler for the second. But if you shift your perspective a little you can see that there really isn't any such thing as temporary drawing since you'll want to be able to reproduce that same view from the EVT_PAINT handler in case the window gets damaged and refreshed while you want that temporary drawing to stay visible.

True -- now that I think about it, I have some kludges in now to deal with that...

And for the "right now" stuff I found that "real soon" was good enough and still quicker than my eye can detect any delay.

I can't tell either -- blitting is really, really fast!

I'll try to come up with a sample showing the use of wx.Overlay...

That would be nice, though it sounds like I won't need it.

-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

Chris.Barker@noaa.gov

Robin Dunn wrote:

Try wx.INVERT instead of wx.XOR.

Still doesn't work :frowning:

BTW, what version of OS X do you have? Your first sample worked fine for me on both 10.5 and 10.4.

I think I may have stumbled on a bug specific to something about my system:

OS-X 10.4.11
PPC Dual G5
Python2.5 (how do I tell which sub-version??)
    >>> sys.version_info
        (2, 5, 0, 'final', 0)
wxPython 2.8.7.1 (from the build on the wxPython site)

Enclosed is my sample -- it should draw a line when you left mouse button drag -- it works with wxPython2.6. I THINK it worked with some older version of 2.8 -- but I'm not entirely sure how to go back.

-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

Chris.Barker@noaa.gov