Drawing flicker, and Windows issues

Hi. This is an issue on both GTK and Windows - the following code will create a new bitmap of th x/y positions of the mouse cursor. Drag the grey area of the window to resize the bitmap, and there is noticeable tearing/flickering.

#!/usr/bin/python

import os
import wx

CANVAS_BORDER = 15 # pixels in size
RIGHT = 1
DIAGONAL = 2
BOTTOM = 3

class GUI(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, size=(1024, 786))
        tabs = wx.Notebook(self)
        tabs.AddPage(DrawingPanel(tabs), "Tab 1")

class DrawingPanel(wx.ScrolledWindow):
    def __init__(self, tab): wx.ScrolledWindow.__init__(self, tab, style=wx.NO_FULL_REPAINT_ON_RESIZE | wx.CLIP_CHILDREN)
        self.canvas_size = (1000, 1000)
        self.area = (self.canvas_size[0] - 200, self.canvas_size[1] - 200)
        self.SetVirtualSizeHints(2, 2)
        self.SetVirtualSize(self.canvas_size)
        self.SetScrollRate(1, 1)
        self.SetBackgroundColour('Grey')
               self.buffer = wx.EmptyBitmap(*self.area)
        self.resizing = False
        self.resize_direction = False

        self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase)
        self.Bind(wx.EVT_LEFT_DOWN, self.left_down)
        self.Bind(wx.EVT_LEFT_UP, self.left_up)
        self.Bind(wx.EVT_MOTION, self.left_motion)
        self.Bind(wx.EVT_PAINT, self.on_paint)
       
    def convert_coords(self, event):
        return self.CalcUnscrolledPosition(event.GetX(), event.GetY())
       
                   def left_down(self, event):
        x, y = self.convert_coords(event)
        if os.name == "nt":
            self.CaptureMouse()
        if self.check_canvas_resize(x, y): # start resizing
            self.resizing = True

    def left_motion(self, event):
        x, y = self.convert_coords(event)

        if self.resizing:
            self.resize_canvas((x, y), self.resize_direction)
        else:
            direction = self.check_canvas_resize(x, y)

            if direction:
                if not self.resize_direction:
                    self.resize_direction = direction
                if direction != self.resize_direction:
                    self.resize_direction = direction

    def left_up(self, event):
        if os.name == "nt":
            self.ReleaseMouse()
        if self.resizing:
            self.resizing = False

               def on_paint(self, event=None):
        if os.name == "nt" and self.resizing:
            dc = wx.ClientDC(self)
            dc.SetBrush(wx.GREY_BRUSH)
            dc.Clear()
        wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
                   def on_erase(self, event=None):
        event.Skip()
       
    def check_canvas_resize(self, x, y):
        if x > self.area[0] and y > self.area[1]:
            return DIAGONAL
        elif x > self.area[0]:
            return RIGHT
        elif y > self.area[1]:
            return BOTTOM
        return False

    def resize_canvas(self, size, direction=None):
        """ Resizes the canvas. Size = (w, h) tuple """
        if size[0] < 1 or size[1] < 1:
            return

        if direction == RIGHT:
            size = (size[0] + CANVAS_BORDER, self.area[1])
            self.Scroll(size[0], -1)
        elif direction == BOTTOM:
            size = (self.area[0], size[1])
            self.Scroll(-1, size[1] + CANVAS_BORDER)
        else:
            self.Scroll(*size)

        self.buffer = wx.EmptyBitmap(*size)
        self.area = size

        size = (size[0] + CANVAS_BORDER, size[1] + CANVAS_BORDER)
        self.SetVirtualSize(size)
        self.Refresh() # I need to force a paint in this sample
                                   
           # -----------------------------------------------------------

class TestApp(wx.App):
    def OnInit(self):
        frame = GUI(None)
        frame.Show(True)
        return True

if __name__ == '__main__':
    app = TestApp(redirect=True)
    app.MainLoop()

I also have an issue with Windows. Previously, I was using self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) to prevent flickering. However, if I use that style, the background colour of grey is not applied.

So, if I keep the call to self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM), I can then draw the grey background myself in EVT_PAINT. Doing this works fine, until the window is minimised/another window dragged on top of it - then the background area isn't repainted. I've tried binding EVT_ERASE_BACKGROUND, and calling Skip(), and just NOP'ing the event, but neither work here.

Another approach I tried to combat flickering, was to remove the call to SetBackgroundStyle and Bind to EVT_ERASE_BACKGROUND. If I call event.skip() then, the window gets repainted properly when re-exposed. However, drawing flicker is brought in again. If I use pass instead of event.Skip, then the flicker is fixed, but the repainting issue comes back again!

I can't think of the correct mixture of these solutions!

resizing_canvas.py (3.67 KB)

···

--
Steven Sproat, BSc
http://www.basicrpg.com/