Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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



  • I am trying to call destructor from qml. I dont know how to call it
    Here is the simple code what I tried to call constructor

    myitem.h
    @
    class MyItem : public QDeclarativeItem
    {
    Q_OBJECT

    public:
    explicit MyItem(QDeclarativeItem *parent = 0);
    ~MyItem();
    };
    @

    myitem.cpp
    @
    MyItem::MyItem(QDeclarativeItem *parent): QDeclarativeItem(parent)
    {
    ....
    }

    MyItem::~MyItem()
    {
    }
    @

    main.cpp

    @
    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(&#41;;
    

    }
    @

    main.qml

    @
    import QtQuick 1.1
    import mywindow 1.0

    Item {
    id:item_id
    Rectangle{
    x:100
    y:100
    MyItem{

        }
    
    }
    

    }
    @


  • Moderators

    You don't and you can't call the destructor. You can only delete dynamically created objects.

    When you add MyItem explicitly in your QML code, it will stay there as long as your engine works/ it's parent is alive.



  • Sierdzio thank you for the reply. Here is the problem what I am facing. I am using opencv with qt and qml.

    myitem.cpp
    @
    MyItem::MyItem(QDeclarativeItem *parent): QDeclarativeItem(parent)
    {
    CvCapture * camera = cvCreateCameraCapture(0);
    assert(camera);
    IplImage * image=cvQueryFrame(camera);
    assert(image);

    mycamera = new MyCameraWindow(camera);
    mProxy = new QGraphicsProxyWidget(this);
    mProxy->setWidget(mycamera);
    

    }
    @

    I am invoking the camera from qml side . It works fine for single qml file. Here is the code

    main.qml
    @
    import QtQuick 1.1
    import mywindow 1.0

    Item {
    id:item_id
    width:800
    height:600
    Rectangle{
    width: 80
    height: 60
    color: "red"
    MouseArea{
    anchors.fill: parent
    onClicked:{
    pageloader.source="second.qml"
    }
    }
    }

    Rectangle{
        x:100
        y:100
        MyItem{
    
        }
    
    }
    Loader {
        id: pageloader
    }
    

    }
    @

    I have to use MyItem in second.qml . I thought process will die completely but camera is still not released by the first qml. When I click the button second.qml is invoked and it crashes. I am getting an assert message at line assert(image);


  • Moderators

    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.



  • 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



  • 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.



  • 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.


  • Moderators

    Set "visible" to false.



  • 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.



  • 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



  • 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.



  • 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
        }
    
    }
    

    }

    @



  • 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.



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



  • 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.



  • 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");@



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


Log in to reply