无闪烁可绘制的ScrolledWindow

时间:2010-06-30 08:35:42

标签: python wxpython flicker scrolledwindow

我正在尝试构建一个可以使用鼠标绘制的ScrolledWindow,它也可以正常工作,但是当用户在窗口上绘图而滚动条不在“滚动条”中时,我会得到一个令人讨厌的闪烁家“位置..

要重现,请运行附加的程序,向下滚动(或向右)并按住鼠标左键“涂抹”一下。你现在应该看到闪烁......

import wx

class MainFrame(wx.Frame):
    """ Just a frame with a DrawPane """

    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)
        s = wx.BoxSizer(wx.VERTICAL)
        s.Add(DrawPane(self), 1, wx.EXPAND)
        self.SetSizer(s)

########################################################################
class DrawPane(wx.PyScrolledWindow):
    """ A PyScrolledWindow with a 1000x1000 drawable area """

    VSIZE = (1000, 1000)

    def __init__(self, *args, **kw):
        wx.PyScrolledWindow.__init__(self, *args, **kw)
        self.SetScrollbars(10, 10, 100, 100)
        self.prepare_buffer()

        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
        self.Bind(wx.EVT_MOTION, self.on_motion)

    def prepare_buffer(self):
        self.buffer = wx.EmptyBitmap(*DrawPane.VSIZE)
        dc = wx.BufferedDC(None, self.buffer)
        dc.Clear()
        dc.DrawLine(0, 0, 999, 999) # Draw something to better show the flicker problem

    def on_paint(self, evt):
        dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)

    def on_mouse_down(self, evt):
        self.mouse_pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()

    def on_motion(self, evt):
        if evt.Dragging() and evt.LeftIsDown():
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            newpos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
            coords = self.mouse_pos + newpos
            dc.DrawLine(*coords)
            self.mouse_pos = newpos
            self.Refresh()

if __name__ == "__main__":
    app = wx.PySimpleApp()
    wx.InitAllImageHandlers()
    MainFrame(None).Show()
    app.MainLoop()

我尝试使用SetBackgroundStyle(wx.BG_STYLE_CUSTOM),或绑定EVT_ERASE_BACKGROUND,或使用RefreshRect代替Refresh,但闪烁仍在那里..对我可能尝试的内容有任何想法下?

我的环境:Xubuntu 9.04,wxPython 2.8.9.1 (但也在Ubuntu 10.04上测试过)

非常感谢你的时间!

2 个答案:

答案 0 :(得分:5)

来自Robin Dunn本人:

  

首先,默认情况下会Refresh()   发送前擦除背景   油漆事件(虽然设置了   BG风格或捕捉擦除事件   本来会照顾的。)   第二,也许最明显   在这种情况下的问题是在你的   你不是on_motion处理程序   通过滚动抵消ClientDC   抵消,只是在的位置   你正在画线的缓冲区   细分市场。所以当缓冲区是   冲到客户端DC就是这样   在物理(0,0)绘制,而不是   虚拟(0,0)。换句话说,   你看到的闪烁来自   在错误的情况下绘制缓冲区   每次鼠标拖动事件后的位置,   然后它立即被绘制   再次在正确的位置   on_paint由...引发   Refresh()

     

你应该能够解决这个问题   在客户端DC上调用PrepareDC   在使用之前,像这样:

    cdc = wx.CLientDC(self)
    self.PrepareDC(cdc)
    dc = wx.BufferedDC(cdc, self.buffer)
     

然而,因为你正在做一个   无论如何RefreshRefreshRect,   没有必要使用客户端DC   在这里,只是让冲洗   屏幕缓冲区完成   改为on_paint:

    dc = wx.BufferedDC(None, self.buffer)

答案 1 :(得分:1)

使用Joril建议并删除Refresh(),不再有闪烁(甚至放大帧)。

import wx


class MainFrame(wx.Frame):
    """ Just a frame with a DrawPane """

    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)
        s = wx.BoxSizer(wx.VERTICAL)
        s.Add(DrawPane(self), 1, wx.EXPAND)
        self.SetSizer(s)

########################################################################
class DrawPane(wx.PyScrolledWindow):
    """ A PyScrolledWindow with a 1000x1000 drawable area """

    VSIZE = (1000, 1000)

    def __init__(self, *args, **kw):
        wx.PyScrolledWindow.__init__(self, *args, **kw)
        self.SetScrollbars(10, 10, 100, 100)
        self.prepare_buffer()
        cdc = wx.ClientDC(self)
        self.PrepareDC(cdc)
        dc = wx.BufferedDC(cdc, self.buffer)

        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
        self.Bind(wx.EVT_MOTION, self.on_motion)

    def prepare_buffer(self):
        self.buffer = wx.EmptyBitmap(*DrawPane.VSIZE)
        cdc = wx.ClientDC(self)
        self.PrepareDC(cdc)
        dc = wx.BufferedDC(cdc, self.buffer)
        dc.Clear()
        dc.DrawLine(0, 0, 999, 999) # Draw something to better show the flicker problem


    def on_paint(self, evt):
        dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)

    def on_mouse_down(self, evt):
        self.mouse_pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()

    def on_motion(self, evt):
        if evt.Dragging() and evt.LeftIsDown():
            newpos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
            coords = self.mouse_pos + newpos
            cdc = wx.ClientDC(self)
            self.PrepareDC(cdc)
            dc = wx.BufferedDC(cdc, self.buffer)
            dc.DrawLine(*coords)
            self.mouse_pos = newpos

if __name__ == "__main__":
    app = wx.PySimpleApp()
    wx.InitAllImageHandlers()
    MainFrame(None).Show()
    app.MainLoop()