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.
  • sierdzioS Offline
    sierdzioS Offline
    sierdzio
    Moderators
    wrote on last edited by
    #4

    Add an invokable method to MyItem class where you release the camera. Then simply make sure you call this method before showing your second.qml.

    (Z(:^

    1 Reply Last reply
    0
    • 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