Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

概要

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)))
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away