Размер шрифта:
Лепим тулбар на PyQt, экспортируем данные в Excel и HTML
Play

Лепим тулбар на PyQt, экспортируем данные в Excel и HTML

В предыдущей части я рассказывал о создании модуля для запуска SQL-запросов и оболочки, в которой эти модули запускаются. После недолгой работы с запросами возникает очевидный вопрос — а как воспользоваться результатом выборки, кроме как посмотреть на экране?

Для этого стоит сделать дополнительные инструменты экспорта и копирования данных. Экспортировать будем в файл в формате Excel, а копировать в системный буфер в формате HTML.

Но для начала прилепим к нашему главному окну панель инструментов.

Панель инструментов

Напомню, что наше приложение призвано быть простым, универсальным и расширяемым. Чтобы тулбар тоже сделать универсальным и расширяемым, вынесем его определение в файл конфигурации, а выполняемые функции будут находиться во внешних модулях, явно не импортируемых в модуле тулбара. Таким образом добавление новой кнопки и функции сведется к прописыванию их в конфигурационном файле и добавлению модуля в каталог программы.

Для панелей инструментов в Qt есть готовый класс QToolBar, от него породим свой ToolBar. Сейчас нам достаточно одного тулбара, но заложимся на возможность добавления в программу нескольких панелей. Каждой панели нужен свой конфигурационный файл со своим набором кнопок, поэтому имя файла будем передавать параметром при создании тулбара. Конфигурационный файл будет традиционно в формате Ini и кодировке UTF-8.

Синтаксис определения кнопок в наших руках, в простейшем случае нам нужны три вещи:

— текст на кнопке — модуль, содержащий функцию кнопки — функция кнопки

Определимся, что функция кнопки принимает один параметр — текущее дочернее окно. Что именно будет делать модуль с ним — задача модуля кнопки, а задача тулбара ограничивается только его вызовом.

Создадим такой файл tools.ini:

Теперь в питоне разбираем определения из Ini-файла:

Метод выполнения, назначенный всем кнопкам, будет импортировать нужный модуль и вызывать из него назначенную кнопке функцию. Чтобы нам не прописывать каждый модуль в перечне импорта тулбара, воспользуемся библиотекой importlib. Осталось только узнать, что за кнопка была нажата и от какого QAction пришел сигнал — за это отвечает стандартный метод QObject.sender(), далее возьмем сохраненные в нем параметры и сделаем то, что задумано в модуле (что бы это ни было).

Осталось добавить нашу панель в наше главное окно (модуль tasktree.py)

Можем запустить и проверить, появилась ли панель:

Может быть не так симпатично, как на первой картинке, главное, что работает.

Модуль функций инструментов

Теперь самое время сделать модуль с функциями кнопок. Модуль у нас будет один, потому что функции экспорта и копирования будут работать с одним источником данных и по одинаковым правилам, нет смысла разносить их по разным модулям.

Наши функции будут работать с таблицами данных QTableView, который мы использовали в модулях для просмотра результатов запроса. Чтобы сохранить независимость модулей, определять нужный компонент будем «на лету» — либо это текущий выбранный (focused) компонент QTableView в текущем окне, либо первый попавшийся нужного класса среди дочерних элементов текущего окна.

Из таблицы получаем список выбранных ячеек. Если ничего не выбрано, то принудительно выбираем всё.

Наверное, вы уже в курсе, что в Qt вы не получаете массив данных напрямую, вместо этого вы работаете с индексами в модели. Индекс QModelIndex представляет собой простую структуру и указывает на конкретную позицию данных (строку row() и столбец column(), а в иерархии указание на индекс родителя parent()). Получив индекс, можно из него получить сами данные методом data().

Мы получили список индексов выбранных ячеек в модели, но индексы в этом списке следуют в том порядке, в котором пользователь их выделял, а не отсортированные по строкам и столбцам. Нам же удобнее будет работать не со списком, а с словарем (позиция → индекс) и сортированными списками задействованных строк и столбцов.

Еще стоит учесть, что QTableView по умолчанию позволяет выделять несвязанные ячейки, потому в списке индексов могут быть ячейки, практически случайно расположенные:

Поэтому в d.rows есть каждая использованная строка, в d.columns есть каждый использованный столбец, но их сочетание необязательно есть в d.indexes.

Еще нам для пущей красоты нужен перечень наименований столбцов, который выводятся в QTableView. Получим их из модели методом headerData:

До сих пор код для экспорта и копирования был одинаковым, но теперь пошли различия.

Экспорт в Excel

Для экспорта в файлы Excel я воспользовался пакетом xlsxwriter. Он устанавливается, как обычно, через pip:

Документация пакета вполне подробная и понятная, с примерами, поэтому останавливаться на нем не буду. Суть в том, что запись идет по ячейкам, адресуемым по номеру строки и столбца. Если нужно дополнительное форматирование, то нужно определить стиль и указывать его при записи ячейки.

Имя xlsx-файла, в который будем экспортировать, запросим у пользователя, у Qt есть такая функция. В PyQt функция возвращает список из выбранного имени файла и использованного фильтра. Если вернулся список из пустых строк, то это означает, что пользователь отказался от выбора.

Танцы вокруг QDateTime добавлены из-за разного понимания даты/времени в Python, Qt и Excel — во-первых, пакет xlsxwriter умеет работать с питоновским datetime, но не умеет с QDateTime из Qt, поэтому приходится дополнительно его конвертировать специальной функцией toPyDateTime; во-вторых, Excel умеет работать только с датами с 01.01.1900, а всё, что было до этого времени для Excel — просто строка.

Результат экспорта в Excel:

Копирование в системный буфер в формате HTML

Не всегда нужен отдельный файл с выборкой, часто, особенно когда данных немного, удобнее скопировать их в табличном виде в системный буфер (clipboard), а затем вставить в нужное место, будь то Excel, Word, редактор веб-страниц или что-то другое.

Наиболее универсальным способом копирования табличных данных через буфер — это обычный формат HTML. В Windows, *nix и MacOS сильно разные способы работы с буфером (не говоря о том, что их несколько), поэтому хорошо, что Qt скрывает от нас детали реализации.

Всё, что нам нужно — создать объект QMimeData, заполнить его через метод setHtml фрагментом HTML-разметки, и отдать в системный clipboard, который доступен через QApplication

Таблицу собираем построчно, начиная с заголовков.

Результат, вставленный в Word:

Здесь границы таблицы видны только благодаря включенной в Word настройке "Показывать границы текста", на самом деле они невидимы. Чтобы таблица копировалась с явными границами, нужно изменить стиль таблицы в тэге table. Предоставляю это сделать вам.

Заключение

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

Исходники, использованные в примерах, как и ранее, выложены на github под лицензией MIT.

📎📎📎📎📎📎📎📎📎📎