Can't get wx.BufferedDC to draw anything
I've got a problem with DCs. I'm trying to make an application that will draw many lines on the screens and needs to update really fast, and since I don't want flickering, I decided to give buffered dcs a shot. But when I run this code, it doesn't draw anything. What am I doing wrong?
import wx
class MainFrame(wx.Frame):
def __init__(self):
screensize = wx.GetDisplaySize()
self.framesize = (开发者_StackOverflowscreensize[0]/4*3, screensize[1]/4*3)
wx.Frame.__init__(self, None, -1, "CursorTracker", size=self.framesize,
style=wx.SYSTEM_MENU|
wx.CAPTION|
wx.CLOSE_BOX|
wx.MINIMIZE_BOX)
self.dc = wx.ClientDC(self)
self.bdc = wx.BufferedDC(self.dc)
self.SetBackgroundColour(wx.WHITE)
self.Timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.Timer.Start(100)
def OnTimer(self, event):
self.bdc.DrawLine(1,1,100,100)
class App(wx.App):
def OnInit(self):
frame = MainFrame()
frame.Show()
return True
app = App(redirect=False)
app.MainLoop()
I've used AutoBufferedPaintDC, but I've found doing my own double-buffering with a MemoryDC to be more flexible. Here's a template for you.
import wx
class Frame(wx.Frame):
def __init__(self):
super(Frame, self).__init__(None, -1, 'CursorTracker')
self.mdc = None # memory dc to draw off-screen
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase)
self.Bind(wx.EVT_PAINT, self.on_paint)
w, h = wx.GetDisplaySize()
w, h = w * 3 / 4, h * 3 / 4
self.SetSize((w, h))
self.Center()
self.on_timer()
def on_size(self, event):
# re-create memory dc to fill window
w, h = self.GetClientSize()
self.mdc = wx.MemoryDC(wx.EmptyBitmap(w, h))
self.redraw()
def on_erase(self, event):
pass # don't do any erasing to avoid flicker
def on_paint(self, event):
# just blit the memory dc
dc = wx.PaintDC(self)
if not self.mdc:
return
w, h = self.mdc.GetSize()
dc.Blit(0, 0, w, h, self.mdc, 0, 0)
def on_timer(self):
# refresh every N milliseconds
self.redraw()
wx.CallLater(100, self.on_timer)
def redraw(self):
# do the actual drawing on the memory dc here
dc = self.mdc
w, h = dc.GetSize()
dc.Clear()
dc.DrawLine(0, 0, w, h)
self.Refresh()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = Frame()
frame.Show()
app.MainLoop()
The basic approach is:
- create a memory dc for off-screen drawing
- if the window is resized, resize the memory dc and redraw
- when a paint event occurs, just blit the memory dc onto the paint dc
- do nothing on a erase background event to avoid flicker
- call redraw when and only when you actually need to change what's on the screen
If you store a reference to that EmptyBitmap that's created in on_size, you can even save the window contents to an image file with wxBitmap.SaveFile()
A BufferedDC is copied to the screen only when the object is destroy. Since you never destroy self.bdc
, of course its contents will never be copied to the screen. So, you need to arrange to let go of self.bdc
(maybe just replace it with a new BufferedDc instance: i.e., redo the assignment self.bdc = wc.BufferedDc(self.dc)
at "strategic" moments in time when you do want the drawing to be visualized.
精彩评论