Введение
С помощью библиотеки pywin32 попробуем создать и наполнить XLSB файл. Сделаем так, чтобы основной класс вызывался через оператор with
За пределами статьи:
- заранее были подготовлены файлы для работы с виртуальной средой. О том, как их подготавливать, описываю в отдельной статье: https://dzen.ru/a/aEKVHTM_GULyazKa
- стартовые исходники для статьи лежат в GitLab. В рамках статьи не объясняется, как устанавливать и настраивать git. Всё это описываю в отдельной статье: https://dzen.ru/a/aGgSbU4TnF-_7wL_
- должен быть установлен Excel от версии 2007 и старше
В конце статьи есть ссылка в GitLab на финальную версию программы.
Используемые технологии
ОС: Windows 10
Язык: Python 3.12
Основные библиотеки: pywin32
Прочее: Microsoft Office 2013
План работы
- Клонируем репозиторий с GitLab
- Настраиваем виртуальную среду
- Планируем архитектуру программы
- Создаём программу
- Запускаем программу
Клонируем репозиторий с GitLab
Создадим папку "XLSB_CREATOR", куда будем клонировать репозиторий
mkdir XLSB_CREATOR
Клонируем репозиторий
git clone https://gitlab.com/pytechnotes1/xlsb_creator XLSB_CREATOR
Заходим в папку
cd XLSB_CREATOR/
Делаем чекаут версии коммита, который я сделал специально для статьи
git checkout 8485adfbc6d6a04369068eb243b85843204f265b
Настраиваем виртуальную среду
Открываем в cmd наш проект XLSB_CREATOR и создать виртуальную среду
py -3.12 -m venv venv
Активируем среду
venv\Scripts\activate
Производим установку библиотек из файла, который я подготовил заранее
pip install -r requirements.txt
Выходим из виртуальной среды
deactivate
Планируем архитектуру программы
Архитектуру сделаем без излишеств, всего будет четыре скрипта:
- app.py - скрипт, который будет запускать нашу программу.
- creator.py - основная программа, которая будет создавать xlsb файл
- copier.py - функционал, который не связан напрямую с манипуляциями с xlsb файлом, выведен в отдельный скрипт
- creator_format.py - как такового конфигурационного файла в программе нет, но был создан скрипт, который является неким контейнером входящих параметров программы.
Создаём программу
Первым делом изучим файл app.py.
В файле ничего менять не будем.
В 1 строке указываем шебанг, в котором указывается путь до интерпретатора в виртуальной среде.
В 7 и 8 строках указываем путь к будущему XLSB файлу, а так как программа будет напрямую обращаться к библиотеке Excel, которая не знает, в какой директории запущена программа на Python, необходимо указать полный путь до XLSB файла, который будем создавать. В рамках теста эксель файл будет создаваться в той же директории, где расположен файл app.py.
На 9 строке указываем размерность начальной строки для записи. Первая ячейка будет (1, 1), т.е. "A1", последняя ячейка будет (1, 3), т.е. "A3".
С 10 по 15 строку тестовый набор данных. Программа сделана максимально просто, чтоб можно было легко ее переделать под себя, например для работы с библиотекой pandas.
На 17 строке вызываем нашу программу, применяя оператор with. В терминологии Python это называется "вызов менеджера контекста".
С 18 по 20 строки производим построчную запись в файл.
Начнем реализацию с файла creator_format.py
В этом файле реализуем 2 небольших класса:
- RangeData - будет хранить координаты диапазона ячеек
- XLConfig - будет хранить параметры XLSB файла
Для реализации нам понадобится библиотека dataclasses
from dataclasses import dataclass
Классам мы присваиваем декоратор @dataclass
Для класса XLConfig в декоратор передаем дополнительный параметр frozen=True, таким образом класс XLConfig станет неизменяемым.
Класс RangeData нам нужен изменяемым, т.к. диапазон ячеек будет постоянно меняться.
По параметрам RangeData - это просто координаты начальной и конечной ячеек строки, которая будет заполняться данными.
Касательно параметров XLConfig:
- file_path - полный путь к XLSB файлу
- visible_flag - (Application.Visible) - Если указать False, то все создание и заполнение файла будет происходить в фоновом режиме, но есть шанс, что что-то пойдет не так и процесс Excel не завершится и останется висеть в активных процессах в диспетчере задач. Если поставить True и что-то пойдет не так, то процесс не завершится, но мы будем видеть окно Excel открытым.
- screen_update_flag - (Application.ScreenUpdating) - Если указать False, то программа будет работать быстрее, но мы не увидим магии заполнения экселя:)
- ac_sheet - программа умеет работать только с Sheet(1)
- file_format - Для создания XLSB файла необходимо указать значение: 50
Теперь откроем файл creator.py
В файле частично намечена архитектура класса XLSBCreator.
Из архитектуры видно, что:
- будет только один внешний метод set_row
- есть методы __enter__ и __exit__ , реализовав которые, XLSBCreator станет менеджером контекста и сможет вызываться через оператор with
Первым делом начнем заполнять конструктор __init__:
В момент создания экземпляра класса XLSBCreator будут созданы:
- self.conf - конфиг с параметрами для создания Excel файла
- self.range - диапазон ячеек экселя в формате конструкций языка Python
- self.target_range - объект Range библиотеки Excel
Укажем импорт классов, которые создали ранее
from creator_format import RangeData, XLConfig
Реализуем метод __enter__:
В рамках метода вызовем класс Copier (его реализуем позже) , а также выполним ряд действий для создания эксель файла.
Если по причине COM-ошибки произойдет падение создания файла, то вызовется метод __exit__.
Укажем необходимые импорты
import sys
from typing import Self
from pywintypes import com_error
from copier import Copier
Реализуем методы _create_new_app, _create_new_wb и _activate_sheet:
Создание эксель файла и манипуляции с ним происходит с помощью библиотеки Excel, т.е. если бы создавали эксель файл с помощью VBA, мы бы обращались бы к той же библиотеке, к тем же объектам и функциям с теми же параметрами.
Для удобства манипуляции с объектами эксель, добавляем их в переменные класса: self.xl_app , self.xl_wb , self._xl_sheet
По умолчанию программа работает только с первым листом в эксель файле.
Добавляем в импорт внешнюю библиотеку
import win32com.client
Реализуем метод __exit__:
В задачу данного метода входит:
- сохранить и закрыть эксель файл
- в случае COM-ошибки, просто закрыть приложение, иначе в диспетчере задач будут висеть и накапливаться незакрытые процессы MS Excel
В дополнение к COM-ошибкам еще можно обработать прочие ошибки, но я не стал этого делать
Реализуем метод set_row:
Метод принимает на вход list, где лежат данные для заполнения одной строки. Далее метод получает координаты для объекта Range и создает этот объект. После диапазон ячеек, на который ссылается объект Range, заполняется данными, которые поступили методу на вход. И в конце метод определяет следующие координаты ячеек.
Реализуем методы _get_cells_for_range и _next_cells:
Метод _get_cells_for_range создает объекты Cells, которые потом будут использоваться для создания объекта Range.
Метод _next_cells смещает координаты ячеек на 1 строку вниз.
Теперь перейдем к файлу copier.py
В файле намечена архитектура класса Copier.
В задачу класса будет входить 2 задачи:
- проверить, существует ли файл, который планируется создать с помощью класса XLSBCreator
- и если существует такой файл, то переименовать его с указанием даты последней модификации файла
Из архитектуры видим, что все методы у класса являются методами класса. Можно было бы сделать все методы статическими, но при написании логики класса мне показалось это менее удобным.
Сразу укажем библиотеки, которые нам понадобятся
from datetime import datetime as dt
from pathlib import Path
Касательно библиотеки pathlib, то выбор на нее пал, так как её богатый функционал лучше подошел к задаче, чем функционал более привычной библиотеки os.
Начнем с реализации методов run и _check_file:
В рамках этих двух методов прекрасно показывается функционал класса, мы просто проверяем, существует ли файл по пути, который будет использовать XLSBCreator, и если существует, то вызываем _rename_exist_file.
Реализуем методы _rename_exist_file, _get_file_time и _get_new_file_name:
В рамках метода _get_file_time мы берем последнюю дату модификации файла. Вообще, это не очень надежный параметр, т.к. если зайти в файл и нажать кнопку Сохранить, то дата модификации изменится, хотя мы в файле ничего не меняли. Но для текущей задачи нам это неважно.
В рамках метода _get_new_file_name мы формируем новое название файла.
В рамках метода _rename_exist_file мы производим переименование файла.
На этом все, можем запускать программу.
Запускаем программу
Запускаем скрипт через "py", таким образом запустится интерпретатор из виртуальной среды.
py app.py
Поздравляю, файл создался и заполнился данными.
Исходники проекта
Подписывайтесь на Дзен, а также приглашаю в мой телеграмм канал, там публикую другой, но не менее интересный контент.