Перейти к содержанию

Графический интерфейс программы. Разработка приложений на python.

В работе рассмотрены примеры использования python для создания кросс-платформенных сервисов визуализации значений параметров датчиков в реальном времени.

Теоретическая часть

Одна из отличительных особенностей языка Python - его универсальность. В предыдущих работах microPython использовался как язык программирования встраиваемых систем на микроконтроллерах семейства Arduino. В данной работе приводится его более традиционное применение - разработка приложений и сервисов.

Выполнение python скриптов

Выполнение Python скриптов возможно несколькими способами. Простейший - внутри интерпретатора Python.

$ python3  #запуск интерпретатора python в терминале Linux
Python 3.11.2 (main, Feb  8 2023, 00:00:00) [GCC 13.0.1 20230208 (Red Hat 13.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello World")
Hello World
>>>

Это не самый удобный вариант для разработки ПО, однако наиболее доступный для выполнения простейших операций и проверки работы отдельных функций.

Следующий вариант - запуск Python скрипта из файла.

$ touch hello.py #создание файла hello.py
$ echo "print(123)" > hello.py #запись в этот файл строки print(123)
$ python3 hello.py #исполнение файла интерпретатором python
123

Однако для удобства работы со скриптами Python в исследовательских целях наиболее часто применим Jupyter Notebook - интерактивный блокнот с исполняемой средой Python.

Jupyter Notebook

Jupyter Notebook поставляется в качестве расширения к python и устанавливается разными методами. Предыдущая лабораторная работа затрагивала использование Jupyter как части программного комплекса GNS3. Желающие могут установить jypiter notebook в виртуальной машине с ubuntu/debian в два шага:

предполагается, что python в системе уже установлен, а также пользователю не требуется установка и настройка окружений среды, таких как conda

$ pip3 install jupyter #установка пакета
$ jupyter notebook #запуск notebook

Для простоты эксперимента проведение лабораторной работы доступно и в экспериментальном проекте от Jupiter - браузерной версии программы JupyterLite, доступной по ссылке:

https://jupyter.org/try-jupyter/


Рисунок 1 – Интерфейс Jupyter Notebook

Исполнение Python файлов в данной среде выполняется в специальном формате Notebook файла ipynb. Это позволяет разделить сегменты кода python, оформление файла в Markdown и прочие элементы.

Для создания нового блокнота достаточно нажать на Python в секции Notebook. При этом в левой секции появится файл Untitled.ipynb.


Рисунок 2 – Интерфейс редактора

Откроется окно редактирования файла. Первая кнопка - сохранение, при первом сохранении будет предложено переименовать файл

Функционал всех кнопок управления виден при наведении курсора:

  • Сохранение документа
  • Добавить ячейку ниже текущей
  • Вырезать данную ячейку
  • Скопировать данную ячейку
  • Вставить ячейку из буфера обмена
  • Запустить эту и следующие ячейки
  • Прервать выполнение
  • Перезапустить интерпретатор
  • Перезапустить интерпретатор и выполнить ячейки
  • Выбор формата текущей ячейки (нас интересуют Code и Markdown)

Ячейки в Notebook

Ячейки (cells) - блоки кода, которые форматируются или выполняются интерпретатором в соответствие с их предназначением. Для code происходит выполнение ячеек в Python. Для markdown происходит преобразование кода в форматированный текст, что позволяет оформить документ.


Рисунок 4 – Ячейка Markdown и ячейка Code

Основные инструменты Python

Большинство функций, которые может нам дать Python, содержатся в его библиотеках. Существует три варианта их использования:

import time #подключение библиотеки напрямую
import numpy as np #подключение библиотеки с подменой названия
from matplotlib import pyplot as plt #подключение части библиотеки

Основные библиотеки python

  • numpy (как правило, импортируется под названием np) - один из самых фундаментальных пакетов в Python - универсальный пакет для обработки массивов. Он предоставляет высокопроизводительные объекты многомерных массивов и инструменты для работы с массивами.

  • scipy (sc) - содержит модули для эффективных математических процедур, таких как линейная алгебра, интерполяция, оптимизация, интеграция и статистика. Основной функционал библиотеки SciPy построен на NumPy и его массивах.

  • matplotlib (plt) - функционал для построения различных графиков, включая гистограммы, столбцовые, точечные, круговые диаграммы.

  • requests - позволяет делать http(s) запросы с минимальным количеством кода.

  • random - генератор псевдо-случайных чисел в различных численных форматах.

  • math - математические операции, тригонометрия, формулы.

  • os - функции операционной системы.

Подробнее с функционалом, доступным внутри python, можно и стоит ознакомиться на сторонних сайтах, например: https://losst.pro/standartnye-biblioteki-python.

Используемые в работе библиотеки и методы

pyodide.http, requests, json

В силу особенностей среды JupyterLite (использование интерпретатора Pyodide вместо Python) нарушена работа части библиотек, в том числе requests. Если вы используете JupyterLite, применяйте следующий код для выполнения запросов:

from pyodide.http import open_url

url = "https://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new"
request = open_url(url).read()

При выполнении ЛР в Jupyter Notebook или запуске скрипта в python, применима библиотека requests:

import requests

url = "https://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new"
request = requests.get(url=url)

Пример использования http запроса

Сервис https://open-meteo.com предоставляет открытый API для прогноза погоды и истории данных о погоде. Составленный в соответствие с документацией запрос позволяет получить величину средней температуры в Москве за некоторый день ноября 2023 года с помощью request/pyodide.http и библиотеки json, преобразовывающей текст запроса в python словарь.

from pyodide.http import open_url
import json

day = 9
url = f"https://archive-api.open-meteo.com/v1/era5?latitude=37.21&longitude=55.98&start_date=2023-11-{day:02}&end_date=2023-11-{day:02}&daily=temperature_2m_mean"

request = open_url(url).read()
request = json.loads(request)
print(request)
print(request['daily']['temperature_2m_mean'][0])

Результат запроса:

{
    "latitude":37.223198,
    "longitude":56.02649,
    "generationtime_ms":0.03802776336669922,
    "utc_offset_seconds":0,
    "timezone":"GMT",
    "timezone_abbreviation":"GMT",
    "elevation":1440.0,
    "daily_units":{
        "time":"iso8601",
        "temperature_2m_mean":"°C"
    },
    "daily":{
        "time":[
            "2023-11-09"
        ],
        "temperature_2m_mean":[
            7.2
        ]
    }
}

7.2

Аналогичным образом можно запрограммировать измерительное устройство на базе ESP32 и получать данные с него в формате json.

time

Из библиотеки time потребуется функция sleep, позволяющая задержать выполнение скрипта на определённый промежуток времени. Также мы применим несколько функций, сохраняющих текущее время.

import time

print("Start")
time.sleep(1) #пауза на 1 секунду
print("Stop")

matplotlib

Библиотека matplotlib будет использована для построения графиков. В следующем примере можно познакомиться с основными функциями для построения линейных графиков зависимости величин.

Для начала создадим тестовые данные:

plots = [
    {
        'name': "Temperature outside",
        'unit': "T, ºC",
        'values': [20, 22, 22, 20, 21, 24, 25],
        'days': [10, 11, 12, 13, 14, 15, 16]
    },
    {
        'name': "Rain probability",
        'unit': "R, %",
        'values': [10, 5, 15, 30, 30, 5, 5],
        'days': [10, 11, 12, 13, 14, 15, 16]
    }
]

В массиве plots содержатся словари, каждый отвечающий за одну из диаграм на графике. К ним можно добавить минимум и максимум по осям, цвет и стиль оформления линий, можно задать, на каких подграфиках (subplot) будут отрисовываться диаграмы - все эти данные будут обработаны позднее.

Также для удобства применим функцию enumerate, чтобы пронумеровать каждый элемент массива, и используем for in конструкцию как более удобный аналог for i in range():

for i, plot in enumerate(plots):
    ...

Полный пример matplotlib:

from matplotlib import pyplot as plt

plots = [
    {
        'name': "Temperature outside",
        'unit': "T, ºC",
        'values': [20, 22, 22, 20, 21, 24, 25],
        'days': [10, 11, 12, 13, 14, 15, 16]
    },
    {
        'name': "Rain probability",
        'unit': "R, %",
        'values': [10, 5, 15, 30, 30, 5, 5],
        'days': [10, 11, 12, 13, 14, 15, 16]
    }
]

fig, axs = plt.subplots(len(plots), figsize=(12,4))
#создать subplot с размером 12*3 для каждого графика
fig.suptitle('Weather forecast')
#заголовок для всего графика

for i, plot in enumerate(plots):
    axs[i].plot(plot['days'], plot['values'], label=plot['name'])
    axs[i].legend()
    axs[i].set_xlabel('t, д')
    axs[i].set_ylabel(plot['unit'])
    i=i+1

plt.tight_layout() #увеличенные отступы
plt.show() #показать график

Обновление графиков в реальном времени

Существует несколько способов обновлять графики matplotlib в реальном времени, им посвящены достаточно длительные обсуждения на stackoverflow. Для работы с jupyter подходит следующий:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import time

fig, axs = plt.subplots(1, figsize=(12,4))

for i in range(10): #показать 10 кадров
    clear_output(wait=True)
    #очистка предыдущего графика

    ###
    # построение некоторого графика
    plt.plot(range(i), [(i-4)**2 for i in range(i)])
    plt.tight_layout()
    plt.show() #показ графика заново
    ###

    time.sleep(0.5) #сон на полсекунды

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

for i in range(10): #отрисовать 10 кадров
    clear_output(wait=True)
    plot_graphs() #от plt.subplots() до plt.show()
    time.sleep(0.5)

Практическая часть

Требуется разработать программу на python, выполняющую роль графического интерфейса для некоторой измерительной системы. Программа строит не менее двух графиков (разные subplot или в одной координатной плоскости). График обновляется несколько раз в реальном времени, показания добавляются или замещаются более новыми.

Предлагается следующая структура программы:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import time
from pyodide.http import open_url
import json

plots = [
    {
        'name': "Mean temperature",
        'unit': "T, ºC",
        'values': [],
        'days': []
    },
    ...
]

def plot_graphs():  #функция рисования графиков
    fig, axs = plt.subplots(len(plots), figsize=(12,4))
    ...
    plt.show()

def update_plots(day): #функция добавления данных
    ...
    plots[0]['values'].append(temp)
    plots[0]['days'].append(day)

for i in range(1,10):
    update_plots(i)
    clear_output(wait=True)
    plot_graphs()
    time.sleep(0.5)

Требования к программе:

  • Производится корректное отображение графиков, графики не дублируются
  • На графиках присутствуют подписи осей, подписаны названия измеряемых величин (title либо legend)
  • Данные для работы берутся либо из открытых данных (api погоды, онлайн генераторы тестовых данных), либо генерируются случайным образом (библиотека random), либо берутся из некоторого набора подготовленных данных; но массив данных (plots в примерах) обязан быть пустым и пополняться только по мере работы скрипта.
  • Данные не обязаны быть правильными. В случае недоступности открытых данных за ноябрь-декабрь 2023 допустимо использовать данные за 2022.

Варианты работы:

Вариант работы соответствует номеру студента в группе.

N График 1 График 2 Месяц
1 Количество учебных пар Длительность светового дня Февраль
2 Средняя температура за день Максимальная температура Март
3 Средняя температура за день Минимальная температура Апрель
4 Количество учебных пар Интенсивность дождя Сентябрь
5 Время рассвета Длительность светового дня Октябрь
6 Время заката Длительность светового дня Ноябрь
7 Уровень выпавшего снега Длительность заката Декабрь

Оформление работы

Работа оформляется в виде ipynb файла с описанием элементов программы (ячеек кода), рабочим кодом программы.