ScrolledWindow без мерцания - PullRequest
       35

ScrolledWindow без мерцания

3 голосов
/ 30 июня 2010

Я пытаюсь создать окно 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 ]

5 голосов
/ 01 июля 2010

От самого Робина Данна:

Во-первых, Refresh() по умолчанию сотрет фон перед отправкой события рисования (хотя установка стиля BG или перехват события стирания позаботились бы оэто.) Вторая и, вероятно, наиболее заметная проблема в этом случае заключается в том, что в вашем обработчике on_motion вы не смещаете ClientDC смещениями прокрутки, а просто позицией в буфере, в котором вы рисуете отрезок линии.Поэтому, когда буфер сбрасывается на клиентский DC, он обращается к физическому (0,0), а не к виртуальному (0,0).Другими словами, мерцание, которое вы видите, происходит от прорисовки буфера в неправильной позиции после каждого события перетаскивания мышью, а затем он сразу же снова рисуется в правильной позиции в on_paint, вызванном Refresh().

Вы должны иметь возможность исправить это, вызвав PrepareDC на клиентском контроллере домена перед его использованием, например:

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

Однако, поскольку вы все равно делаете Refresh или RefreshRectздесь вообще нет необходимости использовать клиентский DC, просто вместо этого сделайте сброс буфера на экран в on_paint:

    dc = wx.BufferedDC(None, self.buffer)
1 голос
/ 02 июля 2010

Используя рекомендации 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()
...