双缓冲类里的OnPaint与OnSize,以及构造函数的关系

代码摘自wxlibagwknobctrl.py
一点体会是,OnSize作为class的函数,被放在构造函数里执行,会先于OnPaint执行。
测试结果是,初始启动后,会执行8次OnSize(为什么是8次?2个闹钟就2次,每个闹钟分别1次SetTags,其中一个调用一次self.OnTicks,总共应该是5次),然后才会执行2次OnPaint。
而且这两次OnPaint可能还是附送的,估计是执行的时候正好console盖住gui,gui恢复显示的时候会执行两次OnPaint
用任意窗口遮住OnPaint,然后最小化,就会发现执行两次OnPaint(为什么是两次?一次就够了啊)
每次OnPaint的时候,会自动调用OnSize为之准备好的数据:self._Buffer

#----------------------------------------------------------------------
# BUFFERENDWINDOW Class
# This Class Has Been Taken From The wxPython Wiki, And Slightly
# Adapted To Fill My Needs. See:
#
# http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing
#
# For More Info About DC And Double Buffered Drawing.
#----------------------------------------------------------------------

class BufferedWindow(wx.Window):
    """
    A Buffered window class.

    To use it, subclass it and define a `Draw(dc)` method that takes a `dc`
    to draw to. In that method, put the code needed to draw the picture
    you want. The window will automatically be double buffered, and the
    screen will be automatically updated when a Paint event is received.

    When the drawing needs to change, you app needs to call the
    L{BufferedWindow.UpdateDrawing} method. Since the drawing is stored in a bitmap, you
    can also save the drawing to file by calling the
    `SaveToFile(self, file_name, file_type)` method.
    """

    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.NO_FULL_REPAINT_ON_RESIZE, agwStyle=KC_BUFFERED_DC):
        """
        Default class constructor.
        :param `style`: the window style;
        :param `agwStyle`: if set to ``KC_BUFFERED_DC``, double-buffering will
         be used.
        """

        wx.Window.__init__(self, parent, id, pos, size, style)

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)

        # OnSize called to make sure the buffer is initialized.
        # This might result in OnSize getting called twice on some
        # platforms at initialization, but little harm done.
        self.OnSize(None)


    def Draw(self, dc):
        """
        This method should be overridden when sub-classed.
        :param `dc`: an instance of `wx.DC`.
        """
        pass


    def OnPaint(self, event): # 需要使用的时候,才会被系统自动调用,否则也不会执行到这里来
        """
        Handles the ``wx.EVT_PAINT`` event for L{BufferedWindow}.
        :param `event`: a `wx.PaintEvent` event to be processed.
        """
        print "OnPaint"
        # All that is needed here is to draw the buffer to screen
        if self._agwStyle == KC_BUFFERED_DC: # 如果是双缓冲
            dc = wx.BufferedPaintDC(self, self._Buffer) # 从已经准备好的双缓冲中直接得到DC指针并显示
        else: # 如果不是双缓冲
            dc = wx.PaintDC(self) # 那就直接申请新的PaintDC,无法从双缓冲处直接获得DC
            dc.DrawBitmap(self._Buffer,0,0) # 也可以用新的DC读取相关数据,现场读数据也许慢,且很多数据的话会闪烁?


    def OnSize(self,event):
        """
        Handles the ``wx.EVT_SIZE`` event for L{BufferedWindow}.
        :param `event`: a `wx.SizeEvent` event to be processed.
        """

        # The Buffer init is done here, to make sure the buffer is always
        # the same size as the Window
        self.Width, self.Height = self.GetClientSizeTuple()
        print "OnSize"

        # Make new off screen bitmap: this bitmap will always have the
        # current drawing in it, so it can be used to save the image to
        # a file, or whatever.

        # This seems required on MacOS, it doesn't like wx.EmptyBitmap with
        # size = (0, 0)
        # Thanks to Gerard Grazzini

        if "__WXMAC__" in wx.Platform:
            if self.Width == 0:
                self.Width = 1
            if self.Height == 0:
                self.Height = 1

        self._Buffer = wx.EmptyBitmap(self.Width, self.Height) # 准备空图片(以后会被子类的数据所填充)

        memory = wx.MemoryDC() # 在内存DC里建立图像。经测试,发现这个memory局部变量是用来覆盖闹钟的背景色的,因为与Form的颜色一致,所以闹钟的效果被单独突出显示来了。
        memory.SelectObject(self._Buffer) # DC连接要显示的图像
        memory.SetBackground(wx.Brush(self.GetBackgroundColour())) # 设置图像背景,并使用刷子刷干净
        memory.SetPen(wx.TRANSPARENT_PEN) # 选中透明画笔
        memory.Clear() # 清空背景图

        minradius = min(0.9*self.Width/2, 0.9*self.Height/2)
        memory.DrawCircle(self.Width/2, self.Height/2, minradius) # (使用透明铅笔)画圆
        memory.SelectObject(wx.NullBitmap) # 选中空图像,其实这句话我不明白,经过测试,去掉这句话不影响,是用来丢弃数据或者回收垃圾的。且这里的Memory似乎也不会被传递出去。
        self._region = wx.RegionFromBitmapColour(self._Buffer, self.GetBackgroundColour()) # 使用背景色模板,除去背景色以后,得到一张透明图
        self._minradius = minradius

        self.UpdateDrawing()


    def UpdateDrawing(self):
        """
        This would get called if the drawing needed to change, for whatever reason.
        The idea here is that the drawing is based on some data generated
        elsewhere in the system. If that data changes, the drawing needs to
        be updated.
        """

        if self._agwStyle == KC_BUFFERED_DC:
            dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer) # 每次都建立新的双缓冲DC,以后好直接在OnPaint里改变DC指针即可
            self.Draw(dc) # 调用子类的Draw,这时候可以填充相关数据
        else:
            # update the buffer
            dc = wx.MemoryDC() # 申请内存DC,虽然也是单独准备DC,但毕竟不是系统提供的双缓冲
            dc.SelectObject(self._Buffer) # 似乎这里的DC选中数据也没有用,这个DC不会被传递到OnPaint里去悠悠什么用?而且这里不是self.dc

            self.Draw(dc)
            # update the screen
            wx.ClientDC(self).Blit(0, 0, self.Width, self.Height, dc, 0, 0)
原文地址:https://www.cnblogs.com/findumars/p/3620090.html