Разработка под Android / [Из песочницы] Построение графиков на Android: sl4a, python и flot

в 6:17, , рубрики: android, flot, javascript, python, sl4a, графики, метки: , , , , ,

Доброго времени суток, %username%!

Введение

Графики — наглядный способ представления информации. Картинка стоит тысячи слов, а график в некоторых случаях часто полностью описывает результаты эксперимента, физического или вычислительного. В конце концов, мне нравятся графики.

Однажды я оказался в ситуации, что данные для построения графика уже есть, а компьютера под рукой нет. Но ведь с такой задачей может справиться и смартфон! Так я смог заставить себя начать применять знания, полученные из наполовину пройденного курса по Python от Google, и использовать sl4a, уже успевший покрыться виртуальной пылью. Итак, для работы понадобятся sl4a (кто еще не слышал такую аббревиатуру, прочитайте это, это и это) + flot (подойдет любая библиотека для построения графиков на js).

Вдоль оси абсцисс будем отсчитывать номер наблюдаемой величины, вдоль оси ординат — её значение. Значения вычисляются следующим образом: есть 30 логов, содержащих строчки вида «value = 0.1 0.2 0.15 0.12 ...», где «0.1 0.2 ...» — значения, «value» — название величины. Значение «1» есть среднее по всем первым числам из соответствующих строк логов («0.1» в примере строки), «2» — по всем вторым и т.д. В итоге получается двумерная матрица размера M*N, где M — количество строк в логе, N — количество чисел в строке. Предполагается, что логи содержат одинаковое количество строк и одинаковое количество чисел в каждой строке.

Реализация

Отображение графика с помощью flot

За построение графика отвечает плагин flot к jquery. Из полного комплекта с сайта разработчиков для нашей задачи понадобятся только файлы jquery.flot.js и jquery.js. Сам код log_manager.html:

<html>    <head>     <title>Plot</title>     <link href="layout.css" rel="stylesheet" type="text/css">     <script src="jquery.js"></script>     <script src="jquery.flot.js"></script>   </head>    <body>     <div id="placeholder" style="width:535px;height:270px;"></div>     <script>         var plotData = function(d) {$.plot($("#placeholder"), [ {label: "flux", data: eval(d.data), color: "rgb(255, 100, 100)" }] );};         var droid = new Android();         droid.registerCallback("plotData", plotData);     </script>    </body>  </html> 

Строка 13 — построение графика с помощью flot. Например, можно написать

$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16]], color: "rgb(255, 100, 100)" }] );

и на графике отобразится кусок параболы. Таким образом, данные для построения должны иметь вид [[x0,y0],[x1,y1],[x2,y2],[x3,y3], ...]. Самый простой способ, пришедший мне в голову — подготовить их в python-скрипте в строку точно такого же вида и обернуть в javascript в eval(), которая выполнит переданную строку как если бы это был кусок js-кода. Далее я использую именно этот способ.

Модификация существующих и добавление новых свойств отображения кривых на графике реализуется просто. Например, чтобы отключить тень под кривой, достаточно добавить «shadowSize: 0»:

$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16]], shadowSize: 0, color: "rgb(255, 100, 100)" }] );

Две кривых на одном графике:

$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16],[5,25]], shadowSize: 0, color: "rgb(255, 100, 100)" }, {label: "flux", data: [[0,0],[5,25]], shadowSize: 0, color: "rgb(255, 100, 100)" }] );

В строке 14 создается объект для взаимодействия с Android API (его возвращает встроеная в sl4a функция «Android()»).

В 15 строке описывается, как обрабатывать полученный event с именем «plotData». Как только получен event с таким именем, вызывается функция «plotData». Переданные с ним данные (строка-массив) будут находиться в <имя_входной_переменной_в_функции>.data.

Осталось только написать скрипт, который файлы прочитает, строку подготовит и пошлет её. Об этом следующая часть.

Подготовка данных с помощью Python

Код log_manager.py:

#!/usr/python  ## Import libraries #  android for access to Android API #  time for sleep(sec)  import android, time  #  Filename is "<FileCounter>-of-<NumberOfFiles>.log" filename = "/sdcard/864x864x30-0-of-30.log"  #  Get number of files N = int(filename.split("/")[-1].split("-")[-1].split(".")[0])  # Read first file file = open(filename,"r")  value = [] for line in file.readlines():      if "value =" in line:         value.append([])         for val in line.split(" "):             try:                 value[-1].append(float(val))                         except:                 continue   # Read other files for f in range(1, N):     file = open(filename.replace("-0-","-"+str(f)+"-"))     i = 0     for line in file.readlines():         if "value =" in line:             j = 0             for val in line.split(" "):                 try:                     value[i][j] += float(val)                     j += 1                 except:                     continue     i += 1  # Prepare string for flot toBePlotted = "[" for i in range(0, len(value[-1])):     toBePlotted += "[" + str(i) + "," + str(value[-1][i]) + "]," toBePlotted += "]"  # Get droid object to use Android API droid = android.Android()  # Set web view droid.webViewShow('file:///sdcard/sl4a/scripts/log_manager.html')  # Wait 3 seconds while web view starts time.sleep(3)  # Post event 'plotData' to web view droid.eventPost('plotData', toBePlotted) 

Данный код плох, так писать не стоит. Но свою задачу он выполняет: получает матрицу, состоящую из соответствующих средних по логам.

В строках 1-43 происходит считывание данных из файлов в двумерный массив value. В 44-48 подготавливается строка для вывода графика в flot. В 50-60 создается webView на основе странички log_manager.html (54), ждем 3 секунды, скрипт ждет, пока страничка загрузится (плохой подход!) (57), и посылает событие с данными для построения графика (60).

Результаты

Чтобы протестировать написанный скрипт, необходимо положить log_manager.py, log_manager.html, jquery.flot.js и jquery.js в папку /sdcard/sl4a/scripts. В корне карты памяти должны лежать файлы с именами «864x864x30-0-of-30.log»...«864x864x30-29-of-30.log». В каждом логе должно быть записано записано одинаковое число строк вида «value = 0.2 0.34 0.343 ...» с одинаковым количеством чисел в каждой строке. Скрипт построит график на основе средних значений последних строк логов и имеющий вид:

Разработка под Android / [Из песочницы] Построение графиков на Android: sl4a, python и flot

Архив с файлами, упомянутыми в статье. Также в архиве присутствуют jquery.js и jquery.flot.js из комплекта с сайта flot.

Автор: Kenarius


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js