[wxPython] Any way to do a wxWindow.RefreshNow()???

Hi guys,

In my application, I've just come across a nasty visual highlighting problem which seems to be caused by the fact that wxWindow.Refresh() queues a request to repaint the window rather than repainting it immediately.

I'm implementing a drag-and-drop like capability, but because of the type of visual highlighting I need I'm doing it myself rather than using the standard drag-and-drop built into wxWindows. To achieve this, I'm using:

     EVT_LEFT_CLICK(self, self.onMouseDown)
     EVT_LEFT_UP(self, self.onMouseUp)
     EVT_MOTION(self, self.onMouseMotion)

and then having my onMouseXXX methods do the appropriate visual highlighting to let the user drag an outline of the currently selected object(s) around on the screen. The actual visual highlighting is done by the following routines:

     def _showDragOutline(self):
         if not self.dragOutlineShown:
             self._invertDragOutline()
             self.dragOutlineShown = true

     def _hideDragOutline(self):
          if self.dragOutlineShown:
              self._invertDragOutline()
              self.dragOutlineShown = false

     def _invertDragOutline(self):
         dc = wxScreenDC()
         dc.SetLogicalFunction(wxINVERT)
         dc.StartDrawingOnTop()

         ...drawing code goes here...

         dc.EndDrawingOnTop()

So far so good -- the use of dc.SetLogicalFunction(wxINVERT) causes the drag outline to be drawn on the screen, and a second call to _invertDragOutline() causes the screen image to be restored. This lets me do nice things like:

     self._hideDragOutline()
     self.curDragPt = newPt
     self._showDragOutline()

to implement dragging. However, while dragging I also need to be able to refresh the contents of various sub-windows. At present, I'm calling wxWindow.Refresh() to force the sub-window to redraw itself, a bit like this:

     self._hideDragOutline()
     dropTarget = self.findTargetAt(mousePt)
     dropTarget.highlight(true)
     self._showDragOutline()

     ...

     class DropTarget:

         def highlight(self):
             self.highlighted = true
             self.Refresh()

This is where things fall apart. The call to self.Refresh() merely causes the drop target to *queue* a request to redraw itself -- which means that the order of redrawing ends up being:

     hide drag outline
     show drag outline
     redraw drop target

instead of the expected:

     hide drag outline
     redraw drop target
     show drag outline

As a result, an image of the drag outline remains on the screen where the affected window was redrawn. What I really need is some kind of wxWindow.RefreshNow() method, to force the window to be redrawn immediately. Is there any way of achieving this, perhaps by simulating an OnPaint() call?

Unfortunately, there are several things which can trigger the underlying windows to redraw themselves during a drag operation, so I really need to be able to redraw an entire *frame's* contents, including all subwindows, immediately rather than using Refresh() which queues the redraw request...

Has anyone else had similar problems -- and found a way around it? In many windowing toolkits, there's a distinction between:

  window.RequestRedraw()

and

  window.RedrawImmediately()

but I can't find an equivalent under wxPython. Any suggestions would be most welcome!

Thanks,

  - Erik.

In my application, I've just come across a nasty visual highlighting
problem which seems to be caused by the fact that wxWindow.Refresh()

queues

a request to repaint the window rather than repainting it immediately.

Correct. It invalidates the window or the subset of the window passed and
the platform GUI will then send a Paint message in response.

I'm implementing a drag-and-drop like capability, but because of the type
of visual highlighting I need I'm doing it myself rather than using the
standard drag-and-drop built into wxWindows.

Did you try wxDragImage? It should be able to get real close to what I
think you are trying to do.

to implement dragging. However, while dragging I also need to be able to
refresh the contents of various sub-windows. At present, I'm calling
wxWindow.Refresh() to force the sub-window to redraw itself, a bit like

this:

     self._hideDragOutline()
     dropTarget = self.findTargetAt(mousePt)
     dropTarget.highlight(true)
     self._showDragOutline()

     ...

     class DropTarget:

         def highlight(self):
             self.highlighted = true
             self.Refresh()

This is where things fall apart. The call to self.Refresh() merely causes
the drop target to *queue* a request to redraw itself -- which means that
the order of redrawing ends up being:

Instead of calling Refresh create a wxClientDC and pass it to the target
window's draw routine, (what ever it's OnPaint calls) or at least the part
that draw's the highlight.

Unfortunately, there are several things which can trigger the underlying
windows to redraw themselves during a drag operation, so I really need to
be able to redraw an entire *frame's* contents, including all subwindows,
immediately

That would be way slower and flickery than what you would want. It's much
better to find the windows affected and call their draw methods with a
wxClientDC.

···

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

Hi Robin,

> I'm implementing a drag-and-drop like capability, but because of the type
> of visual highlighting I need I'm doing it myself rather than using the
> standard drag-and-drop built into wxWindows.

Did you try wxDragImage? It should be able to get real close to what I
think you are trying to do.

I hadn't seen that, thanks! I'll have a look at converting my code when I have the chance...

> This is where things fall apart. The call to self.Refresh() merely causes
> the drop target to *queue* a request to redraw itself -- which means that
> the order of redrawing ends up being:

Instead of calling Refresh create a wxClientDC and pass it to the target
window's draw routine, (what ever it's OnPaint calls) or at least the part
that draw's the highlight.

Ah, yes, that'd work -- at least for the simple "drop target highlighting" case...

> Unfortunately, there are several things which can trigger the underlying
> windows to redraw themselves during a drag operation, so I really need to
> be able to redraw an entire *frame's* contents, including all subwindows,
> immediately

That would be way slower and flickery than what you would want. It's much
better to find the windows affected and call their draw methods with a
wxClientDC.

I see what you mean...though that immediately begs another question: how can I do this when the items I need to update are wxGrids with custom cell renderers? I have a feeling that may not be possible...about the only thing I can think of would be to figure out which cells are visible and manually call the renderer function for each visible cell -- which seems like a rather complex task to me!

Any suggestions?

Thanks, as always,

  - Erik.