Введение
Как и большинства разработчиков Qt, использующих библиотеку OpenCV, меня заинтересовала тема представления изображения, полученного из web-камеры, как компонент визуального проектирования интерфейса для Qt Designer.
Перерыв кучу информации в сети, заметил, что большинство статей повторяют друг друга, и найти «изюминку» слишком трудно. Думаю, мой обыт создания визуальной компоненты по представлению изобравжения библиотеки OpenCV для Qt Designer будет полезен. К тому же, поделюсь информацией, как разделить библиотеки времени проектирования дизайна и времени выполнения кода. Подобный подход весьма удачно зарекомендовал себя в RAD Delphi для операционных систем семейства Windows.
Замечания
- Автор coffeesmoke не «разжёвывает» простые истины, методы и тонкости. Код понятен. Функции не превышают 20 строк.
- Проект и пример использования реализован на Qt Creator для ОС Linux. Разработчики Windows-приложений должны установить настройки в файлох .pro соответственно своей ОС
- Автор не пускается в дискуссии типа «а так было бы лучше». Нашли оптимальное решение, реализуйте.
- Автор не отвечает на вопросы настроек Qt Creator и/или Qt Designer. Поисковые системы вам в помощь!
- Пост несёт ознакомительный характер и служит обозначению направления движения ваших мыслей. Куда они пойдут, вам виднее.
Проектирование и выполнение
Выполнение
Оставим ненадолго библиотеку OpenCV работы с изображениями. Наша задача — универсальная runtime-библиотека (библиотека времени исполнения кода).
Возможно, то, что я сейчас покажу используется повсеместно. Однако, не встретил я на просторах сети по тематике построения плагинов под Qt Designer подобного подхода.
Задача состоит в том, чтобы не «тащить» библиотеку плагина Qt Designera в проект, а обойтись библиотекой времени исполнения. Очевидно, что runtime-библиотеку можно «обрамить» виджетом представления компонента на палитре Qt Dessigner
Дизайнер содержит простой компонент QLabel, способный передавать изображение свойством pixmap. Для наших целей этого достаточно. Обратимся к переводу документации Добавление модулей Qt Designer
и самому первоисточнику информации по ссылке Using Custom Widgets with Qt Designer.Самое полезное — информация о размещении пользовательских библиотек-плагинов! теперь нам известен путь назначения: $$QTDIR/plugin/designer
Оставим обвёртку нового компонента в стороне.Займёмся непосредственно компонентом runtime-библиотеки. Создадим динамически подгружаемую библиотеку с классом CQtOpenCVImage нашего нового виджета.
Заголовочный файл cqtopencvimage.h
#ifndef QTOPENCVIMAGE_H #define QTOPENCVIMAGE_H
#include <QtDesigner/QDesignerExportWidget>
#include <QWidget> #include <QUrl> #include <QLabel> #include "opencv2/opencv.hpp" #include <QScopedPointer> /*----------------------------------------------------------------------------*/
class CQtOpenCVImagePrivate;
CQtOpenCVImage/*----------------------------------------------------------------------------*/ class #if defined(QDESIGNER_EXPORT_WIDGETS) QDESIGNER_WIDGET_EXPORT #else Q_DECL_EXPORT #endif
: public QWidget { Q_OBJECT Q_PROPERTY(QUrl capture READ getCapture WRITE slot_setCapture) Q_PROPERTY(QString text READ getLabelText WRITE slot_setLabelText) Q_PROPERTY(QPixmap pixmap READ getPixmap WRITE slot_setPixmap) Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE slot_setScaledContents) Q_PROPERTY(bool grayscale READ isGrayscale WRITE slot_setGrayscale) Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment) public: explicit CQtOpenCVImage(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~CQtOpenCVImage(); QUrl& getCapture (); QString getLabelText () const; const QPixmap* getPixmap () const; const QImage* getImage () const; Qt::Alignment getAlignment() const; void setAlignment(Qt::Alignment); const QLabel* getQLabel () const; bool hasScaledContents() const; bool isGrayscale() const; public Q_SLOTS: void slot_setCapture ( const QUrl& ); void slot_setLabelText ( const QString& ); void slot_setPixmap ( const QPixmap& ); void slot_setImage ( const QImage& ); void slot_setQLabel ( const QLabel& ); void slot_setGrayscale(bool); void slot_setScaledContents(bool); Q_SIGNALS: void signal_Grayscale(bool); void signal_ImageChanged(); void signal_CaptureChanged(); void signal_PixmapChanged(); void signal_LabelChanged(); void signal_AlignmentChanged(); private: Q_DISABLE_COPY(CQtOpenCVImage) Q_DECLARE_PRIVATE(CQtOpenCVImage)
QScopedPointer d_ptr;
}; /*----------------------------------------------------------------------------*/ #endif // QTOPENCVIMAGE_H
Обратите внимание на участки, выделенные специальным шрифтом
- Включение заголовочного файла QtDesigner/QDesignerExportWidget гарантирует подключение необходимого макроса QDESIGNER_WIDGET_EXPORT при использовании в файле проекта директивы
DEFINES += QDESIGNER_EXPORT_WIDGETS - Класс CQtOpenCVImagePrivate скрывает внутренние переменные и указатели из объявления основного класса CQtOpenCVImage. Мне кажется это правильным: не следует перегружать объявления ненужной информацией;
- Объявление единственной приватной переменной-указателя
на структуру внутренней реализации алгоритмов и данных в рамках правил Qt. ИМХО, «хороший тон».QScopedPointer<CQtOpenCVImagePrivate> d_ptr;
Наш класс CQtOpenCVImage предоставляет для Qt Designer свойства (Q_PROPERTY) визуального управления изображением. Методы записи свойст реализованы слотами void slot_**** (***).
И так, наследуемый от QWidget класс представления изображения, полученного из web(IP)-камеры при вызуальном проектировании даёт доступ к свойствам:
- capture — URL устройства видеозахвата или номер web-камеры;
- text — текстовая надпись. Аналог свойства text QLabel;
- pixmap — указатель на объект типа QPixmap. Аналог свойства pixmap QLabel;
- scaledContents — масштабируемость изображения. Аналог свойства scaledContents QLabel;
- grayscale — булевый переключатель цветового режима вывода изображения, где значение true соответствует градациям серого, а false — цветное (RGB);
- alignment — выравнивание содержимого text и pixmap. Аналог свойства alignment QLabel;
Думаю, назначения методов класса описывать не имеет смысла: название говорят сами за себя.
Перейдём к коду реализации классов (файл cqtopencvimage.cpp).
Рассмотрим скрытый класс CQtOpenCVImagePrivate, его атрибуты и методы.
class CQtOpenCVImagePrivate { Q_DECLARE_PUBLIC(CQtOpenCVImage) public: CQtOpenCVImagePrivate(CQtOpenCVImage* owner); virtual ~CQtOpenCVImagePrivate(); CQtOpenCVImage* q_ptr; // указатель на экземпляр основного класса QGridLayout* f_grid_layout; // сетка выравнивания находящихся в ней компонент по всему периметру QLabel* f_label;// экземпляр типа QLabel для вывода изображения QUrl f_capture_path;// URL устройства видеозахвата или его номер QImage* p_qt_image; // Указател на экземпляр изображения типа QImage CvCapture* p_capture; // Указател на экземпляр устройства видеозахвата в рамках описания OpenCV IplImage* p_opencv_frame; // Указател на текущий фрем из p_capture uint f_grayscaled:1; // признак представления изобравжения в градациях серого void init (); // инициализация данных void close (); // корекктное закрытие всех указателей void free_qt_image (); // освобождение памяти экземпляра p_qt_image void new_qt_image ();// распределение памяти для экземпляра p_qt_image void free_capture (); // освобождение памяти экземпляра p_capture void new_capture (); // распределение памяти для экземпляра p_capture };
Перейдём к реализации методов.
/*----------------------------------------------------------------------------*/ void CQtOpenCVImagePrivate::init () { Q_ASSERT(q_ptr); f_grid_layout = new QGridLayout(q_ptr); Q_ASSERT(f_grid_layout); f_label = new QLabel(q_ptr/*, Qt::WindowNoState*/); Q_ASSERT(f_label); f_grid_layout->addWidget (f_label); p_qt_image = 0, p_capture = 0, p_opencv_frame = 0, f_grayscaled = 0; } /*----------------------------------------------------------------------------*/ inline void CQtOpenCVImagePrivate::close () { free_qt_image (); free_capture (); } /*----------------------------------------------------------------------------*/ CQtOpenCVImagePrivate::CQtOpenCVImagePrivate(CQtOpenCVImage* owner) : q_ptr(owner) { init (); } /*----------------------------------------------------------------------------*/ CQtOpenCVImagePrivate::~CQtOpenCVImagePrivate () { close (); if(!(f_label->parent ())) delete f_label; if(!(f_grid_layout->parent ())) delete f_grid_layout; } /*----------------------------------------------------------------------------*/ inline void CQtOpenCVImagePrivate::free_qt_image () { if(p_qt_image) { delete p_qt_image; p_qt_image = 0; } } /*----------------------------------------------------------------------------*/ inline void CQtOpenCVImagePrivate::free_capture () { if(p_capture) { cvReleaseCapture(&p_capture); p_capture = 0; } }
Код понятен и читаем. Вопросов не должно возникнуть. Параметр CQtOpenCVImage* owner конструтора представляет указател на объект CQtOpenCVImage, содержащий экземпляр скрытого класса как переменную CQtOpenCVImage::d_ptr.
Как говорил доктор, герой Леонида Броневого, из фильма «Формула любви», — «Так, я продолжу...».
Рассмотрим определение устройства захвата методом new_capture
/*----------------------------------------------------------------------------*/ void CQtOpenCVImagePrivate::new_capture () { free_capture ();//освобождение устройства захвата bool b_ok; int i_index = f_capture_path.toString ().toInt (&b_ok); // определение номера камеры, если это номер, а не URL /* информация к размышлению: перезахват устройства, если камера реализована как CGI для вебсервера, возвращающий по одному кадру if(b_ok) { p_capture = cvCreateCameraCapture(i_index); if(p_capture) if(!p_opencv_frame) p_opencv_frame = cvQueryFrame (p_capture); } else { while((p_capture =cvCaptureFromFile(f_capture_path.toString ().toStdString ().c_str ()))) { p_opencv_frame = cvQueryFrame (p_capture); new_qt_image (); cvWaitKey (1000); } } */ // да, ну его, этот перезахват. Перезахватим по запросу методами OpenCV. p_capture = b_ok ? cvCreateCameraCapture(i_index) : cvCaptureFromFile(f_capture_path.toString ().toStdString ().c_str ()); p_opencv_frame = p_capture ? cvQueryFrame (p_capture) : 0; // получение фрейма методом из OpenCV new_qt_image (); // формирование экземпляра QImage }
Для подробного знакомства с OpenCV читайте Оглавление
Формирование изображения типа QImage:
/*----------------------------------------------------------------------------*/ void CQtOpenCVImagePrivate::new_qt_image () { if(!p_capture) return; free_qt_image (); // освободим указатель на изображение if(p_opencv_frame) { // создадим OpenCV экземпляр изображения согласно параметру оттенка серого IplImage *_tmp = f_grayscaled ? cvCreateImage( cvSize( p_opencv_frame->width, p_opencv_frame->height ), IPL_DEPTH_8U, 1 ) : cvCloneImage (p_opencv_frame) ; try { // пребразование цвета в RGB или оттенки серого cvCvtColor( p_opencv_frame, _tmp, f_grayscaled ? CV_RGB2GRAY : CV_BGR2RGB ); // формирование изображения типа QImage p_qt_image = new QImage( (const uchar*)(_tmp->imageData), _tmp->width, _tmp->height, _tmp->widthStep, (f_grayscaled ? QImage::Format_Indexed8 : QImage::Format_RGB888) ); emit q_ptr->signal_ImageChanged (); // оповещение о готовности q_ptr->slot_setPixmap (QPixmap::fromImage (*p_qt_image)); // отрисовать изображение на QLabel } catch(...) { // упс... Дрова -- в исходное, пельмени разлепить! close (); } // не забываем прибрать за собой! cvReleaseImage(&_tmp); } }
Думаю, с подводной частью айсберга всё понятно.
Реализация основного класса ещё проще. Даже, лень объяснять. Смотрите сами:
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ CQtOpenCVImage::CQtOpenCVImage(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f), d_ptr(new CQtOpenCVImagePrivate(this)) { } /*----------------------------------------------------------------------------*/ CQtOpenCVImage::~CQtOpenCVImage() { } /*----------------------------------------------------------------------------*/ QUrl& CQtOpenCVImage::getCapture () { return (d_func ()->f_capture_path); } /*----------------------------------------------------------------------------*/ QString CQtOpenCVImage::getLabelText () const { return (d_func ()->f_label->text ()); } /*----------------------------------------------------------------------------*/ bool CQtOpenCVImage::hasScaledContents() const { return d_func ()->f_label->hasScaledContents (); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setScaledContents(bool Value ) { d_func ()->f_label->setScaledContents (Value); } /*----------------------------------------------------------------------------*/ bool CQtOpenCVImage::isGrayscale() const { return d_func ()->f_grayscaled; } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setGrayscale( bool Value ) { if(d_func ()->f_grayscaled != Value) { d_func ()->f_grayscaled = Value; if(!(d_func ()->p_capture)) d_func ()->new_capture (); else d_func ()->new_qt_image (); emit signal_Grayscale (d_func ()->f_grayscaled); } } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setLabelText ( const QString& Value ) { d_func ()->f_label->setText (Value); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setCapture ( const QUrl& Value ) { // Раскомментировать при необходимости исключения переинициализации захвата // if(getCapture ().toString () != Value.toString () || !d_func ()->p_opencv_frame) // { d_func ()->f_capture_path = Value.toString ().trimmed (); d_func ()->new_capture (); emit signal_CaptureChanged (); // } } /*----------------------------------------------------------------------------*/ const QPixmap* CQtOpenCVImage::getPixmap () const { return ((const QPixmap*)(d_func ()->f_label->pixmap ())); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setPixmap ( const QPixmap& Value ) { d_func ()->f_label->setPixmap (Value); emit signal_PixmapChanged (); } /*----------------------------------------------------------------------------*/ const QImage* CQtOpenCVImage::getImage () const { return(d_func ()->p_qt_image); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setImage ( const QImage& Value ) { d_func ()->free_qt_image (); d_func ()->p_qt_image = new QImage(Value); slot_setPixmap (QPixmap::fromImage (*(d_func ()->p_qt_image))); emit signal_ImageChanged (); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::slot_setQLabel ( const QLabel& Value) { d_func ()->f_label->setText (Value.text ()); emit signal_LabelChanged (); } /*----------------------------------------------------------------------------*/ Qt::Alignment CQtOpenCVImage::getAlignment() const { return(d_func ()->f_label->alignment ()); } /*----------------------------------------------------------------------------*/ void CQtOpenCVImage::setAlignment(Qt::Alignment Value) { d_func ()->f_label->setAlignment (Value); emit signal_AlignmentChanged (); } /*----------------------------------------------------------------------------*/ const QLabel* CQtOpenCVImage::getQLabel () const { return ((const QLabel*)(d_func ()->f_label)); }
Формируем runtime-библиотеку. Опишем файл проекта images.pro:
TARGET = QtOpenCVImages TARGET = $$qtLibraryTarget($$TARGET) TEMPLATE = lib CONFIG += debug_and_release
Не забываем включить:
DEFINES += QDESIGNER_EXPORT_WIDGETS
Определим путь, куда попадут файлы библиотек:
unix:!symbian { target.path = $$PWD/../../../../lib DESTDIR = $$PWD/../../../../lib INSTALLS += target }
Добавим файлы класса:
SOURCES += \ cqtopencvimage.cpp HEADERS += \ cqtopencvimage.h
Определим пути заголовков файлов библиотек OpenCV и пути зависимостей проекта:
INCLUDEPATH += /usr/include/opencv2 DEPENDPATH += /usr/include/opencv2
Добавим библиотеку OpenCV (core и highgui) к нашему проекту:
#win32:CONFIG(release): LIBS += -L/usr/lib/ -lopencv_core #else:win32:CONFIG(debug, debug|release): LIBS += -L/usr/lib/ -lopencv_cored #else: unix: LIBS += -L/usr/lib/ -lopencv_core #win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../../../../usr/lib/release/ -lopencv_highgui #else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../../../../usr/lib/debug/ -lopencv_highguid #win32:CONFIG(release): LIBS += -L/usr/lib/release/ -lopencv_highgui #else:symbian: LIBS += -lopencv_highgui #else: unix: LIBS += -L/usr/lib/ -lopencv_highgui
Уважаемые разработчики ОС Windows, я «соскочил» с иглы Microsoft давно и прошу прощения, что закомментировал пути библиотек для вашей ОС. Вы — народ неглупый, разберётесь.
*NIX-оиды, используйте команду ln -s, чтобы создать ссылки на ваши библиотеки из места расположения в каталог /usr/libОдин раз создав ссылку, просто, пересобирайте проект, и всё будет работать нормально!
Итак, мы создали динамическую библиотеку Qt визуального компонента отображения рисунка, полученного из web-камеры средствами OpenCV.
Исходный код: Wu a La (в прямом смысле)
Проектирование
Настало время подключить нашу runtime-библиотеку к оболочке класса-плагина для Qt Designer.
Создадим проект библиотеки-плагина:
TARGET = QtOpenCVWidgets
TARGET = $$qtLibraryTarget($$TARGET)
TEMPLATE = lib
CONFIG += designer plugin release
DEFINES += QDESIGNER_EXPORT_WIDGETS
SOURCES += \
qtopencvimageplugin.cpp \
../qtopencvwidgets.cpp
HEADERS +=\
qtopencvimageplugin.h \
../qtopencvwidgets.h \
../runtime/images/cqtopencvimage.h
RESOURCES += \
qtopencvimages.qrc
unix:!symbian {
target.path = $$PWD/../../../lib
DESTDIR = $$PWD/../../../lib
INSTALLS += target
}
INCLUDEPATH += /usr/include/opencv2
DEPENDPATH += /usr/include/opencv2
#win32:CONFIG(release, debug|release): LIBS += -L/usr/lib/ -lQtOpenCVImages
#else:win32:CONFIG(debug, debug|release): LIBS += -L/usr/lib -lQtOpenCVImages
#else:symbian: LIBS += -lQtOpenCVImages
#else:
unix: LIBS += -L/usr/lib -lQtOpenCVImages
INCLUDEPATH += $$PWD/../runtime/images
DEPENDPATH += $$PWD/../runtime/images
Похож на проект Runtime, не так ли? Рассмотрм некоторые отличия:
- Изменилось название библиотеки: TARGET = QtOpenCVWidgets;
- Параметр CONFIG += designer plugin release содержит тэги plugin и designer;
- Плагин реализован в двух файлах по одному на заголовок и реализацию qtopencvimageplugin.* qtopencvwidgets.*<.i>;
Всё так же присутствует зависимость от заголовков библиотеки OpenCV т.к. добавилась зависимость от включённого заголовка cqtopencvimage.h нашего виджета QtOpenCVImages;
Включена динамическая runtime библиотека libQtOpenCVImages по символьной ссылке из /usr/lib/libQtOpenCVImages.so, указывающей на её реальное расположение в каталоге проекта
Создаём виджет-плагин Qt Designer по всем канонам Qt: Creating Custom Widgets for Qt Designer (надо же,coffeesmoke, как громко сказано!):
qtopencvimageplugin.h
#ifndef QTOPENCVIMAGEPLUGIN_H
#define QTOPENCVIMAGEPLUGIN_H
#include <QObject>
#include <QDesignerCustomWidgetInterface>
class QtOpenCVImagePlugin : public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
explicit QtOpenCVImagePlugin(QObject *parent = 0);
QString name() const;
QString includeFile() const;
QString group() const;
QIcon icon() const;
QString toolTip() const;
QString whatsThis() const;
bool isContainer() const;
QWidget* createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);
bool isInitialized() const;
QString domXml() const;
signals:
public slots:
private:
bool f_init;
};
#endif // QTOPENCVIMAGEPLUGIN_H
Основная идея: объявить как Q_INTERFACE, наследуясь от QDesignerCustomWidgetInterface и перегрузить методы класса-интерфеса дизайнера согласно своим требованиям.
qtopencvimageplugin.cpp
#include "qtopencvimageplugin.h"
#include "cqtopencvimage.h"
QtOpenCVImagePlugin::QtOpenCVImagePlugin(QObject *parent) :
QObject(parent),
f_init(false)
{
}
QString
QtOpenCVImagePlugin::name() const
{
return "CQtOpenCVImage";// имя класса виджета
}
QString
QtOpenCVImagePlugin::includeFile() const
{
return QLatin1String("cqtopencvimage.h"); // название включения в файл ui_*.h новой формы
}
QString
QtOpenCVImagePlugin::group() const
{
return tr("OpenCV Widgets"); // название группы отображения на панели компонентов Qt Designer
}
QIcon
QtOpenCVImagePlugin::icon() const
{
return QIcon(":QtOpenCVLogo.png"); // значок виджета
}
QString
QtOpenCVImagePlugin::toolTip() const
{
return QString();
}
QString
QtOpenCVImagePlugin::whatsThis() const
{
return QString();
}
bool
QtOpenCVImagePlugin::isContainer() const
{
return false;
}
QWidget*
QtOpenCVImagePlugin::createWidget(QWidget *parent)
{
return new CQtOpenCVImage(parent); // вот она, реализация экземпляра класса нашего виджета!
}
void
QtOpenCVImagePlugin::initialize(QDesignerFormEditorInterface *core)
{
// установка признака инициализации при помещении на форму
if (f_init) return;
f_init = true;
}
bool
QtOpenCVImagePlugin::isInitialized() const
{
return f_init;
}
QString
QtOpenCVImagePlugin::domXml() const
{
// основные параметры для дизайнера
return "<ui language=\"c++\">\n"
" <widget class=\"CQtOpenCVImage\" name=\"QtOpenCVImage\">\n"
" <property name=\"geometry\">\n"
" <rect>\n"
" <x>0</x>\n"
" <y>0</y>\n"
" <width>400</width>\n"
" <height>200</height>\n"
" </rect>\n"
" </property>\n"
" </widget>\n"
"</ui>";
}
TARGET = QtOpenCVWidgets
TARGET = $$qtLibraryTarget($$TARGET)
TEMPLATE = lib
CONFIG += designer plugin release
DEFINES += QDESIGNER_EXPORT_WIDGETS
SOURCES += \
qtopencvimageplugin.cpp \
../qtopencvwidgets.cpp
HEADERS +=\
qtopencvimageplugin.h \
../qtopencvwidgets.h \
../runtime/images/cqtopencvimage.h
RESOURCES += \
qtopencvimages.qrc
unix:!symbian {
target.path = $$PWD/../../../lib
DESTDIR = $$PWD/../../../lib
INSTALLS += target
}
INCLUDEPATH += /usr/include/opencv2
DEPENDPATH += /usr/include/opencv2
#win32:CONFIG(release, debug|release): LIBS += -L/usr/lib/ -lQtOpenCVImages
#else:win32:CONFIG(debug, debug|release): LIBS += -L/usr/lib -lQtOpenCVImages
#else:symbian: LIBS += -lQtOpenCVImages
#else:
unix: LIBS += -L/usr/lib -lQtOpenCVImages
INCLUDEPATH += $$PWD/../runtime/images
DEPENDPATH += $$PWD/../runtime/images
Всё так же присутствует зависимость от заголовков библиотеки OpenCV т.к. добавилась зависимость от включённого заголовка cqtopencvimage.h нашего виджета QtOpenCVImages;
Включена динамическая runtime библиотека libQtOpenCVImages по символьной ссылке из
/usr/lib/libQtOpenCVImages.so, указывающей на её реальное расположение в каталоге проекта#ifndef QTOPENCVIMAGEPLUGIN_H
#define QTOPENCVIMAGEPLUGIN_H
#include <QObject>
#include <QDesignerCustomWidgetInterface>
class QtOpenCVImagePlugin : public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
explicit QtOpenCVImagePlugin(QObject *parent = 0);
QString name() const;
QString includeFile() const;
QString group() const;
QIcon icon() const;
QString toolTip() const;
QString whatsThis() const;
bool isContainer() const;
QWidget* createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);
bool isInitialized() const;
QString domXml() const;
signals:
public slots:
private:
bool f_init;
};
#endif // QTOPENCVIMAGEPLUGIN_H
#include "qtopencvimageplugin.h"
#include "cqtopencvimage.h"
QtOpenCVImagePlugin::QtOpenCVImagePlugin(QObject *parent) :
QObject(parent),
f_init(false)
{
}
QString
QtOpenCVImagePlugin::name() const
{
return "CQtOpenCVImage";// имя класса виджета
}
QString
QtOpenCVImagePlugin::includeFile() const
{
return QLatin1String("cqtopencvimage.h"); // название включения в файл ui_*.h новой формы
}
QString
QtOpenCVImagePlugin::group() const
{
return tr("OpenCV Widgets"); // название группы отображения на панели компонентов Qt Designer
}
QIcon
QtOpenCVImagePlugin::icon() const
{
return QIcon(":QtOpenCVLogo.png"); // значок виджета
}
QString
QtOpenCVImagePlugin::toolTip() const
{
return QString();
}
QString
QtOpenCVImagePlugin::whatsThis() const
{
return QString();
}
bool
QtOpenCVImagePlugin::isContainer() const
{
return false;
}
QWidget*
QtOpenCVImagePlugin::createWidget(QWidget *parent)
{
return new CQtOpenCVImage(parent); // вот она, реализация экземпляра класса нашего виджета!
}
void
QtOpenCVImagePlugin::initialize(QDesignerFormEditorInterface *core)
{
// установка признака инициализации при помещении на форму
if (f_init) return;
f_init = true;
}
bool
QtOpenCVImagePlugin::isInitialized() const
{
return f_init;
}
QString
QtOpenCVImagePlugin::domXml() const
{
// основные параметры для дизайнера
return "<ui language=\"c++\">\n"
" <widget class=\"CQtOpenCVImage\" name=\"QtOpenCVImage\">\n"
" <property name=\"geometry\">\n"
" <rect>\n"
" <x>0</x>\n"
" <y>0</y>\n"
" <width>400</width>\n"
" <height>200</height>\n"
" </rect>\n"
" </property>\n"
" </widget>\n"
"</ui>";
}
Осталось совсем немного: создать общую обвёртку-контейнер для всех наших виджетов группы
Qt <-> OpenCVqtopencvwidgets.h
#ifndef QTOPENCVWIDGETS_H #define QTOPENCVWIDGETS_H #include <QObject> #include <QtPlugin> #include <QDesignerCustomWidgetCollectionInterface> class QtOpenCVWidgets : public QObject, public QDesignerCustomWidgetCollectionInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) public: explicit QtOpenCVWidgets(QObject *parent = 0); QList<QDesignerCustomWidgetInterface*> customWidgets() const { return f_plugins; } private: QList<QDesignerCustomWidgetInterface *> f_plugins; }; #endif // QTOPENCVWIDGETS_H
qtopencvwidgets.cpp
#include "qtopencvwidgets.h" #include "images/qtopencvimageplugin.h" QtOpenCVWidgets::QtOpenCVWidgets(QObject *parent) : QObject(parent) { f_plugins << new QtOpenCVImagePlugin(this); } //Q_DECLARE_INTERFACE(QtOpenCVWidgets, "com.trolltech.Qt.Designer.QtOpenCV") Q_EXPORT_PLUGIN2(qtopencvwidgetsplugin, QtOpenCVWidgets)
Интерес представляет конструктор, а именно:
f_plugins << new QtOpenCVImagePlugin(this);. При последующем добавлении новых компонентов в коллекцию достаточно будет переписать конструктор, добавив ещё один оператор f_plugins << new <Очередной>Plugin(this); Применение
Открываем Qt Designer и ищем на палитре компонентов наш СQtOpenCVImage. Пмещаем его на новую форму. Меняем capture на нужный URL
192.168.0.20:8080/image.jpg. В данном случае, это Java-аплет сервера камеры, предоставляющий текущий запрашиваемый кадрПоставим «галочку» на свойстве
grayscaleИсходный код: Если скачали ранее, не переходите по ней.
Выводы
- Научились создавать библиотеку времени выполнения для реализации собственного класса на основе визуальных компонент Qt;
- Научились обрамлять runtime библиотеку в плагин Qt Designer, сделав приложения независимыми от плагина дизайнера на время выполнения;
Приложения
Простой тестовый пример
Создадим стандартное Qt GUI приложение, откроем основную форму окна, поместим с неё наш компонент, создадим немного сервисных управлений, соберём и запустим.
Меняем «Адрес...» на значение 0 (встроенная видеокамера) и смотрим на изменения изображения.
Исходный код: Если скачали ранее, не переходите по ней.
Что дальше?
- Оптимизация текущего проекта;
- Описание проверенного визуального компонента поиска границ изображения методом Кэнни (Canny): CQtOpenCVCannyWidget на основе класса CQtOpenCVCanny;
- Связь между QtOpenCVImage и QtOpenCVCanny
Всего доброго.
