LoginSignup
1
5

More than 5 years have passed since last update.

Pythonistaでリアルタイムグラフ表示(pyplot vs ui.WebView+Google Charts)

Last updated at Posted at 2018-12-30

概要

Pythonista でリアルタイムグラフ表示を行おうとして、最初 pyplot を試したところ結構遅かったので ui.WebView 上で Google Charts を使ったグラフ表示と速度比較を行ってみました。

環境

実行結果

表示対象のデータは Raspberry Pi に接続した BME280 の温度と湿度の値で、1秒間隔で取得して最新の300件(5分間)のグラフ表示の更新にどれだけかかるかを計測しています。

上側の pyplot による表示は更新に 0.521 秒
下側の ui.WebView + Google Charts による表示は更新に 0.233 秒

元々やりたかったのは 0.05 秒間隔で送られてくるデータの表示で、グラフの更新間隔は遅くするとしても、更新中に送られてくるデータを取りこぼしてしまうのが悩みのタネです。

IMG_0300.png

Pythonista プログラム

realtime_charts.py
from matplotlib import pyplot as plt
from matplotlib import ticker
import io
import requests
import time
import ui

HTML = '''
<html>
    <head>
        <title>温度・湿度 - Google Charts</title>
        <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
        <script type="text/javascript">
            google.charts.load('current', {'packages':['line', 'corechart']});
            google.charts.setOnLoadCallback(setupChart);

            var optionLineTH = {
                    title: '温度&湿度の変化',
                    width:  1024,
                    height: 350,
                    series: {
                        0: {targetAxisIndex: 0,
                            color: 'red'},
                        1: {targetAxisIndex: 1,
                            color: 'blue'}
                    },
                    vAxes: {
                        0: {title: '温度',
                            format: '#.#℃'},
                        1: {title: '湿度',
                            format: '#.#%'}
                    },
                    hAxis: {
                        format: '#.#'
                    },
                    vAxis: {
                        titleTextStyle: {italic: false}
                    //  viewWindow: {
                    //    max: 30
                    //  }
                    }
                };

            var dataLineTH;
            var chartLineTH;

            function setupChart() {
                dataLineTH = new google.visualization.DataTable();
                dataLineTH.addColumn('number', '秒');
                dataLineTH.addColumn('number', '温度');
                dataLineTH.addColumn('number', '湿度');

                //chartLineTH = new google.charts.Line(document.getElementById('line_chart_temp_hum'));
                chartLineTH = new google.visualization.LineChart(document.getElementById('line_chart_temp_hum'));
            }
            function updateChart(sec, temperature, humidity) {
                dataLineTH.addRow([sec, temperature, humidity / 100])
                if (dataLineTH.getNumberOfRows() > 300)
                    dataLineTH.removeRow(0)

                //chartLineTH.draw(dataLineTH, google.charts.Line.convertOptions(optionLineTH));
                chartLineTH.draw(dataLineTH, optionLineTH);
            }
        </script>
    </head>
    <body>
        <div>
            <div id="line_chart_temp_hum"></div>
        </div>
    </body>
</html>
'''

class MyView(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def will_close(self):
        global action_loop
        action_loop = False


frame_w = 1024
frame_h =  700
main_view = MyView(frame=(0, 0, frame_w, frame_h))
main_view.background_color = 'white'

plt_view = ui.ImageView(frame=(0, 0, frame_w, int(frame_h / 2)))
plt_info_view = ui.Label(frame=(0, 0, 150, 30))
main_view.add_subview(plt_view)
main_view.add_subview(plt_info_view)

web_view = ui.WebView(frame=(0, int(frame_h / 2), frame_w, int(frame_h / 2)))
web_info_view = ui.Label(frame=(0, int(frame_h / 2), 150, 30))
main_view.add_subview(web_view)
main_view.add_subview(web_info_view)

main_view.present()

plt.rcParams['axes.labelsize'] = 8
plt.rcParams['xtick.labelsize'] = 8
plt.rcParams['ytick.labelsize'] = 8
fig = plt.figure(figsize=(9, 3))
fig.suptitle('Temperature & Humidity')

ax1 = fig.add_subplot(111)
ax1.grid(True)
ax1.set_ylabel('Temp')
ax2 = ax1.twinx()
ax2.grid(True)
ax2.set_ylabel('Hum')

e_time = []
data_t = []
data_h = []
line_t, = ax1.plot(e_time, data_t, label='Temp', color='red')
line_h, = ax2.plot(e_time, data_h, label='Hum', color='blue')
ax1.legend(bbox_to_anchor=(0.11, 1.12), frameon=False)
ax2.legend(bbox_to_anchor=(0.21, 1.12), frameon=False)
ax1.yaxis.set_major_formatter(ticker.FormatStrFormatter('%.1fC'))
ax2.yaxis.set_major_formatter(ticker.FormatStrFormatter('%.1f%%'))

web_view.load_html(HTML)

s = requests.Session()
action_loop = True
start_time = time.time()
while action_loop:
    event_time = time.time()

    # Web Server に対する GET で以下のようなデータが返るようにしてあります。
    # {"timestamp": "Sun Dec 30 23:27:33 2018", "temperature": 20.00649283626699, "pressure": 1018.4915113213154, "humidity": 46.925826193156354}
    r = s.get('Web Server URL')
    e = r.json()

    # pyplot によるグラフ表示
    plot_start = time.time()
    e_time.append(event_time - start_time)
    if len(e_time) > 300:
        e_time.pop(0)
    data_t.append(e['temperature'])
    if len(data_t) > 300:
        data_t.pop(0)
    data_h.append(e['humidity'])
    if len(data_h) > 300:
        data_h.pop(0)
    line_t.set_data(e_time, data_t)
    line_h.set_data(e_time, data_h)
    ax1.set_xlim(e_time[0], e_time[-1]+0.1)
    ax1.set_ylim(min(data_t), max(data_t)+0.1)
    ax2.set_ylim(min(data_h), max(data_h)+0.1)
    with io.BytesIO() as bIO:
        plt.savefig(bIO)
        plt_view.image = ui.Image.from_data(bIO.getvalue())
        del bIO
    plt_info_view.text = ' {:6.3f} sec/upd'.format(time.time() - plot_start)

    # Google Charts によるグラフ表示
    plot_start = time.time()
    web_view.evaluate_javascript(
        'updateChart({},{},{});'.format(
            event_time - start_time,
            e['temperature'],
            e['humidity']))
    web_info_view.text = ' {:6.3f} sec/upd'.format(time.time() - plot_start)

    # 1秒間隔での更新となるように sleep
    time.sleep(max(0, 1 - (time.time() - event_time)))
1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5