Qt + OpenCV. Runtime и Widget для CvCapture (устройства видеозахвата)

в 12:56, , рубрики: c++, opencv, qt, Qt Software, runtime, widget, обработка изображений, метки: , , , ,

Введение.

Анализируя замечания предыдущего поста (Qt Designer & Runtime Qt библиотеки на службе OpenCV...), пришлось более детально проработать устройство видеозахвата библиотеки OpenCV и методы разделения библиотек runtime и виджетов.
Работа с Qt Designer удобна (я — лентяй), поэтому и возник компонент проектирования интерфейса для CvCapture. После начала работы подтянулось и «научное » тому объяснение — удобно применить нечто похожее, скажем, при проектировании интерфейса свойств или параметров приложения, использующего устройство видеозахвата.
Пока идёт скачивание архива проекта, читайте далее.

Благодарности.

Спасибо всем, кто оставил свои замечания по предыдущему посту.

Библиотека времени выполнения.

Итак, библиотека OpenCV установлена, примеры кода просмотрены. Самое время «прикрутить» устройство видеозахвата к Qt. Да не просто так, а чтобы и изображения получать, меняя устройства динамически. Да, чтобы помнило все пути к изображению, видео. Да, чтобы и номера камер переключало. Да, чтобы и… пост покороче получился.

Библиотека.

Чего проще! Наследуем новый класс библиотеки от QObject, прячем ненужные разработчику поля и методы в приватный класс как элемент коллекции QScopedPointer, монтируем сигналы оповещения и слоты-обработчики.

Получаем файл заголовка cqtopencvcaptureobject.h

#ifndef CQTOPENCVCAPTUREOBJECT_H
#define CQTOPENCVCAPTUREOBJECT_H

#include "../../../include/qtopencv_global.h"
#include <opencv2/opencv.hpp>

#include <QObject>
#include <QScopedPointer>

QT_BEGIN_HEADER

QT_BEGIN_NAMESPACE

/*----------------------------------------------------------------------------*/
/**
  internal
  class CQtOpenCVCaptureObjectPrivate
  brief Класс скрытого для стороннего разработчика объявлений свойст и реализаций
  механизмов работы с устройством видеозахвата типа CvCapture библиотеки OpenCV.
*/
class CQtOpenCVCaptureObjectPrivate;

/*----------------------------------------------------------------------------*/
/**
  class CQtOpenCVCaptureObject
  brief Класс Работы с устройством видеозахвата типа CvCapture библиотеки OpenCV.
*/
class
    #if defined(QTOPENCV_LIB_EXPORT)
    QTOPENCV_DECLARE_EXPORT
    #else
    Q_DECL_EXPORT
    #endif
CQtOpenCVCaptureObject : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("brief",    "OpenCV Capture class.")
    Q_CLASSINFO("author",   "Vladimir N. Litvinenko")
    Q_CLASSINFO("URL",      "http://www.codepaint.ru")
    Q_CLASSINFO("email",    "litvinenko.vladimir@gmail.com")
    Q_CLASSINFO("created",  "21-JUN-2012")
    Q_CLASSINFO("edited",   "06-JUL-2012")

  public:
    explicit
    CQtOpenCVCaptureObject ( QObject *parent = 0 );
    virtual
    ~CQtOpenCVCaptureObject ();

    CvCapture*  getCapture () const;
    QString     getPath () const;
    int         getNumber () const;
    int         getTimeoutInterval () const;
    IplImage*   getImage () const;
    int         getFrameCount () const;

    bool        isInternal () const;
    bool        isExternal () const;
    bool        isActive () const;
    bool        isRecap () const;

  signals:

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_Errno ( int errno, const QString& errmsg );
      brief Сигнал оповещения о возникновении ошибочной ситуации.
      param errno - номер ошибки
      param errmsg - строка сообщения
    */
    void  signal_Errno ( int, const QString& );

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_CaptureChanged ()
      brief Сигнал оповещения об изменении устройства видеозахвата
    */
    void  signal_CaptureChanged ();

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_CaptureChanged ( const CvCapture* value )
      brief Сигнал оповещения об изменении устройства видеозахвата
      param value - указател на новое устройство видеозахвата типа CvCapture
                     библиотеки OpenCV
    */
    void  signal_CaptureChanged ( const CvCapture* );

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_ImageChanged ( const IplImage* value )
      brief Сигнал оповещения об изменении указателя на изображение типа IplImage
             библиотеки OpenCV.
      param value - новый указатель на изображение типа IplImage библиотеки OpenCV.
    */
    void  signal_ImageChanged ( const IplImage* );

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_TimeoutChanged ()
      brief Сигнал оповещения об изменении таймаута запроса изображения
    */
    void  signal_TimeoutChanged ();

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_TimeoutChanged ( int value )
      brief Сигнал оповещения об изменении таймаута запроса изображения
      param value - новое значение таймаута запроса изображения
    */
    void  signal_TimeoutChanged ( int );

    /**
      fn [SIGNAL] void CQtOpenCVCaptureObject::signal_ActiveChanged ( bool value )
      brief Сигнал оповещения смены статуса активности запроса изображения от
      устройства видеозахвата по тайм-ауту.
      param value - новое значение активности процесса запроса. Значение true
                     говорит о выполнении, а false - об останове процесса запроса
                     по тайм-ауту.
    */
    void  signal_ActiveChanged ( bool );

  public slots:
    void  slot_SetNumber ( int );
    void  slot_SetPath ( const QString& );
    void  slot_SetAsInternal ( bool );
    void  slot_SetTimeout ( int );
    void  slot_captureOn ( bool );
    void  slot_Activate (bool);
    void  slot_SetRecap (bool);

  protected:
    virtual
    void timerEvent(QTimerEvent *event);

  private:
    Q_DISABLE_COPY(CQtOpenCVCaptureObject)
    Q_DECLARE_PRIVATE(CQtOpenCVCaptureObject)

    QScopedPointer<CQtOpenCVCaptureObjectPrivate> d_ptr;
};
/*----------------------------------------------------------------------------*/

QT_END_NAMESPACE

QT_END_HEADER

#endif // CQTOPENCVCAPTUREOBJECT_H

Слоты:

  • slot_SetNumber отвечает за установку номера камеры
  • slot_SetPath нацеливает устройство видеозахвата файл изображения, видео или web-CGI URL
  • slot_SetAsInternal указывает, что устройство видеозахвата — камера вашего компьютера (по-моему, лишнее. Сами решите)
  • slot_SetTimeout устанавливает время извлечения фрейма из CvCapture в миллисекундах
  • slot_captureOn включает/отключает работу CvCapture
  • slot_Activate активирует/отключает режим запроса фрейма по таймауту
  • slot_SetRecap указывает устройству видеозахвата, что перед извлечением фрейма (изображения) необходимо… пересоздаться. Нужно бывает при использовании web-интерфейса к камерам

Жизнь научила ставить комментарии. Думаю, вам всё понятно.

В файле .pro подключим библиотеки OpenCV
unix: LIBS += -L/usr/lib/ -lopencv_core -lopencv_highgui

INCLUDEPATH += /usr/include/opencv2/core
/usr/include/opencv2/highgui
DEPENDPATH += /usr/include/opencv2/core
/usr/include/opencv2/highgui

Всё. Библиотека создана. Проверим её работу.

Тестовый пример.

Создадим графический интерфейс к нашему устройству видеозахвата. Зачем графический? Всё просто: мы используем его и компоненты форматирования при построении виджета в коллекции плагинов к Qt Designer.

Не буду утруждать рисунками, вы найдёте форму в каталоге examples проекта testCaptureRuntime
Укажем видеофайл:

image

Скорость кадров можно регулировать значением таймаута, «поиграйте» с ним.

Переключим на встроенную в компьютер камеру:
image
Переключим на внешнюю IP-камеру:
Smartek Giganetix GigEVisionSDK

Библиотека работает.

Сегодня добавил в OpenCV поддержку камер Smartek Giganetix на основе их GigEVisionSDK. Возможно, скоро выйдет, но я обязательно напишу в дальнейшем, как подключать своё устройство к списку видеозахвата OpenCV.

Библиотека на основе QWidget.

Здесь всё так же несложно, как и при создании биьблиотеки времени исполнения. Только «родитель» у класса QWidget, и прячем в приватный класс экземпляр нашего runtime класса.

В .pro файл помещаем

DEFINES += QDESIGNER_EXPORT_WIDGETS

Больше нет необходимости подключать библиотеку OpenCV, она пойдёт по зависимости от runtime-библиотеки.
А, вот, её, родимую, и прикрутим:

unix: LIBS += -L/usr/lib -lQtOpenCVCapture

INCLUDEPATH += $$PWD/../../runtime/capture/QtOpenCVCapture

DEPENDPATH += /usr/lib

Обратите внимание! Путь указывает на /usr/lib. Это удобно. В дальнейшем, просто создайте символическую ссылку на вашу библиотеку в этом каталоге, и не будет проблем с линковкой!

Объявим свойства виджета для дизайнера:

...
    Q_PROPERTY(
        bool      visible
        READ      isVisible
        WRITE     slot_setVisible
               )
    Q_PROPERTY(
        bool      internal
        READ      isInternal
        WRITE     slot_SetAsInternal
               )
    Q_PROPERTY(
        QString   path
        READ      getPath
        WRITE     slot_SetPath
               )
    Q_PROPERTY(
        int       number
        READ      getNumber
        WRITE     slot_SetNumber
               )
    Q_PROPERTY(
        int       numberMax
        READ      getNumberMax
        WRITE     slot_SetNumberMax
               )
    Q_PROPERTY(
        int       timeout
        READ      getTimeoutInterval
        WRITE     slot_SetTimeout
               )
    Q_PROPERTY(
        int       timeoutMax
        READ      getTimeoutMax
        WRITE     slot_SetTimeoutMax
               )
    Q_PROPERTY(
        int       timeoutStep
        READ      getTimeoutStep
        WRITE     slot_SetTimeoutStep
               )
    Q_PROPERTY(
        bool      recapture
        READ      isRecap
        WRITE     slot_SetRecap
               )
    Q_PROPERTY(
        QString   FrameTitle
        READ      getFrameTitle
        WRITE     slot_setFrameTitle
               )
    Q_PROPERTY(
        QString   GroupBoxTitle
        READ      getGroupBoxTitle
        WRITE     slot_setGroupBoxTitle
               )
    Q_PROPERTY(
        QString   RadioNumberTitle
        READ      getRadioNumberTitle
        WRITE     slot_setRadioNumberTitle
               )
    Q_PROPERTY(
        QString   RadioPathTitle
        READ      getRadioPathTitle
        WRITE     slot_setRadioPathTitle
               )
    Q_PROPERTY(
        QString   PathTitle
        READ      getPathTitle
        WRITE     slot_setPathTitle
               )
    Q_PROPERTY(
        QString   NumberTitle
        READ      getNumberTitle
        WRITE     slot_setNumberTitle
               )
    Q_PROPERTY(
        QString   TimeOutTitle
        READ      getTimeOutTitle
        WRITE     slot_setTimeOutTitle
               )
    Q_PROPERTY(
        QString   TimeOutExtTitle
        READ      getTimeOutExtTitle
        WRITE     slot_setTimeOutExtTitle
               )
    Q_PROPERTY(
        QString   RecaptureTitle
        READ      getRecaptureTitle
        WRITE     slot_setRecaptureTitle
               )
...

Расширим набор слотов и методов для поддержки свойств:

...
   //собственные методы доступа
    bool        isVisible () const;
    QString     getFrameTitle () const;
    QString     getGroupBoxTitle () const;
    QString     getRadioNumberTitle () const;
    QString     getRadioPathTitle () const;
    QString     getPathTitle () const;
    QString     getNumberTitle () const;
    QString     getTimeOutTitle () const;
    QString     getTimeOutExtTitle () const;
    QString     getRecaptureTitle () const;
...
    //собственные слоты
    void  slot_setVisible( bool );
    void  slot_setFrameTitle( const QString& );
    void  slot_setGroupBoxTitle( const QString& );
    void  slot_setRadioNumberTitle( const QString& );
    void  slot_setRadioPathTitle( const QString& );
    void  slot_setPathTitle( const QString& );
    void  slot_setNumberTitle( const QString& );
    void  slot_setTimeOutTitle( const QString& );
    void  slot_setTimeOutExtTitle( const QString& );
    void  slot_setRecaptureTitle( const QString& );
    void  slot_SetNumberMax ( int );
    void  slot_SetTimeoutMax ( int );
    void  slot_SetTimeoutStep ( int );
...

Скопируем настройки окна из тестового примера runtime- библиотеки:

/*----------------------------------------------------------------------------*/
void
CQtOpenCVCaptureWidgetPrivate::setupUi ( QWidget* parent )
{
  p_Frame = new QFrame(parent);
  Q_ASSERT(p_Frame);
  p_Frame->setObjectName(QString::fromUtf8("p_Frame"));
  p_Frame->resize(550, 195);
  p_Frame->setMinimumSize(QSize(550, 195));
//  p_Frame->setMaximumSize(QSize(550, 195));
  p_Frame->setFrameShape(QFrame::StyledPanel);
  p_Frame->setFrameShadow(QFrame::Raised);

  p_gridLayout_Frame = new QGridLayout(p_Frame);
  Q_ASSERT(p_gridLayout_Frame);
  p_gridLayout_Frame->setObjectName(QString::fromUtf8("p_gridLayout_Frame"));

  p_groupBox_Capture = new QGroupBox(p_Frame);
  Q_ASSERT(p_groupBox_Capture);
  p_groupBox_Capture->setObjectName(QString::fromUtf8("p_groupBox_Capture"));

  QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
  sizePolicy.setHorizontalStretch(0);
  sizePolicy.setVerticalStretch(0);
  sizePolicy.setHeightForWidth(p_groupBox_Capture->sizePolicy().hasHeightForWidth());
  p_groupBox_Capture->setSizePolicy(sizePolicy);
//  p_groupBox_Capture->setMinimumSize(QSize(540, 190));
//  p_groupBox_Capture->setMaximumSize(QSize(540, 185));
  p_groupBox_Capture->setFlat(true);

  p_gridLayout_GBox = new QGridLayout(p_groupBox_Capture);
  Q_ASSERT(p_gridLayout_GBox);
  p_gridLayout_GBox->setSpacing(1);
  p_gridLayout_GBox->setContentsMargins(3, 3, 3, 3);
  p_gridLayout_GBox->setObjectName(QString::fromUtf8("p_gridLayout_GBox"));

  p_horLayout_Radio = new QHBoxLayout();
  Q_ASSERT(p_horLayout_Radio);
  p_horLayout_Radio->setSpacing(1);
  p_horLayout_Radio->setObjectName(QString::fromUtf8("p_horLayout_Radio"));

  p_radioButton_URL = new QRadioButton(p_groupBox_Capture);
  Q_ASSERT(p_radioButton_URL);
  p_radioButton_URL->setObjectName(QString::fromUtf8("p_radioButton_URL"));
  p_horLayout_Radio->addWidget(p_radioButton_URL);

  p_radioButton_Number = new QRadioButton(p_groupBox_Capture);
  Q_ASSERT(p_radioButton_Number);
  p_radioButton_Number->setObjectName(QString::fromUtf8("p_radioButton_Number"));
  p_radioButton_Number->setChecked(true);
  p_horLayout_Radio->addWidget(p_radioButton_Number);
  p_gridLayout_GBox->addLayout(p_horLayout_Radio, 0, 0, 1, 1);

  p_horLayout_PathNum = new QHBoxLayout();
  Q_ASSERT(p_horLayout_PathNum);
  p_horLayout_PathNum->setSpacing(3);
  p_horLayout_PathNum->setObjectName(QString::fromUtf8("p_horLayout_PathNum"));

  p_label_PathNum = new QLabel(p_groupBox_Capture);
  Q_ASSERT(p_label_PathNum);
  p_label_PathNum->setObjectName(QString::fromUtf8("p_label_PathNum"));
  QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Preferred);
  sizePolicy1.setHorizontalStretch(0);
  sizePolicy1.setVerticalStretch(0);
  sizePolicy1.setHeightForWidth(p_label_PathNum->sizePolicy().hasHeightForWidth());
  p_label_PathNum->setSizePolicy(sizePolicy1);
  p_label_PathNum->setMinimumSize(QSize(150, 0));
  p_label_PathNum->setLayoutDirection(Qt::LeftToRight);
  p_label_PathNum->setFrameShape(QFrame::NoFrame);
  p_label_PathNum->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
  p_horLayout_PathNum->addWidget(p_label_PathNum);

  p_spinBox_Number = new QSpinBox(p_groupBox_Capture);
  Q_ASSERT(p_spinBox_Number);
  p_spinBox_Number->setObjectName(QString::fromUtf8("p_spinBox_Number"));
  sizePolicy.setHeightForWidth(p_spinBox_Number->sizePolicy().hasHeightForWidth());
  p_spinBox_Number->setSizePolicy(sizePolicy);
  p_spinBox_Number->setMinimum(0);
  p_spinBox_Number->setMaximum(999999);
  p_horLayout_PathNum->addWidget(p_spinBox_Number);

  p_lineEdit = new QLineEdit(p_groupBox_Capture);
  Q_ASSERT(p_lineEdit);
  p_lineEdit->setObjectName(QString::fromUtf8("p_lineEdit"));
  p_lineEdit->setEnabled(true);
  p_horLayout_PathNum->addWidget(p_lineEdit);
  p_gridLayout_GBox->addLayout(p_horLayout_PathNum, 1, 0, 1, 1);

  p_horLayout_TOut = new QHBoxLayout();
  Q_ASSERT(p_horLayout_TOut);
  p_horLayout_TOut->setSpacing(3);
  p_horLayout_TOut->setObjectName(QString::fromUtf8("p_horLayout_TOut"));

  p_label_TOut = new QLabel(p_groupBox_Capture);
  Q_ASSERT(p_label_TOut);
  p_label_TOut->setObjectName(QString::fromUtf8("p_label_TOut"));
  sizePolicy1.setHeightForWidth(p_label_TOut->sizePolicy().hasHeightForWidth());
  p_label_TOut->setSizePolicy(sizePolicy1);
  p_label_TOut->setMinimumSize(QSize(150, 0));
  p_horLayout_TOut->addWidget(p_label_TOut);

  p_spinBox_TOut = new QSpinBox(p_groupBox_Capture);
  Q_ASSERT(p_spinBox_TOut);
  p_spinBox_TOut->setObjectName(QString::fromUtf8("p_spinBox_TOut"));
  sizePolicy.setHeightForWidth(p_spinBox_TOut->sizePolicy().hasHeightForWidth());
  p_spinBox_TOut->setSizePolicy(sizePolicy);
  p_spinBox_TOut->setMinimum(0);
  p_spinBox_TOut->setMaximum(999999);
  p_horLayout_TOut->addWidget(p_spinBox_TOut);

  p_label_ms = new QLabel(p_groupBox_Capture);
  Q_ASSERT(p_label_ms);
  p_label_ms->setObjectName(QString::fromUtf8("p_label_ms"));
  sizePolicy1.setHeightForWidth(p_label_ms->sizePolicy().hasHeightForWidth());
  p_label_ms->setSizePolicy(sizePolicy1);
  p_horLayout_TOut->addWidget(p_label_ms);

  p_horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Preferred, QSizePolicy::Minimum);
  Q_ASSERT(p_horizontalSpacer);
  p_horLayout_TOut->addItem(p_horizontalSpacer);
  p_gridLayout_GBox->addLayout(p_horLayout_TOut, 2, 0, 1, 1);

  p_horLayout_Buttons = new QHBoxLayout();
  Q_ASSERT(p_horLayout_Buttons);
  p_horLayout_Buttons->setSpacing(3);
  p_horLayout_Buttons->setObjectName(QString::fromUtf8("p_horLayout_Buttons"));

  p_checkBox_Recap = new QCheckBox(p_groupBox_Capture);
  Q_ASSERT(p_checkBox_Recap);
  p_checkBox_Recap->setObjectName(QString::fromUtf8("p_checkBox_Recap"));
  sizePolicy.setHeightForWidth(p_checkBox_Recap->sizePolicy().hasHeightForWidth());
  p_checkBox_Recap->setSizePolicy(sizePolicy);
  p_horLayout_Buttons->addWidget(p_checkBox_Recap);

  p_pushButton_Test = new QPushButton(p_groupBox_Capture);
  Q_ASSERT(p_pushButton_Test);
  p_pushButton_Test->setObjectName(QString::fromUtf8("p_pushButton_Test"));
  p_pushButton_Test->setCheckable(true);
  p_pushButton_Test->setChecked(false);

  p_horLayout_Buttons->addWidget(p_pushButton_Test);
  p_gridLayout_GBox->addLayout(p_horLayout_Buttons, 3, 0, 1, 1);
  p_gridLayout_Frame->addWidget(p_groupBox_Capture, 0, 0, 1, 1);

#ifndef QT_NO_SHORTCUT
  p_label_PathNum->setBuddy(p_lineEdit);
#endif // QT_NO_SHORTCUT

  retranslateUi();

  QMetaObject::connectSlotsByName(p_Frame);
}

Подключим сигналы и слоты:

/*----------------------------------------------------------------------------*/
void
CQtOpenCVCaptureWidgetPrivate::setupConnections ()
{
  // перенаправление сигналов runtime-объекта на выход виджета
  q_obj->connect (q_obj, SIGNAL(signal_Errno(int,QString)), q_ptr, SIGNAL(signal_Errno(int,QString)));
  q_obj->connect (q_obj, SIGNAL(signal_CaptureChanged()), q_ptr, SIGNAL(signal_CaptureChanged()));
  q_obj->connect (q_obj, SIGNAL(signal_CaptureChanged(const CvCapture*)), q_ptr, SIGNAL(signal_CaptureChanged(const CvCapture*)));
  q_obj->connect (q_obj, SIGNAL(signal_ImageChanged(const IplImage*)), q_ptr, SIGNAL(signal_ImageChanged(const IplImage*)));
  q_obj->connect (q_obj, SIGNAL(signal_TimeoutChanged()), q_ptr, SIGNAL(signal_TimeoutChanged()));
  q_obj->connect (q_obj, SIGNAL(signal_TimeoutChanged(int)), q_ptr, SIGNAL(signal_TimeoutChanged(int)));
  q_obj->connect (q_obj, SIGNAL(signal_ActiveChanged(bool)), q_ptr, SIGNAL(signal_ActiveChanged(bool)));

  // установка обработки переключения устройств получения видеоинформации
  q_ptr->connect (p_radioButton_Number,SIGNAL(toggled(bool)), q_ptr, SLOT(slot_SetAsInternal(bool)));
  q_ptr->connect (p_radioButton_URL, SIGNAL(toggled(bool)), q_ptr, SLOT(slot_SetAsExternal(bool)));

  // обработка редактирования и ввода строки пути устроцства видеозахвата
  q_ptr->connect (p_lineEdit, SIGNAL(textChanged(QString)), q_ptr, SLOT(slot_SetPath(QString)));
  q_ptr->connect (p_lineEdit, SIGNAL(returnPressed()), q_ptr, SLOT(slot_PathComplete()));

  // установка обработки изменения номера встроенной вебкамеры
  q_ptr->connect (p_spinBox_Number, SIGNAL(valueChanged(int)), q_ptr, SLOT(slot_SetNumber(int)));

  // установка обработки изменения тайм-аута опроса устройства
  q_ptr->connect (p_spinBox_TOut, SIGNAL(valueChanged(int)), q_ptr, SLOT(slot_SetTimeout(int)));

  // установка обработки изменения включения реинициализации устройства видеозахвата в каждом тайм-ауте
  q_ptr->connect (p_checkBox_Recap, SIGNAL(toggled(bool)), q_ptr, SLOT(slot_SetRecap(bool)));

  // установка обработки сигнала к тестированию
  q_ptr->connect (p_pushButton_Test, SIGNAL(toggled(bool)),  q_ptr, SLOT(slot_Test(bool)));

//  q_ptr->connect (q_ptr, SIGNAL(signal_ImageChanged(const IplImage*)), q_ptr, SLOT(slot_ShowImage(const IplImage*)));
}

/*----------------------------------------------------------------------------*/

Собираем библиотеку на основе виджета.

Плагин к Дизайнеру.

Настало время подключить нашу QWidget-библиотеку к Qt Designer.
Создадим проект-коллекцию.

CONFIG      += designer plugin debug_and_release
TARGET      = $$qtLibraryTarget(CQtOpenCVCollection)
TEMPLATE    = lib

DEFINES += QDESIGNER_EXPORT_WIDGETS

HEADERS     = cqtopencvcapturewidgetplugin.h  cqtopencvcollection.h
SOURCES     = cqtopencvcapturewidgetplugin.cpp cqtopencvcollection.cpp
RESOURCES   = icons.qrc

//target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS    += target

#include(cqtopencvimagewidget.pri)
include(cqtopencvcapturewidget.pri)
#include(cqtopencvcannywidget.pri)

unix: LIBS += -L/usr/lib/ -lQtOpenCVCaptureWidget

INCLUDEPATH += $$PWD/
DEPENDPATH += $$PWD/

Объявим класс плагина.

#ifndef CQTOPENCVCAPTUREWIDGETPLUGIN_H
#define CQTOPENCVCAPTUREWIDGETPLUGIN_H

#include <QDesignerCustomWidgetInterface>
#include <QtDesigner/QDesignerExportWidget>

class
    #if defined(QDESIGNER_EXPORT_WIDGETS)
      QDESIGNER_WIDGET_EXPORT
    #else
      Q_DECL_EXPORT
    #endif
CQtOpenCVCaptureWidgetPlugin : public QObject, public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetInterface)

  public:
    CQtOpenCVCaptureWidgetPlugin(QObject *parent = 0);

    bool isContainer() const;
    bool isInitialized() const;
    QIcon icon() const;
    QString domXml() const;
    QString group() const;
    QString includeFile() const;
    QString name() const;
    QString toolTip() const;
    QString whatsThis() const;
    QWidget *createWidget(QWidget *parent);
    void initialize(QDesignerFormEditorInterface *core);

  private:
    bool m_initialized;
};

Объявим класс коллекции:

#ifndef CQTOPENCVCOLLECTION_H
#define CQTOPENCVCOLLECTION_H

#include <QtDesigner/QtDesigner>
#include <QtCore/qplugin.h>

class CQtOpenCVCollection : public QObject, public QDesignerCustomWidgetCollectionInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
    
  public:
    explicit CQtOpenCVCollection(QObject *parent = 0);
    
    virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const;
    
  private:
    QList<QDesignerCustomWidgetInterface*> m_widgets;
};

#endif

Реализация:

#include "cqtopencvcapturewidgetplugin.h"
//#include "cqtopencvimagewidgetplugin.h"
//#include "cqtopencvcannywidgetplugin.h"
#include "cqtopencvcollection.h"

CQtOpenCVCollection::CQtOpenCVCollection(QObject *parent)
  : QObject(parent)
{
  m_widgets.append(new CQtOpenCVCaptureWidgetPlugin(this));
//  m_widgets.append(new CQtOpenCVImageWidgetPlugin(this));
//  m_widgets.append(new CQtOpenCVCannyWidgetPlugin(this));

}

QList<QDesignerCustomWidgetInterface*> CQtOpenCVCollection::customWidgets() const
{
  return m_widgets;
}

Q_EXPORT_PLUGIN2(CQtOpenCVCollection, CQtOpenCVCollection)

Обратите внимание на

//  m_widgets.append(new CQtOpenCVImageWidgetPlugin(this));
//  m_widgets.append(new CQtOpenCVCannyWidgetPlugin(this));

После реализаций плагинов CQtOpenCVImageWidgetPlugin и CQtOpenCVCannyWidgetPlugin комментарии можно будет убрать. И в группе OpenCV компонент Qt Designer появятся новые элементы.

Aрхива проекта.

Тестовый пример.

После сборки всех библиотек, корректного назначения ссылок и установки ссылки на коллекцию в каталог плагинов для Qt Designer можно создать тестовый пример.

В Дизайнере создаём MainWindow, помещаем наш виджет, собираем.
image

Видим:
image

Заключение.

  • Ещё раз, Исходные коды проекта.
  • Попробуйте добавить ComboBox для выбора типа камер
  • НЕе пишите, пожалуйса, «а можно сделать так». Просто, сделайте. Коды в вашем распоряжении

Спасибо за внимание.

Автор: coffeesmoke

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js