16

Python と wxPython を使用して、1 つまたは 2 つの曲線と 1 秒あたり最大 50 サンプルのリアルタイム グラフを表示したいと思います。ウィジェットは、Win32 と Linux の両方のプラットフォームをサポートする必要があります。

どんなヒントでも大歓迎です。

追加するために編集:

ディスプレイを 50 fps で更新する必要はありませんが、両方の曲線に最大 50 サンプルのデータを表示する必要があり、ディスプレイの適切な更新レート (5..10 fps で問題ありません) が必要です。

追加するために編集:

プロジェクトで mathplotlib を使用して成功しました。その後、他のプロジェクト用に wx.lib.plot に落ち着きました。これは、よりシンプルであることがわかりましたが、やや使いやすく、CPU サイクルの消費が少ないことがわかりました。wx.lib は標準の wxPython ディストリビューションの一部として提供されるため、特に使いやすいです。

4

5 に答える 5

16

最小限のコード フットプリントで高いパフォーマンスが必要な場合は、Python の組み込みプロット ライブラリ tkinter 以外に探す必要はありません。50 fps よりもはるかに優れたパフォーマンスを得るために、特別な C / C++ コードを記述したり、大規模なプロット パッケージを使用したりする必要はありません。

スクリーンショット

次のコードは、2.2 GHz Core 2 duo では 400 fps、3.4 GHz Core i3 では 1000 fps で 1000x200 ストリップ チャートをスクロールします。中央ルーチン「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, borderwidth=1, relief="raised")
        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()
于 2011-09-30T01:24:00.377 に答える
6

データ ソースから読み取り、実際に 50 FPS で更新する C++ ウィジェットを作成することは難しくありません。このアプローチの優れた点は、更新されたデータをウィジェットに渡す方法に応じて、50 FPS で実行される Python コードが (あるとしても) ごくわずかであることです。

Python 側からカスタム リアルタイム データ ビューアーにイベント ハンドラーをプッシュして、すべてのマウス イベントとユーザー インタラクションを処理し、レンダリングだけを C++ に残すこともできます。

wxWidget の wxWindow クラスを拡張する小さな C++ クラスになります。

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,
                          )],

見栄えが良く、うまく機能するようになるまでには数日かかります...しかし、おそらくそれは、プロジェクトを将来に向けて本当に加速させる1つのオプションです.

これらはすべて、Mac、Windows、および Linux でうまく機能します。

wxPython は、より専門的にサポートされている IDE / デザイナー ツールで世界を席巻する隠れた宝石です。

とはいえ、最初に matplotlib を試してみてください。最適化された美しいレンダリングがたくさんあり、リアルタイムで更新することもできます。

于 2009-01-19T14:40:50.027 に答える
2

毎秒 50 フレームで本当に高速なものが必要な場合は、PyGame のようなものが必要であり、プロット モジュールではなく、ディスプレイに直接話しかける必要があると思います。

関連するスレッドを確認してください:

于 2009-01-19T12:57:15.197 に答える
0

もしかしてチャコ?毎秒 50 フレームを処理できるかどうかはわかりませんが、非常にスムーズなリアルタイム プロットをデモで見ました。それは間違いなくmatplotlibよりも速いはずです。

于 2009-01-19T11:27:17.780 に答える