我想用Python和wxPython显示一条带有一条或两条曲线的实时图表,每秒最多50个样本.该小部件应该支持Win32和Linux平台.
任何提示都是受欢迎的.
编辑添加:
我不需要以50 fps更新显示,但需要在两条曲线上显示多达50个数据样本,并且显示器的更新速率合理(5..10 fps应该没问题).
编辑添加:
我在一个项目中使用了mathplotlib取得了很好的成功.然后我为其他项目选择了wx.lib.plot,我发现它更简单,但更容易使用并且消耗更少的CPU周期.由于wx.lib是标准wxPython发行版的一部分,因此特别容易使用.
如果您希望以最小的代码占用空间获得高性能,那么看起来不比Python的内置绘图库tkinter更远.无需编写特殊的C/C++代码或使用大型绘图包来获得比50 fps更好的性能.
以下代码在2.2 GHz Core 2 duo上以400 fps滚动1000x200带状图,在3.4 GHz Core i3上以1000 fps滚动.中心例程"scrollstrip"在右边缘绘制一组数据点和相应的颜色以及可选的垂直网格条,然后将条形图向左滚动1.要绘制水平网格条,只需将它们包含在数据和颜色中数组作为常量以及可变数据点.
from tkinter import * import math, random, threading, time class StripChart: def __init__(self, root): self.gf = self.makeGraph(root) self.cf = self.makeControls(root) self.gf.pack() self.cf.pack() self.Reset() def makeGraph(self, frame): self.sw = 1000 self.h = 200 self.top = 2 gf = Canvas(frame, width=self.sw, height=self.h+10, bg="#002", bd=0, highlightthickness=0) gf.p = PhotoImage(width=2*self.sw, height=self.h) self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW) return(gf) def makeControls(self, frame): cf = Frame(frame, borderraised") Button(cf, text="Run", command=self.Run).grid(column=2, row=2) Button(cf, text="Stop", command=self.Stop).grid(column=4, row=2) Button(cf, text="Reset", command=self.Reset).grid(column=6, row=2) self.fps = Label(cf, text="0 fps") self.fps.grid(column=2, row=4, columnspan=5) return(cf) def Run(self): self.go = 1 for t in threading.enumerate(): if t.name == "_gen_": print("already running") return threading.Thread(target=self.do_start, name="_gen_").start() def Stop(self): self.go = 0 for t in threading.enumerate(): if t.name == "_gen_": t.join() def Reset(self): self.Stop() self.clearstrip(self.gf.p, '#345') def do_start(self): t = 0 y2 = 0 tx = time.time() while self.go: y1 = 0.2*math.sin(0.02*math.pi*t) y2 = 0.9*y2 + 0.1*(random.random()-0.5) self.scrollstrip(self.gf.p, (0.25+y1, 0.25, 0.7+y2, 0.6, 0.7, 0.8), ( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'), "" if t % 65 else "#088") t += 1 if not t % 100: tx2 = time.time() self.fps.config(text='%d fps' % int(100/(tx2 - tx))) tx = tx2 # time.sleep(0.001) def clearstrip(self, p, color): # Fill strip with background color self.bg = color # save background color for scroll self.data = None # clear previous data self.x = 0 p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height']) def scrollstrip(self, p, data, colors, bar=""): # Scroll the strip, add new data self.x = (self.x + 1) % self.sw # x = double buffer position bg = bar if bar else self.bg p.tk.call(p, 'put', bg, '-to', self.x, 0, self.x+1, self.h) p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0, self.x+self.sw+1, self.h) self.gf.coords(self.item, -1-self.x, self.top) # scroll to just-written column if not self.data: self.data = data for d in range(len(data)): y0 = int((self.h-1) * (1.0-self.data[d])) # plot all the data points y1 = int((self.h-1) * (1.0-data[d])) ya, yb = sorted((y0, y1)) for y in range(ya, yb+1): # connect the dots p.put(colors[d], (self.x,y)) p.put(colors[d], (self.x+self.sw,y)) self.data = data # save for next call def main(): root = Tk() root.title("StripChart") app = StripChart(root) root.mainloop() main()
创建一个可以从您的数据源读取的C++小部件并且以50 FPS真正更新并不困难.这种方法的美妙之处在于,很少(如果有的话)Python代码将以50FPS执行,它将全部在C++中,具体取决于您将更新的数据传递给小部件的方式.
您甚至可以将事件处理程序从Python端推送到自定义实时数据查看器中,以处理所有鼠标事件和用户交互,并将渲染保留在C++中.
它将是一个小型的C++类,它扩展了wxWidget的wxWindow类
class RealtimeDataViewer:public wxWindow {...
并覆盖OnPaint
void OnPaint(wxPaintEvent&WXUNUSED(event)){....
然后它将得到一个设备上下文,并开始绘制线条和形状......
然后,您必须获取.h文件,并将其复制到.i,并稍微调整一下,使其成为SWIG可用于扩展wxPython的定义.
构建过程可以由Python自己的distutils使用以下参数来设置:
ext_modules=[Extension('myextension', sources, include_dirs=includeDirs library_dirs=usual_libs, )],
让它看起来很棒并且运作良好将是一段时间的工作......但它可能是一个真正加速你的项目未来的选择.
所有这些在Mac,Windows和Linux上运行良好.
wxPython实际上是一个隐藏的Gem,可以通过更专业支持的IDE /设计器工具来接管世界.
也就是说,首先尝试matplotlib,它有很多漂亮的优化渲染,并且可以实时进行更新.