2010-06-30 2 views
3

Я пытаюсь создать ScrolledWindow, который вы можете использовать с помощью мыши, и он тоже работает, но я получаю неприятное мерцание, когда пользователь рисует окно, в то время как полосы прокрутки aren 't в «домашнем» положении.Flicker-free drawable 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 тоже)

Большое спасибо за ваше время!

ответ

5

С самого Робина Данна:

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

Вы должны быть в состоянии исправить это вызывающему PrepareDC на клиенте DC , прежде чем использовать его, как это:

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

Использование рекомендаций 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() 
Смежные вопросы