Доброго времени суток, %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 ...» с одинаковым количеством чисел в каждой строке. Скрипт построит график на основе средних значений последних строк логов и имеющий вид:
Архив с файлами, упомянутыми в статье. Также в архиве присутствуют jquery.js и jquery.flot.js из комплекта с сайта flot.
Автор: Kenarius