help needed with wx.GraphicsContext unwanted redraws

Hi,
From a really cool example in wx.demo I took some code and modified it heavily (graphicscontext).
It essentially now is a gauge (again), but circular and looks really good…
That is until some actions outside the window, triggers a (unwanted) repaint or refresh and the previous alpha bitmap is redraw on top on the last image, so it becomes darker. So when you, e.g. minimize and maximize the windows several times, a nice image is totally destroyed because it is becoming too dark.
I have played with the methods to prevent redrawing in this way, like clearing the whole background and repainting it using a double buffer, but …I failed, and failed again.
The original example in wx.demo performs even worse. Almost like when you look at it for the second time, the first image is copied on top on the original, making the alpha go away almost intermediate.
Please tell we how to avoid this unwanted redraw on actions like minimize/maximize.

I brought the code down to a minimum,but it still is pretty large. However when you forget about the colouring functions (explicitly marked in a block), you would be able to see a tree and not a forest.
I think you just need to look at
def UpdateCounter(self, counter=5, numCircles=1):
def OnPaint(self, evt):
def OnSize(self, event):
def Draw(self, dc):
def UpdateDrawing(self):

TIA, Samuel

circleGauge01.py (10.5 KB)

I modified the sample in the demo (attached) to use double buffering on Windows, so it has no flicker and doesn't do any unnecessary redraws. You should be able to adapt the changes to your code without too much hassle. Just look at the things that are done when USE_BUFFER is True.

Since you need to display a changing view instead of the fixed view like this example, then you'll want to decide how to update the buffer. You can either clear the buffer and redraw the whole thing, or you can just add to the buffer only the things that have changed since the last time you updated it.

Notice that when using buffering I don't do anything in the EVT_PAINT handler except let the wx.BufferedPaintDC blit my buffer to the screen. There is no need to do anything else since the complete drawing is already in the buffer bitmap. That means that even if you do things like draw other windows over the top there is no drawing being done except for that Blit and it is very fast.

GraphicsContext.py (6.49 KB)

···

On 3/23/10 12:45 PM, samuel samuel wrote:

Hi,
From a really cool example in wx.demo I took some code and modified it
heavily (graphicscontext).
It essentially now is a gauge (again), but circular and looks really good...
That is until some actions outside the window, triggers a (unwanted)
repaint or refresh and the previous alpha bitmap is redraw on top on the
last image, so it becomes darker. So when you, e.g. minimize and
maximize the windows several times, a nice image is totally destroyed
because it is becoming too dark.
I have played with the methods to prevent redrawing in this way, like
clearing the whole background and repainting it using a double buffer,
but ....I failed, and failed again.
The original example in wx.demo performs even worse. Almost like when
you look at it for the second time, the first image is copied on top on
the original, making the alpha go away almost intermediate.
Please tell we how to avoid this unwanted redraw on actions like
minimize/maximize.

--
Robin Dunn
Software Craftsman

Thanks Robin,
Your example now is perfect and looking really cool.
My problem however, doesn't lies so much in the initial drawing and
preventing the unwanted redraws (e.g. triggered by maximize/minimize
actions), but in updating the drawing and still be able to avoid
those.
Before I start with a long story, probably too vague and in really bad
english, I can only say that if you could tell me how to draw only the
first five of the coloured circles (below the bird, forget about the
bird anyway) in your example (on init) and the rest after clicking a
button/invoking a method, this would be perfect. I then can throw my
code away and start rebuilding completely from your example.
Here the long story starts:
I am unsure how to update the drawing (and best to be said: more in
general I find the concepts of a buffered dc with a gc where the
actual virtual drawing is done, complex to fully grasp and very
difficult to debug)
When I copy the initbuffer method and call it updateNow and then try
to change the picture (e.g. adding a circle) by calling (a method
similar to) self.Draw(gc) again: nothing happens (I am pretty sure
my code is parallel to your example here). However I get the desired
result when I call self.Refresh() afterwards To avoid
misunderstanding: off course I changed some properties (like
self.BLAH) to get a different result from self.Draw each tiem.
So now I can change actually start with 0 circles on init and add 1 at
the time, by calling self.Draw and self.refresh: but the result is
some flickering. And obviously when I want to add another new circle,
it feels stupid to regenerate all circles starting from 1 to new again
and redraw everything. For some reason beyond my comprehesion, I can
not remove the line gc = wx.GraphicsContext.Create(dc) (aka gc =
self.MakeGC(dc), even although I capture the creation of the gc in
self.gc in the original InitBuffer. I can however exclude the line:
self._buffer = wx.EmptyBitmap(sz.width, sz.height, 32) in the
updateNow function.

Thanks, Samuel

BTW: do you now the best place to share some code? I have been working
on a wxGauge for Windows (I didn't like the limitation of just a green
foreground and grey background) and the code is now (close to)
finished (single colorod or multicoloured or multigradient backgrounds
and foregrounds, horizontal en vertical gauges, moving both up/down
and left/right, from start to finish or backwards, and it is fast but
using numpy ).

As I alluded to before, it's just a matter of updating the buffer bitmap when the state of your class changes. You need to decide how to respond to the change in state, you can either just draw what is new since the last state change onto the existing bitmap (smartest), or you can clear the whole bitmap and redraw everything in the new state (easiest). When you've done that you just need to call Refresh afterward to ensure that there will be a paint event soon so your EVT_PAINT handler can flush the newly updated bitmap to the screen.

···

On 3/26/10 8:02 AM, samuel en wrote:

Thanks Robin,
Your example now is perfect and looking really cool.
My problem however, doesn't lies so much in the initial drawing and
preventing the unwanted redraws (e.g. triggered by maximize/minimize
actions), but in updating the drawing and still be able to avoid
those.
Before I start with a long story, probably too vague and in really bad
english, I can only say that if you could tell me how to draw only the
first five of the coloured circles (below the bird, forget about the
bird anyway) in your example (on init) and the rest after clicking a
button/invoking a method, this would be perfect. I then can throw my
code away and start rebuilding completely from your example.

--
Robin Dunn
Software Craftsman

As I alluded to before, it's just a matter of updating the buffer bitmap
when the state of your class changes. You need to decide how to respond
to the change in state, you can either just draw what is new since the
last state change onto the existing bitmap (smartest), or you can clear
the whole bitmap and redraw everything in the new state (easiest). When
you've done that you just need to call Refresh afterward to ensure that
there will be a paint event soon so your EVT_PAINT handler can flush the
newly updated bitmap to the screen.

Thanks, Robin, I am sorry to say there is still something wrong
(screen flickering). When I copy and rename:
def InitBuffer(self):
        sz = self.GetClientSize()
        sz.width = max(1, sz.width)
        sz.height = max(1, sz.height)
        self._buffer = wx.EmptyBitmap(sz.width, sz.height, 32)
        dc = wx.MemoryDC(self._buffer)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        gc = self.MakeGC(dc)
        self.Draw(gc)
to…
def UpdateBuffer(self):
        dc = wx.MemoryDC(self._buffer)
        gc = self.MakeGC(dc)
        self.Draw(gc)
        self.Refresh()
And call UpdateBuffer, it works like expected, but with too much
screen flickering.

Next I tried to remove the recreation of the gc, by changing in
Initbuffer:
gc=self.MakeGC(dc) to self.gc=self.MakeGC(dc). And then using self.gc
in the rest of the code and not passing gc as a argument. Result: it
works the same, but when I remove (self.)gc=self.MakeGC(dc) in
UpdateBuffer(self) line, the drawing just doesn’t update on a refresh.

Hi Robin,
Addition:
I think I have found the source of the flickering now.
It is about timing. When I changed your original code and copy your
InitBuffer to an UpdateBuffer and redraw everything with a refresh,
there is no flickering. However when I speed up the code by drawing
less items and moving some code to init only, flickerings starts. The
more fast the code becomes, the more flickering becomes noticeable
(that is on a slow notebook). Then when I put a small delay in the
code: the flickering is gone.

By default Refresh will erase the window before sending the paint event. You may want to try calling Refresh(False) instead.

···

On 3/27/10 3:23 AM, samuel en wrote:

Hi Robin,
Addition:
I think I have found the source of the flickering now.
It is about timing. When I changed your original code and copy your
InitBuffer to an UpdateBuffer and redraw everything with a refresh,
there is no flickering. However when I speed up the code by drawing
less items and moving some code to init only, flickerings starts. The
more fast the code becomes, the more flickering becomes noticeable
(that is on a slow notebook). Then when I put a small delay in the
code: the flickering is gone.

--
Robin Dunn
Software Craftsman

By default Refresh will erase the window before sending the paint event.
You may want to try calling Refresh(False) instead.
Robin Dunn
Software Craftsmanhttp://wxPython.org

YES, this works perfect. Thanks a lot.
(I am banging my head against the wall right now, having spend many
hours changing the code, whilst is was OK from the start... to
conclude having missed only a simple boolean parameter, worse even: I
tried this option with refreshRect, but then it didn't gave the
required result, so I forgot about it).