Я создаю приложение с GUI для сбора и обработки данных с микроконтроллера на Python с помощью PyQt. И вот я наконец-то доделал часть функционала, предназначенного для взаимодействия компьютера с платой STM32, теперь необходимо было сделать интерфейс для обработки данных, в котором легко можно было бы настраивать параметры выполнения программы. Я начал думать, как не вносить в программу кучу флагов с соответствующими if-else конструкциями, и вот, что я придумал.
Ещё немного контекста
Если кратко, то моя программа строит перемещение железнодорожной телеги с платой по данным гироскопа и акселерометра, этакая автономная система навигации. Отдельно собираются данные, затем запускается их обработка. В результате, пользователь получает необходимые графики и файлы с данными в удобном виде. Нюанс заключался в том, что я хотел предоставить пользователю возможность самому решать, какие результаты ему нужны, а какие нет. Например, в один раз пользователю необходимы и графики исходных данных, и графики отфильтрованных данных с помощью фильтра Калмана (теория алгоритма хорошо описана тут, а практическое применение тут). А в другой раз ему не нужны графики исходных данных. Для этого он активирует или деактивирует соответствующий checkBox в GUI. И вот как это реализовал.

Реализация
Для начала нужно состояния всех чекбоксов сохранить в один словарь любым способом. Получаем такой словарь:
processing_params = { 'Raw_Data': { 'plotting_init_data': True, 'plotting_buffers_data': False, 'plotting_raw_data': True, 'kwargs': {} } }
Во вложенный словарь ‘kwargs’ можно записать необходимые параметры, которые будут принимать методы. В данном случае, оставим его пустым.
Всю последовательность шагов обработки данных я реализовал в отдельном классе DataProcessing, в котором содержатся как и методы обработки данных, так и необходимые поля, в которых хранятся необходимые данные.
Вот основные методы для визуализации собранных данных:
def _plotting_init_data(self): print('Построение графиков данных выставки из файла ...') ... def _plotting_buffers_data(self): print('Построение графиков буферных данных из файла ...') ... def _plotting_raw_data(self): print('Построение графиков исходных данных из файла ...') ...
А теперь вот моё ноухау (ну или очередной велосипед). В класса DataProsessing мы создаём второй словарь, ключи в котором полностью совпадают с ключами первого словаря, а в значения записываем сами методы, отвечающие за тот или иной функционал. Продолжая пример, создаём такой словарь:
self._config = { 'Raw_Data': { 'plotting_raw_data': self._plotting_raw_data, 'plotting_init_data': self._plotting_init_data, 'plotting_buffers_data': self._plotting_buffers_data } }
И теперь мы можем запустить выполнение всех выбранных функций одним циклом!
# ------------------------------- # Визуализация исходных данных # ------------------------------- def _raw_data_plotting(self): print('# -------------------------------') print('# Визуализация исходных данных') print('# -------------------------------') class_params: dir = self._config['Raw_Data'] user_params: dir = self._parameters['Raw_Data'] for key, value in user_params.items(): if value: class_params[key](**user_params['kwargs'])
А благодаря тому, что Python позволяет передавать в функции неопределённое количество именованных переменных, функции могут иметь абсолютно разные входные параметры (что лучше избегать в данном контексте, но возможность такая есть).
В итоге, получаем только необходимые результаты выполнения и такой вывод в консоль:
# ------------------------------- # Визуализация исходных данных # ------------------------------- Построение графиков данных выставки из файла ... Построение данных из файла ...
Представленный код класса DataProsessing целиком:
class DataProcessing: def __init__(self, parameters: dict): self._received_data = {} self._config = { 'Raw_Data': { 'plotting_raw_data': self._plotting_raw_data, 'plotting_init_data': self._plotting_init_data, 'plotting_buffers_data': self._plotting_buffers_data } } # --------------------------------- # Основная функция обработки данных # --------------------------------- def start(self): self._decoding() self._file_classification() self._raw_data_plotting() self._raw_data_analysis() # ------------------------------- # Чтение данных из файлов # ------------------------------- def _decoding(self): ... # ------------------------------- # Классификация файлов # ------------------------------- def _file_classification(self): ... # ------------------------------- # Визуализация исходных данных # ------------------------------- def _raw_data_plotting(self): print('# -------------------------------') print('# Визуализация исходных данных') print('# -------------------------------') class_params: dir = self._config['Raw_Data'] user_params: dir = self._parameters['Raw_Data'] for key, value in user_params.items(): if value: class_params[key](**user_params['kwargs']) def _plotting_init_data(self): print('Построение графиков данных выставки из файла ...') ... def _plotting_buffers_data(self): print('Построение графиков буферных данных из файла ...') ... def _plotting_raw_data(self): print('Построение графиков исходных данных из файла ...') ...
Заключение
Благодаря такой реализации, программу легко дополнять необходимым функционалом. Пользователю необходимо нажать буквально пару кнопок для детальной настройки результатов работы программы, а Вам, как разработчику, добавить несколько полей в нужные словари.
Возможно, есть более элегантный метод решения подобной задачи. Возможно, я сделал очередной велосипед. Но может быть, данная статья поможет Вам в своих проектах.
Всем добра.
ссылка на оригинал статьи https://habr.com/ru/articles/934314/
Добавить комментарий