3

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

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

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()

我尝试了 using SetBackgroundStyle(wx.BG_STYLE_CUSTOM),或 binding EVT_ERASE_BACKGROUND,或 using RefreshRectinstead of Refresh,但闪烁仍然存在。关于下一步我可能尝试什么的任何想法?

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

非常感谢您的宝贵时间!

4

2 回答 2

5

来自罗宾邓恩本人:

首先,aRefresh()默认情况下会在发送绘制事件之前擦除背景(尽管设置 BG 样式或捕获擦除事件会解决这个问题。)在这种情况下,第二个可能也是最明显的问题是,在您的 on_motion处理程序中,您是没有通过滚动偏移来偏移 ClientDC,只是在缓冲区中绘制线段的位置。因此,当缓冲区被刷新到客户端 DC 时,它会在物理 (0,0) 处绘制,而不是在虚拟 (0,0) 处绘制。换句话说,您看到的闪烁来自在每次鼠标拖动事件后在错误的位置绘制缓冲区,然后 on_paintRefresh().

您应该能够通过PrepareDC在使用之前调用客户端 DC 来解决此问题,如下所示:

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

但是,由于您正在执行 a RefreshRefreshRect无论如何,这里根本不需要使用客户端 DC,只需在 on_paint 中将缓冲区刷新到屏幕即可:

    dc = wx.BufferedDC(None, self.buffer)
于 2010-07-01T07:17:01.277 回答
2

使用 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()
于 2010-07-02T01:10:36.857 回答