Понадобилось мне резко увеличить возможности QtE5 в связи с полученным заказом по изготовлению приложения обработки данных с тепловизора. В связи с этим решил я добавить графических возможностей в QtE5. Ниже то, что получилось добавить.
Ресурсы Qt
Ресурсы — важная составляющая приложения. Отличие ресурсов Qt от общепринятых в Windows в том, что они (ресурсы и механизм их работы) кросплатформенные. Один раз скомпилировав (не важно на какой платформе) файл ресурсов, он будет корректно работать на любой системе, чего нельзя сказать о ресурсах Windows. Ресурсы в Qt могут быть задействованы автоматом, через проект, но на конечном этапе сборки нужен линкёр. Поэтому нам такой способ не подходит, нет у нас под руками С++ с линкёром. Однако, в Qt, оказывается, есть динамический способ загрузки ресурсов. Он напоминает динамическую загрузку DLL. Далее рассматриваем только динамические ресурсы.
Основная идея проста. Прочитать файл ресурсов в память, получив указатель на начало. Однако, в связи с тем, что ресурсы могут быть разные, и, главное, что их может быть много, встает вопрос, как в этом буфере памяти понять, где начинается один ресурс, или заканчивается другой. Однако, Qt всё делает за нас. Имеется универсальный механизм доступа, к ресурсам. Пример: Qpixmap.load(“:icons/exit_icon.ico”). Двоеточие в начале строки говорит о том, что файл грузится из ресурса. Таким образом, как обратится к ресурсу понятно (можно подробнее почитать в документации к Qt), а вот как его (ресурс создать и загрузить) – это проблема.
Создание ресурса. Для этого используем компилятор ресурсов rcc.exe входящий в состав Qt. Структура исходного файла ресурса описана в документации Qt. В качестве примера приведем файл описания ресурса для нашей демо программы.
Это файл: t.qrc
ICONS/exit_icon.png ICONS/about_icon.png
То есть, тут сказано: «возьми два файла которые лежат в каталоге ICONS и изготовь из них файл ресурсов». Команда для выполнения этого задания следующая:
rcc -binary t.qrc -o t.rcc
Таким образом, у нас получился файл t.rcc который содержит в сжатом виде наши два файла PNG.
Загрузка ресурса. Есть две почти одинаковых возможности загрузить файл ресурсов. Один способ чисто Qt, второй D + Qt. Разница, как будет видно далее, небольшая.
Способ 1: чистый Qt в QtE5:
QResource res = new QResource(); Bool r = res.registerResource("путьДоФайлаРесурсовRCC");
Фактически, создаем объект работы с ресурсом и просим его загрузить файл ресурсов. Если на выходе Истина (true), то ресурс загружен. Далее просто обращаемся к ресурсу через двоеточие.
Способ 2: D + Qt в QtE5:
QResource res = new QResource(); auto data = cast(ubyte[]) read("путьДоФайлаРесурсовRCC"); r = res.registerResource(cast(ubyte*)data);
Фактически, средствами D читаем файл как набор байтов в буфер, после, передав указатель на буфер функции registerResource, создаем объект работы с ресурсом. Если на выходе Истина (true), то ресурс загружен. Далее обращаемся к ресурсу через двоеточие. Из этого смешанного способа можно получить еще один вариант (который я еще не пробовал, но думаю он должен сработать). Это способ загрузки двоичных файлов в программу на D посредством «import» (Отличный пример программа Олега Бахарева “Пишем валентинку на D”) с дальнейшим получением указателя на буфер. Например так:
QResource res = new QResource(); auto mp3data = cast(ubyte[]) import("путьДоФайлаРесурсовRCC"); r = res.registerResource(cast(ubyte*)data.ptr);
QPixmap и Qbitmap
Эти классы (и плюс имеющийся уже QImage) вносят новые возможности по контекстно-зависимому способу отображения графики. На Qpixmap можно рисовать так же как и на Qwidget и Qimage. Главное, что не происходит мгновенной перерисовки экрана до момента вставки (оканчания рисования, end()). Применение нескольких painter позволяет создавать интересные эффекты. Следует обратить внимание на то, что методы в QtE5 максимально (по возможности) приближены к методам Qt. Это позволяет, рассматривая примеры по Qt в интернете, фактически повторить то же самое в QtE5 на D. Основное отличие в том, что в C++, как правило, объекты созданы на стеке, а в QtE5 только в «куче» ( heap). Типичный пример:
C++ стек: Qwidget w(NULL); w.show; C++ куча: Qwidget w = new Qwidget(NULL); w->show(); D: Qwidget w = new Qwidget(null); w.show();
Следующий пример я срисовал с интернета. Цель: получить строку, на которую вместо цвета натянута фактура (в примере PNG файл).
// Тестирование загрузки ресурсов 08/10/17 // ------------------------------ // dmd testres.d qte5.d asc1251 // // Файл ресурсов: t.qrc /**/ // Компиляция ресурсов только rcc.exe Qt: // rcc -binary t.qrc -o t.rcc // // Запуск программы: // testres qt t.rcc import std.stdio; import qte5; // Графическая библиотека QtE5 import core.runtime; // Обработка входных параметров import asc1251; import std.file; extern (C) { void onPaintWidget(CView* uk, void* ev, void* qpaint) { (*uk).runPaint(ev, qpaint); }; } class CView : QWidget { QImage im; QPoint pointer; QPixmap pm1; this() { super(null); resize(600, 250); pm1 = new QPixmap(); pm1.load(":ICONS/exit_icon.png","PNG"); setPaintEvent(&onPaintWidget, aThis()); } // ______________________________________________________________ // Перерисовать себя void runPaint(void* ev, void* qpaint) { //-> Перерисовка области QBitmap bm = new QBitmap(new QSize(pm1.width, pm1.height)); bm.setNoDelete(true); // В Linux без этого валится ... bm.fill(); QPainter qp2 = new QPainter(bm); qp2.setPen( new QPen(new QColor(QtE.GlobalColor.color1)) ); QFont fnt = new QFont(); fnt.setPointSize(160); fnt.setBold(true); qp2.setFont( fnt ); qp2.drawText(50, 150, "Привет из QtE5"); qp2.end(); pm1.setMask(bm); QPainter qp = new QPainter('+', qpaint); qp.drawPixmap(pm1, 0, 0, pm1.width, pm1.height); qp.end(); } } void help() { writeln(toCON("Запуск: testres.exe qt|d имяФайлаРесурсов.rcc")); } int main(string[] ards) { bool fDebug = true; if (1 == LoadQt(dll.QtE5Widgets, fDebug)) return 1; QApplication app = new QApplication(&Runtime.cArgs.argc, Runtime.cArgs.argv, 1); if(ards.length != 3) { help(); return 1; } QResource res = new QResource(); bool r; try { switch(ards[1]) { case "qt": r = res.registerResource(ards[2]); break; case "d": auto data = cast(ubyte[]) read(ards[2]); r = res.registerResource(cast(ubyte*)data); break; default: help(); return 1; } } catch(Throwable) { help(); return 1; } if(!r) { writeln(toCON("Файл ресурсов загружен, но он кривой ...")); return 1; } CView widget = new CView(); widget.saveThis(&widget); widget.show(); return app.exec(); } ICONS/exit_icon.png ICONS/about_icon.png
Все манипуляции делаются в методе Paint, который вызывается на событие PaintWidget. Не буду останавливаться на том, как включить данное событие. Рассмотрим то, что происходит в Paint методе. В демо он называется runPaint(…).
- Первое. Создается объект bm класса Qbitmap, по размерам объекта pm1 типа Qpixmap (строки в программе 43-45). Следует заметить, что PNG-файл (или другой файл изображения, например, JPG) следует брать больше по размеру, чем будет размер виджета.
- Заполняем (45 строка) данными по умолчанию (fill()), тем самым получив однородную маску. Это подготовительная операция. Наверное её надо вынести в конструктор … ну да ладно.
- Строки (47 – 52). Создается «рисовальщик» qp2, которому сказано, что рисовать он будет строго цветом color1 (QtE.GlobalColor.color1) в нашей битовой матрице bm. Далее просто рисуется большая жирная надпись. Фраза (52) заканчивает модификацию битовой матрицы.
- Строка (54). pm1.setMask(bm) — фактически происходит наложение битовой матрицы на изображение из PNG файла. При этом все пикселы в матрице имеющие цвет color1 становятся прозрачными. А таким цветом у нас написан тект. Получается модификация нашего графического файла лежащего в Qpixmap, таким образом, что там где была надпись, пикселы сохранились, а там где надписи не было забились фоном виджета.
- Строки (56-58) просто отображают наш Qpixmap на виджет.
Более подробно можно почитать в инете, где народ на форумах обсуждает данные моменты. Еще одно замечание: не обязательно смотреть С++ примеры. С таким же успехом можно смотреть примеры на python (PyQt5) и т.д.
Посмотрел ради интереса на Lazarus (паскаль). Интересная штука. Попробовал прикрутить к нему (к паскалю) QtE5. Работает!
Посмотрел Go. Там тоже есть работа с Qt (правда 4.7), но проработка значительно сильнее, чем у меня, хотя подход такой же. Методов там раз в 10 больше, чем у меня.
Ну а главное достоинство QtE5 в том, что новые методы и классы вполне можно добавить самому, при этом не нужно ставить кучу дополнительного софта типа Cmake, и т.д и т.п. Достаточно одного QtCreator.