Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. [solved] To call function in C++ from QML
Forum Updated to NodeBB v4.3 + New Features

[solved] To call function in C++ from QML

Scheduled Pinned Locked Moved QML and Qt Quick
17 Posts 5 Posters 8.0k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    stereomatching
    wrote on last edited by
    #5

    Add some debug message in your destructor, in my cases
    every classes destroy by the Loader will call destructor

    I guess your c++ codes maybe has some problem(s)
    I do not see any codes to release the camera in your destructor
    or you just omit it?The resource allocated by openCV1 has to
    destroy by the user explicitly because there are no RAII nor
    garbage collector in c.

    In c++, we don't have to call destructor explicitly
    in most of the time except of some special cases(like using
    malloc to allocate memory but not new).The purpose of destructor
    is make sure the resource will be deleted when the object leave the
    scope(die), you have to implement your own resource cleaning codes
    in destructor.

    If you find yourself need to call destructor explicitly,
    maybe you are doing something wrong(smell of bug).

    The other sign of bug in c++ is not using smart pointer(RAII) to handle
    your resources when you can, openCV2 widely use the technique of
    RAII, the codes wrote by openCV2 are far more easier to maintain than
    openCV1.If you use the api provided by openCV2, it would release the
    camera when the object die(if you allocate the object on stack or wrap
    it by smart pointer). In c++, we don't have to handle the memory all
    by ourselves, stick with RAII, it will save you from a lot of troubles.

    The advices from the openCV developer

    bq. The main downside of the C++ interface is that many embedded development systems at the moment support only C. Therefore, unless you are targeting embedded platforms, there’s no point to using the old methods (unless you’re a masochist programmer and you’re asking for trouble).

    "opencv tutorial":http://docs.opencv.org/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html#matthebasicimagecontainer

    1 Reply Last reply
    0
    • S Offline
      S Offline
      stereomatching
      wrote on last edited by
      #6

      Exactly, in modern c++, with the helps of stl and smart pointer(RAII),
      we usually don't have to design our destructor , because the resources
      will be freed when they leave the scope(die).

      If your destructor really haven't been called after the Loader change
      their source, you could consider the solution of sierdzio suggested.

      @
      onClicked:{
      if(pageloader.status == Loader.Ready){
      pageloader.item.releaseCamera()
      }
      pageloader.source="second.qml"
      }
      @

      c++ is very powerful, flexible, the codes of c++ could be very high level or
      very low level(as low as c).But we need disciplines(rules) to write robust,
      good c++ codes(I am learning too).Simply apply the experiences from c, java or c# to c++
      would bring us troubles, because I was a fan of c so I know the agony.

      My painful memories tell me"c with classes" is an anti-paradigm which we should
      avoid in c++ most of the cases.To tell you the truth, I treat it as the sign of troubles
      in c++ and pretty disgust it(in some rare cases, we still need it).

      You may have different opinions than mind or think I am too arrogant
      to say something like this, if so, I am sorry and just ignore my ignorance bubble.

      1 Reply Last reply
      0
      • B Offline
        B Offline
        bts-007
        wrote on last edited by
        #7

        sierdzio and stereo thank you for your great ideas. I thought by making this as global
        @
        CvCapture * camera = cvCreateCameraCapture(0);
        IplImage * image=cvQueryFrame(camera);
        @
        camera is going to be initialized only once and the newly created object will get the output of camera. Ofcourse now I am getting the video in second screen also. The problem what I am facing is, the second screen video is overlaping the first screen. how to destroy the first screen.

        1 Reply Last reply
        0
        • sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #8

          Set "visible" to false.

          (Z(:^

          1 Reply Last reply
          0
          • B Offline
            B Offline
            bts-007
            wrote on last edited by
            #9

            Yes you are right. Now only the second screen is visible. Here comes the new problem. when I go back and forth between main and second qml screen continuosly , there is a degradation in performance. video capturing is becoming slow and it is showing 2-5 frames per second.

            1 Reply Last reply
            0
            • S Offline
              S Offline
              stereomatching
              wrote on last edited by
              #10

              May you show us your codes?Maybe your resources handle have some problem(forget to delete something etc)?

              May I ask you why do you set the openCV camera as global parameter?
              If you need the same camera parameter could be used
              between different entity of the same class, you culd declare it as
              static data member, this way could reduce the chance of name conflict.

              My implementation with openCV2 and Qt5

              @
              #ifndef OPENCVCAMERA_HPP
              #define OPENCVCAMERA_HPP

              #include <QQuickPaintedItem>
              #include <QString>

              //#include <opencv2/core/core.hpp>
              #include <opencv2/highgui/highgui.hpp>

              class QPainter;
              class QTimer;

              class openCVCamera : public QQuickPaintedItem
              {
              Q_OBJECT
              Q_PROPERTY(int imageHeight READ imageHeight WRITE setImageHeight NOTIFY imageHeightChanged)
              Q_PROPERTY(int imageWidth READ imageWidth WRITE setImageWidth NOTIFY imageWidthChanged)
              Q_PROPERTY(bool play READ play WRITE setPlay NOTIFY playChanged)
              public:
              explicit openCVCamera(QQuickItem *parent = 0);
              openCVCamera(openCVCamera const&)=delete;
              openCVCamera& operator=(openCVCamera const&)=delete;

              int imageHeight() const;
              int imageWidth() const;
              
              Q_INVOKABLE void open(int width, int height);
              
              void paint(QPainter *painter);
              bool play() const;
              
              Q_INVOKABLE void release();
              
              void setImageHeight(int value);
              void setImageWidth(int value);
              void setPlay(bool value);
              

              signals:
              void imageHeightChanged();
              void imageWidthChanged();

              void playChanged();
              

              public slots:
              void open(size_t index);

              private slots:
              void captureFrame();

              private:
              cv::VideoCapture camera_;
              cv::Mat cameraFrame_;

              cv::Mat displayedFrame_;
              
              int imageHeight_;
              int imageWidth_;
              
              QString log_;
              
              bool play_;
              
              cv::Mat result_;
              
              QTimer *timer_;
              

              };

              #endif // OPENCVCAMERA_HPP

              @

              @
              #include <QDebug>

              #include <QPainter>
              #include <QTimer>

              #include <opencv2/core/core.hpp>

              #include "openCVToQt.hpp"

              #include "openCVCamera.hpp"

              openCVCamera::openCVCamera(QQuickItem *parent) :
              QQuickPaintedItem(parent), imageHeight_(320), imageWidth_(480), play_(false), timer_(new QTimer(this))
              {
              setRenderTarget(QQuickPaintedItem::FramebufferObject);
              setPerformanceHint(QQuickPaintedItem::FastFBOResizing);
              connect(timer_, &QTimer::timeout, this, &openCVCamera::captureFrame);

              open(imageWidth_, imageHeight_);
              

              }

              int openCVCamera::imageHeight() const
              {
              return imageHeight_;
              }

              int openCVCamera::imageWidth() const
              {
              return imageWidth_;
              }

              void openCVCamera::open(int width, int height)
              {
              if(!camera_.isOpened()){
              camera_.open(0);
              camera_.set(CV_CAP_PROP_FRAME_WIDTH, width);
              camera_.set(CV_CAP_PROP_FRAME_HEIGHT, height);

                  setPlay(true);
              }
              

              }

              void openCVCamera::open(size_t index)
              {
              camera_.open(index);
              if(!camera_.isOpened()){
              log_ = "could not access the camera or video";
              qDebug() << log_;
              }
              }

              void openCVCamera::paint(QPainter *painter)
              {
              if(cameraFrame_.empty()){
              log_ = "could not grab the camera frame";
              qDebug() << log_;
              return;
              }

              if(camaraFrame_.cols != imageWidth_ || cameraFrame_.rows != imageHeight_){
                  cv::resize(cameraFrame_, result_, cv::Size(imageWidth_, imageHeight_));
              }else{
                  result_ = cameraFrame_;
              }
              QImage const img = mat_to_qimage_ref(result_);
              painter->drawImage(0, 0, img);
              

              }

              bool openCVCamera::play() const
              {
              return play_;
              }

              void openCVCamera::release()
              {
              camera_.release();
              }

              void openCVCamera::setImageHeight(int value)
              {
              if(value != imageHeight()){
              imageHeight_ = value;
              qDebug()<<"height changed : "<<value;
              emit imageHeightChanged();
              }
              }

              void openCVCamera::setImageWidth(int value)
              {
              if(value != imageWidth()){
              imageWidth_ = value;
              qDebug()<<"width changed : "<<value;
              emit imageWidthChanged();
              }
              }

              void openCVCamera::setPlay(bool value)
              {
              if(value != play_){
              play_ = value;

                  if(play_){
                      timer_->start(33);
                  }else{
                      timer_->stop();
                  }
              
                  emit playChanged();
              }
              

              }

              /**********************************************************
              ****************** implementation ************************
              **********************************************************/

              void openCVCamera::captureFrame()
              {
              if(play_){
              camera_ >> cameraFrame_;
              if(cameraFrame_.empty()){
              log_ = "could not grab the camera frame";
              qDebug() << log_;
              return;
              }
              update();
              }
              }

              @

              qml
              @
              import QtQuick 2.0
              import CVCamera 1.0

              Rectangle {
              id: root
              width: 480
              height: 320

              Component{
                  id: compCamera
                  CVCam{
                      id: cam1
                      imageHeight: root.height
                      imageWidth: root.width
                  }
              }
              
              Loader{
                  id: loaderCam
              
                  anchors.fill: parent
                  sourceComponent: compCamera
              }
              
              MouseArea{
                  anchors.fill: parent
              
                  onClicked: {
                      loaderCam.sourceComponent = null
                      loaderCam.sourceComponent = compCamera
                  }
              }
              

              }

              @

              I haven't test all of the api(I prefer the Qt5 api when it is possible)
              you could give it a try if you like

              1 Reply Last reply
              0
              • B Offline
                B Offline
                bts-007
                wrote on last edited by
                #11

                sure. I haven't handled resources properly, I tried my best. Here is the source code

                common.h
                @
                #ifndef COMMON_H
                #define COMMON_H

                extern int next_screen;

                #endif // COMMON_H
                @

                MyCameraWindow.h
                @
                #ifndef MYCAMERAWINDOW_H_
                #define MYCAMERAWINDOW_H_

                #include <QWidget>
                #include <QVBoxLayout>
                #include "QOpenCVWidget.h"
                #include <opencv/cv.h>
                #include <opencv/highgui.h>
                #include<QDebug>

                class MyCameraWindow : public QWidget
                {
                Q_OBJECT
                private:
                QOpenCVWidget *cvwidget;
                CvCapture *camera;
                QVBoxLayout *layout;

                public:
                MyCameraWindow(CvCapture *cam, QWidget *parent=0);
                int timerid;

                protected:
                void timerEvent(QTimerEvent*);
                };

                #endif /MYCAMERAWINDOW_H_/
                @

                myitem.h
                @

                #ifndef MYITEM_H
                #define MYITEM_H

                #include "MyCameraWindow.h"
                #include <QtDeclarative/QDeclarativeItem>
                #include <QGraphicsProxyWidget>
                #include<QDebug>
                #include <QObject>
                #include<QOpenCVWidget.h>
                #include"common.h"

                extern CvCapture * camera;

                class MyItem : public QDeclarativeItem
                {
                Q_OBJECT
                Q_PROPERTY(int func_next_screen READ func_next_screen WRITE set_func_next_screen)
                public:

                explicit MyItem(QDeclarativeItem *parent = 0);
                MyCameraWindow *mycamera;
                
                int func_next_screen(){
                    return m_func_next_screen;
                
                }
                
                void set_func_next_screen(int &m_func){
                    next_screen=m_func;
                }
                

                private:
                QGraphicsProxyWidget* mProxy;
                int m_func_next_screen;

                };

                #endif // MYITEM_H
                @

                QOpenCVWidget.h
                @

                #ifndef QOPENCVWIDGET_H
                #define QOPENCVWIDGET_H

                #include <opencv/cv.h>
                #include <opencv/highgui.h>
                #include <QPixmap>
                #include <QLabel>
                #include <QWidget>
                #include <QVBoxLayout>
                #include <QImage>
                #include<QDesktopWidget>
                #include<QDialog>
                #include<QApplication>
                #include<QDebug>

                class QOpenCVWidget : public QWidget {
                private:
                QLabel *imagelabel;
                QVBoxLayout *layout;

                QImage image;
                cv::Mat source_image;     // Variable pointing to the same input frame
                cv::Mat dest_image;
                

                public:
                QOpenCVWidget(QWidget *parent = 0);
                void putImage(IplImage *);
                QDialog *aboutDialog;
                QLabel *label;
                };

                #endif

                @

                I used common.h for controlling but it didn't work. I used timer for changing the frame.

                1 Reply Last reply
                0
                • B Offline
                  B Offline
                  bts-007
                  wrote on last edited by
                  #12

                  common.cpp
                  @
                  int next_screen=0;

                  @

                  main.cpp
                  @
                  #include <QApplication>
                  #include"myitem.h"
                  #include<QDeclarativeView>

                  #include<qdeclarative.h>
                  #include<QDeclarativeView>
                  #include<QDeclarativeItem>
                  #include<QDeclarativeContext>

                  CvCapture * camera = cvCreateCameraCapture(0);

                  int main(int argc, char *argv[])
                  {
                  QApplication a(argc, argv);
                  qmlRegisterType<MyItem>("mywindow",1,0,"MyItem");

                  QDeclarativeView view;
                  view.setSource(QUrl("main.qml"));
                  view.showMaximized();
                  return a.exec&#40;&#41;;
                  

                  }

                  @

                  MyCameraWIndow.cpp
                  @

                  #include "MyCameraWindow.h"
                  #include"myitem.h"
                  #include"common.h"

                  MyCameraWindow::MyCameraWindow(CvCapture *cam, QWidget *parent) : QWidget(parent) {

                  camera = cam;
                  layout = new QVBoxLayout;
                  cvwidget = new QOpenCVWidget(this);
                  layout->addWidget(cvwidget);
                  layout->setContentsMargins(0,0,0,0);
                  setLayout(layout);
                  
                  timerid=startTimer(10);  // 0.1-second timer
                  

                  }

                  void MyCameraWindow::timerEvent(QTimerEvent*) {
                  IplImage *image=cvQueryFrame(camera);
                  cvwidget->putImage(image);
                  if(next_screen==1)
                  {
                  killTimer(timerid);
                  delete cvwidget;
                  next_screen=0;
                  }
                  }

                  @

                  myitem.cpp
                  @
                  #include "myitem.h"

                  #include <QtDeclarative/qdeclarative.h>

                  #include <QGraphicsProxyWidget>

                  MyItem::MyItem(QDeclarativeItem *parent): QDeclarativeItem(parent)
                  {
                  mycamera = new MyCameraWindow(camera);
                  mProxy = new QGraphicsProxyWidget(this);
                  mProxy->setWidget(mycamera);
                  }

                  @

                  QOpenCVWidget.cpp
                  @

                  #include "QOpenCVWidget.h"
                  #include"myitem.h"

                  // Constructor
                  QOpenCVWidget::QOpenCVWidget(QWidget *parent) : QWidget(parent) {

                  aboutDialog = new QDialog;
                  label = new QLabel(aboutDialog);
                  
                  layout = new QVBoxLayout;
                  imagelabel = new QLabel;
                  layout->addWidget(imagelabel);
                  layout->setContentsMargins(0,0,0,0);
                  
                  setLayout(layout);
                  

                  }

                  void QOpenCVWidget::putImage(IplImage *cvimage) {

                  source_image = cvimage;
                  
                  dest_image=cvimage;
                  
                  
                  cv::resize(source_image, source_image, cv::Size(768,576) , 0, 0);
                  cv::resize(dest_image, dest_image, cv::Size(1920,1080) , 0, 0);
                  
                  
                  cv::cvtColor(source_image,source_image,CV_BGR2RGB);
                  cv::cvtColor(dest_image,dest_image,CV_BGR2RGB);
                  
                  QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888);
                  QImage qimg1 = QImage((const unsigned char*) dest_image.data, dest_image.cols, dest_image.rows, QImage::Format_RGB888); // convert to QImage
                  
                  
                  if(QApplication::desktop()->screenCount()==2)
                  {
                      aboutDialog->setGeometry(QApplication::desktop()->screenGeometry(1));
                  
                      label->setPixmap(QPixmap::fromImage(qimg1));
                      aboutDialog->show();
                  }
                  
                  imagelabel->setPixmap(QPixmap::fromImage(qimg));
                  

                  }

                  @

                  Important to know I am using dual screen that is why I am displaying it in 1080. I tried destructor but it crashed that is why I used common variable to stop the timer and delete the object(didn't work)

                  Here is the qml part

                  main.qml
                  @
                  import QtQuick 1.1
                  import mywindow 1.0

                  Rectangle {

                  id:item_id
                  width:1300
                  height:700
                  Rectangle{
                      width: 80
                      height: 60
                      color: "red"
                      x:900
                      y:30
                      MouseArea{
                          anchors.fill: parent
                          onClicked:{
                              myitem_id.func_next_screen=1;
                  
                              pageloader.source="second.qml"
                          }
                      }
                  }
                  
                  Rectangle{
                      width:800
                      height: 500
                      x:100
                      y:100
                  
                      MyItem{
                          id:myitem_id
                      }
                  
                  }
                  Loader {
                      id: pageloader
                  }
                  

                  }
                  @

                  second.qml
                  @
                  import QtQuick 1.1
                  import mywindow 1.0

                  Rectangle {
                  id:item_id
                  width:1300
                  height:700

                  Rectangle{
                      width: 80
                      height: 60
                      color: "red"
                      x:30
                      y:30
                      MouseArea{
                          anchors.fill: parent
                          onClicked:{
                              myitem_id.func_next_screen=1;
                  
                              pageloader.source="main.qml"
                          }
                      }
                  }
                  Rectangle{
                      width:800
                      height: 500
                      x:100
                      y:100
                  
                      MyItem{
                          id:myitem_id
                      }
                  
                  }
                  

                  }

                  @

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    stereomatching
                    wrote on last edited by
                    #13

                    I don't have Qt4 and don't know how to use QGraphicsProxyWidget, but
                    there are five problems in your codes I find by now.

                    1 : you can't delete cvwidget and you don't need to do that, because
                    cvwidget has a parent, the QObject(parent of cvwidget in this case) will
                    delete it when the class destroy. Delete it manually may cause undefined
                    behavior.

                    2 : your codes wouldn't work in some cases
                    @
                    QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888);
                    @
                    cv::Mat of openCV may or may not do some byte patching on
                    every row(for performance sake), you have to tell the QImage how
                    many bytes per row it is

                    example
                    @
                    QImage((T*)(mat.data), mat.cols, mat.rows, mat.step, format);
                    @

                    by the way, wrap the codes of transfer between cv::Mat and QImage, this
                    should save you some headaches(maybe you already done)

                    3 : please use the api of openCV2 to deal with the camera, it is very easy
                    to use, check the function I wrote above(open and captureFrame).The
                    camera of openCV2 will released when the object is destroyed, you don't
                    need to released it explicitly.Besides, with the camera of openCV2, you
                    don't have to convert between IplImage and cv::Mat, this should make
                    your program faster

                    4 : you should not modify the IplImage you get from cvQueryFrame,
                    the IplImage you take may always point to the same buffer, if you
                    alter it, the camera may have some undefined behaviors.
                    You should copy the cv::Mat before you alter it

                    5 : When you transfer between cv::Mat
                    and IplImage, you better don't do anything may reallocate memory, because
                    it may have some weird behaviors(my own experiences), but I also don't know
                    the reason.

                    Another suggestion, please don't use global parameter if possible.

                    1 Reply Last reply
                    0
                    • B Offline
                      B Offline
                      bts-007
                      wrote on last edited by
                      #14

                      stereo can you share this file "openCVToQt.hpp". how did you use qml register.

                      1 Reply Last reply
                      0
                      • S Offline
                        S Offline
                        stereomatching
                        wrote on last edited by
                        #15

                        you can find it on github
                        "github":https://github.com/stereomatchingkiss/qimage_and_mat

                        The function "copy_qimage_to_mat_policy" has bug
                        I should not copy the data by memcpy, a more proper solution
                        is take the reference of qimage and clone it.

                        example
                        @
                        cv::Mat copy_qimage_to_mat_policy::start(QImage &img, int format)
                        {
                        return qimage_to_mat_ref(img).clone();
                        }
                        @

                        There are another weird bug, when the images are bmp(or png),
                        the channel is one and you take the reference of the cv::Mat
                        by the function "mat_to_qimage_ref", the result of the QImage would
                        become very weird, don't know the reason yet.I use a workaround
                        (change one channel image to three channel image) to make the codes work,
                        not a good solution, hope someday I could find the source of the bug.

                        1 Reply Last reply
                        0
                        • R Offline
                          R Offline
                          Ryein
                          wrote on last edited by
                          #16

                          Great post stereo.

                          Just so everyone knows here is how you can implement it. For a QML noob this is surprising help to know.

                          @qmlRegisterType<openCVCamera>("CVCamera",1,0,"CVCam");@

                          1 Reply Last reply
                          0
                          • J Offline
                            J Offline
                            joshmarshall95
                            wrote on last edited by
                            #17

                            I know this an old post but I was hoping someone could tell me how to physically display the camera on the QML side?

                            1 Reply Last reply
                            0

                            • Login

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved