使用 wxpython 从数据流中实时绘制轨迹



我想使用 wxpython 实时显示随机游走的轨迹。但是,该面板仅在最后更新一次,显示整个随机漫游,而不是逐步更新并显示时间过程。

我的第一个想法是使用 wx。客户端DC((。DrawPoint(( 但结果如上所述,我没有看到绘制单个点,而只显示了最终结果。

所以我转而考虑使用 wx。MemoryDC 将轨迹绘制到存储在内存中的位图,然后使用 wx。ClientDC.DrawBitmap(( 以设定的时间间隔将缓冲的图像复制到屏幕上,以防翻转图像是瓶颈。结果仍然是一样的,所以我希望你能帮忙。

本练习的目的是用来自帧速率为1000 Hz的眼动仪的位置数据代替随机游走,我希望能够尽可能接近实时地可视化轨迹(监视器的帧速率为120Hz(。

这是我的代码(大部分来自这里(:

import wx
import random
import time
from time import asctime
#-------------------------------------------------------------------
def jmtime():
return '[' + asctime()[11:19] + '] '
#-------------------------------------------------------------------
class MyDrawingArea(wx.Window):
def __init__(self, parent, id):
sty = wx.NO_BORDER
wx.Window.__init__(self, parent, id, style=sty)
self.parent = parent
self.SetBackgroundColour(wx.WHITE)
self.SetCursor(wx.CROSS_CURSOR)
# Some initalisation, just to reminds the user that a variable
# called self.BufferBmp exists. See self.OnSize().
self.BufferBmp = None
wx.EVT_SIZE(self, self.OnSize)
wx.EVT_PAINT(self, self.OnPaint)
wx.EVT_LEFT_DOWN(self,self.OnClick)
def OnSize(self, event):
print jmtime() + 'OnSize in MyDrawingArea'
# Get the size of the drawing area in pixels.
self.wi, self.he = self.GetSizeTuple()
# Create BufferBmp and set the same size as the drawing area.
self.BufferBmp = wx.EmptyBitmap(self.wi, self.he)
memdc = wx.MemoryDC()
memdc.SelectObject(self.BufferBmp)
# Drawing job
ret = self.DoSomeDrawing(memdc)
if not ret:  #error
self.BufferBmp = None
wx.MessageBox('Error in drawing', 'CommentedDrawing', wx.OK | wx.ICON_EXCLAMATION)

def OnPaint(self, event):
print jmtime() + 'OnPaint in MyDrawingArea'
dc = wx.PaintDC(self)
dc.BeginDrawing()
if self.BufferBmp != None:
print jmtime() + '...drawing'
dc.DrawBitmap(self.BufferBmp, 0, 0, True)
else:
print jmtime() + '...nothing to draw'
dc.EndDrawing()

def OnClick(self,event):
pos = event.GetPosition()
dc = wx.ClientDC(self)
dc.SetPen(wx.Pen(wx.BLACK,1,wx.SOLID))
dcwi, dche = dc.GetSizeTuple()
x = pos.x
y = pos.y
time_start = time.time()
memdc = wx.MemoryDC()
memdc.SelectObject(self.BufferBmp)
memdc.SetPen(wx.Pen(wx.BLACK,1,wx.SOLID))
count = 1
runtime = 5
while (time.time() - time_start) < runtime:
x,y = random_walk(x,y,dcwi,dche)
memdc.DrawPoint(x,y)
if (time.time() - time_start) > count * runtime * 0.1:
print jmtime() + 'Random walk in MyDrawingArea'
count += 1
dc.BeginDrawing()
dc.DrawBitmap(self.BufferBmp, 0, 0, True)
dc.EndDrawing()
dc.BeginDrawing()        
dc.DrawBitmap(self.BufferBmp, 0, 0, True)
dc.EndDrawing()
# End of def OnClick
def DoSomeDrawing(self, dc):
try:
print jmtime() + 'DoSomeDrawing in MyDrawingArea'
dc.BeginDrawing()
#~ raise OverflowError #for test
# Clear everything
dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
dc.Clear()
dc.EndDrawing()
return True
except:
return False
#-------------------------------------------------------------------
class MyPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize)
self.drawingarea = MyDrawingArea(self, -1)
self.SetAutoLayout(True)
gap = 30 #in pixels
lc = wx.LayoutConstraints()
lc.top.SameAs(self, wx.Top, gap)
lc.left.SameAs(self, wx.Left, gap)
lc.right.SameAs(self, wx.Width, gap)
lc.bottom.SameAs(self, wx.Bottom, gap)
self.drawingarea.SetConstraints(lc)
#-------------------------------------------------------------------
# Usual frame. Can be resized, maximized and minimized.
# The frame contains one panel.
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'CommentedDrawing', wx.Point(0, 0), wx.Size(500, 400))
self.panel = MyPanel(self, -1)
wx.EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
print jmtime() + 'OnCloseWindow in MyFrame'
self.Destroy()
#-------------------------------------------------------------------
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1)
frame.Show(True)
self.SetTopWindow(frame)
return True
#-------------------------------------------------------------------
def random_walk(x,y,sizex = 250, sizey = 200):
rn = random.randrange(0,2)
x_new = x + (1-rn) - rn
while x_new < 0 or x_new > sizex:
rn = random.randrange(0,2)
x_new = x + (1-rn) - rn
rn = random.randrange(0,2)
y_new = y + (1-rn) - rn
while y_new < 0 or y_new > sizex:
rn = random.randrange(0,2)
y_new = y + (1-rn) - rn
return x_new, y_new
# end of def random_walk
#-------------------------------------------------------------------
def main():
print 'main is running...'
app = MyApp(0)
app.MainLoop()
#-------------------------------------------------------------------
if __name__ == "__main__" :
main()
#eof-------------------------------------------------------------------

这就是我想出的解决方案。我没有使用 dc.DrawBitmap(( 将缓冲的图像复制到屏幕上,而是使用 Update(( 和 Refresh(( 来触发绘制事件。但是,我仍然不明白为什么我不能使用 DrawBitmap(( 来完成相同的任务。

唯一的区别是 OnPaint(( 使用 PaintDC((,而在 OnClick(( 中我使用 ClientDC((。

无论如何,这是我当前的 OnClick(( 代码:

def OnClick(self,event):
pos = event.GetPosition()
x = pos.x
y = pos.y
time_start = time.time()
memdc = wx.MemoryDC()
memdc.SelectObject(self.BufferBmp)
dcwi, dche = memdc.GetSizeTuple()
memdc.SetPen(wx.Pen(wx.BLACK,1,wx.SOLID))
runtime = 10
while (time.time() - time_start) < runtime:
x,y = random_walk(x,y,dcwi,dche)
memdc.SelectObject(self.BufferBmp)
memdc.DrawPoint(x,y)
memdc.SelectObject(wx.NullBitmap)
self.Update()
self.Refresh()
print jmtime() + 'Random walk in MyDrawingArea done'

最新更新