Давно про Qt не писали, потому сделаем что-то простое но мощное. Фреймворк был создан уже более десяти лет тому (скоро и 20), но всё ещё продолжает нас радовать и удивлять благодаря усилиям Qt сообщества.
Я хочу показать пример разработки приложения с затратой небольших усилий на стыке технологий создания десктопных приложений и веб-программирования.
Несколько недель тому я искал способ конвертации специфических PDF документов в изображения с учетом возможности автоматизации и скриптования в будущем. Конечно есть старожил — пакет ImageMagic с утилитой convert, но к сожалению я столкнулся с тем что этот инструмент не так хорош как я ожидал именно на этих файлах — не рендерит корректно многие файлы и что совсем не радовало — многие иллюстрации были испорчены.
Я стал искать другие инструменты и хотя всевозможных утилит очень много но у каждой есть свои особенности так что я так и не выбрал какую использовать.
Вместо этого у меня появилась идея, может ли Qt как довольно зрелая технология помочь мне? В Qt очень просто создать PDF документ с помощью QPrinter, но как насчет обратной функциональности - сделать изображение из PDF страницы? А ведь есть ещё одна хорошо проработаная технология — PDF.js.
Можно ли совместить эти две технологии? Конечно! Qt имеет компонент QWebEngineView. Продемонстрируем в коде:
По быстрому на основе QMainWindow:
m_webView = new QWebEngineView(this);
m_webView->load(url);
setCentralWidget(m_webView);
Результатом будет окно с веб страницей внутри. Теперь очередь PDF.js. Проект имеет довольно большой объём кода, но есть возможность собрать компактную (minified) версию которую можно легко встроить в вебсайт. Пример сборки:
$ git clone git://github.com/mozilla/pdf.js.git
$ cd pdf.js
$ brew install npm
$ npm install -g gulp-cli
$ npm install
$ gulp minified
Результатом будет «скомпилированая» версия pdf.js в папке build/minified, который копируем в наш проект. Установим стартовый URL на локальный файл minified/web/viewer.html
auto url = QUrl::fromLocalFile(app_path+"/minified/web/viewer.html");
Соберём и запустим:
Работает отлично, подход правильный, но показывает PDF файл по умолчанию. Как можно передать имя файла в среду javascript? Для этого у Qt есть другой отличный модуль QWebChannel. Идея в том, что на стороне C++/Qt создаётся QWebChannel объект и он устанавливается каналом(channel) для загружаемой веб страницы. С этим каналом мы можем зарегистрировать объекты которые будут доступны уже внутри JavaScript кода. Из JavaScript будут видны Q_PROPERTY свойства:
auto url = QUrl::fromLocalFile(app_path+"/minified/web/viewer.html");
m_communicator = new Communicator(this);
m_communicator->setUrl(pdf_path);
m_webView = new QWebEngineView(this);
QWebChannel * channel = new QWebChannel(this);
channel->registerObject(QStringLiteral("communicator"), m_communicator);
m_webView->page()->setWebChannel(channel);
m_webView->load(url);
setCentralWidget(m_webView);
Приведённый код позволяет получить доступ к объекту communicator из JavaScript. Теперь необходимо внести изменения в viewer.html/viewer.js, добавить стандартный qwebchannel.js чтобы заработала коммуникация — довольно просто:
Для viewer.html просто добавим включение qwebchannel.js
Для viewer.js добавим инициализацию QWebChannel и получим из канала имя файла который будет использоваться вместо файла по-умолчанию:
Код:
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
new QWebChannel(qt.webChannelTransport
,function(channel) {
var comm = channel.objects.communicator;
DEFAULT_URL = comm.url;
...
Вот как это работает: Перед загрузкой страницы, прикрепляется web channel и регистрируется communicator объект. Потом, когда viewer.html грузится в первый раз, определяется QWebChannel JS класс. После определения DEFAULT_URL создается JS QWebChannel объект и как только коммуникация установлена, будет вызвана прикреплённая js функция которая читает URL из объекта communicator. Новый URL и будет пользоваться вместо файла примера. Таким же образом можно передать отрендериную страницу как изображение из JavaScript в C++/Qt часть приложения.
Закончив изменения в PDF.js просто пересоберем minified:
$ gulp minified
Скопируем minified версию в папку проекта. Доделаем получение списка файлов из аргументов командной строки итд.
Готово, рабочее десктопное приложение PDF viewer за пару часов.
GitHub: https://github.com/yshurik/qpdfjs
Автор: yshurik