How to load a piece of an image to QML-form on timer signal



  • Hello everybody,

    This is my first experience in QML. My task is to load a random piece (square) of a big image to QML-form on timer signal. The least of application is wriiten in C++, so I decided to realize timer and copying the part of the image in C++-model and display them through QQuickPaintedItem, redefined paint() and calling update() in the slot of the item.

    This is how I redefined paint ():
    @SomeItem::paint( QPainter *painter )
    {
    painter -> eraseRect( contentsBoundingRect());
    painter -> drawImage( QPoint(0,0), image );
    }@

    This is QML-file with registered item:
    @import QtQuick 2.0
    import SomeItem 1.0

    Rectangle {
    width: 500
    height: 500
    SomeItem {
    width: 100
    height: 100
    }
    }@

    I see no result. To make check easier, I temporarily put in paint() this code:
    @SomeItem::paint( QPainter *painter )
    {
    painter -> eraseRect( contentsBoundingRect());
    painter -> fillRect( contentsBoundingRect(), Color );
    }@

    Color is declared as Q_PROPERTY and I change it on timer signal, then slot emits Color_Changed() and calls update(). This time I see the result, but the color doesn't change. QML-form shows only the first color with which I initialized Color in item constructor.
    I have a few questions about it:
    What am I doing wrong?
    How to declare Q_PROPERTY for QImage correctly in this case?
    In standard widgets I use the second thread for random coordinates calculation and copying of the part of bigger QImage to the smaller to avoid the stop of event handling by interface. Is there any use in doing it in QML?

    Thank you.


  • Moderators

    Hi,

    AFAIK, you can't use QImage as QML element using Q_PROPERTY.
    Use "qquickimageprovider":http://qt-project.org/doc/qt-5.0/qtquick/qquickimageprovider.html.
    Or
    You can use QUrl and then expose its setter getter using PROPERTY.
    ie.
    @
    Q_PROPERTY(QUrl imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged)
    @
    and once the path is set from the QML element access it from the cpp and "load":http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#load the image.



  • Thanks for your reply.

    I started investigating QQuickImageProvider, and request implementation with .fill() method works fine, but when I try to copy the part of an image (QImage or QPixmap) or just try to show the image on QML-form loading it either in request-method itself or before QQuickImageProvider initialization - I still have no result.

    @
    class MyProvider : public QQuickImageProvider
    {
    public:
    QPixmap SourcePixmap;
    MyProvider::MyProvider() : QQuickImageProvider( QQuickImageProvider::Pixmap )
    {
    SourcePixmap.load( "Source.png" );
    }
    QPixmap requestPixmap( const QString &id, QSize *size, const QSize &requestedSize )
    {
    int width = 80;
    int height = 80;
    if( size ) *size = QSize( width, height );
    QPixmap pixmap( requestedSize.width() > 0 ? requestedSize.width() : width,
    requestedSize.height() > 0 ? requestedSize.height() : height );

      // does not work
      pixmap = SourcePixmap.copy( 0, 0, width, height );
    
      /* does not work too
      pixmap.load( "Source80.png" ); */
      
      /* this works
      pixmap.fill( QColor( id ).rgba()); */
    
      return pixmap;
    

    }
    }
    @

    @
    int main( int argc, char *argv[])
    {
    ...
    QQuickView *viewer = new QQuickView;
    viewer -> engine() -> addImageProvider( QLatin1String( "myprovider" ), new MyProvider );
    ...
    }
    @

    @
    // main.qml
    ...
    Image { source: "image://myprovider" }
    ...
    @

    What am i doing wrong?

    [quote author="p3c0" date="1384962646"]Hi,
    You can use QUrl and then expose its setter getter using PROPERTY.
    ie.
    @
    Q_PROPERTY(QUrl imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged)
    @
    and once the path is set from the QML element access it from the cpp and "load":http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#load the image.[/quote]

    I didn't understand how can i use your idea with Qurl, as a part of the big image should be chosen randomly (that's why repeats are not very likely), it is not a set of pre-made images, which I form in a single image. I may have not enough knowlegde and experience in QML to understand your idea.

    I also wanted to ask what is better to use in my case - QImage or QPixmap - taking into account that downloading of a source takes place one time. After that very frequent copying of a random fragment, simple random transformation (horizontal/vertical flipping) and showing on QML-form are made.



  • I don't know how to use QQuickImageProvider yet(any benefit(s) compare to QQuickPaintedItem?).But QQuickPaintedItem works well for me, you can't set the QImage as property directly, but you could provide QString as property without any problem.

    The full source codes of my custom image is a little bit complex
    Here is the code snippet
    @
    Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged);

    void customImage::setSource(QString source)
    {
    if(source != source_){
    source_ = source;
    imageOrigin_.load(source_);
    update(); //call paint

            emit sourceChanged();        
    }
    

    }

    void customImage::paint(QPainter *painter)
    {
    //paint your image
    if(!img.isNull()){
    painter->drawImage((width() - imageScaled().width()) / 2,
    (height() - imageScaled().height()) / 2, img);
    }else{
    painter->drawImage((width() - imageScaled().width()) / 2,
    (height() - imageScaled().height()) / 2, imageScaled());
    }
    }
    @

    Therefore, you can use your qml component like this

    @
    customImage
    {
    source : "wwww.jpg"
    //other property
    }
    @



  • Ok, i got the first result. It appeared that copying a part of a big image on timer signal should be done in request-method. I see the result of the first copying, but update() doesnt't repaint item. I call update in a slot of an item on timer from C++.

    Do you have an idea, what's wrong with this?

    @
    void MyItem::paint( QPainter *painter )
    {
    img = sourceImage.copy( x, y, 100, 100 );
    painter -> drawImage( QPoint(0,0), img );
    }

    void MyItem::myItem_update_Slot()
    {
    update();
    }
    @

    So, I don't use Q_PROPERTY, is it correct?



  • Could you reproduce your problems with minimum lines of codes?



  • [quote author="stereomatching" date="1385586192"]Could you reproduce your problems with minimum lines of codes?[/quote]

    There are ten lines of code.



  • [quote author="gggfggg" date="1385666236"][quote author="stereomatching" date="1385586192"]Could you reproduce your problems with minimum lines of codes?[/quote]

    There are ten lines of code.[/quote]
    What I mean is "please give me a full example which could reproduce your problem with minimum lines of codes" but not asking you "how many lines of codes you write".

    if you want to call the c++ functions from qml site, you need to add Q_PROPERTY before the function or make them become public slots.



  • @gggfggg

    1. What thread calls MyItem::myItem_update_Slot()? I had similarly sounding problem with update() when it was called from non-UI thread.
    2. For test's sake put an animated object (e.g. flying rect) into your scene to force redrawing and see if scene updates (update content with timer at the same time).

    @stereomatching
    bq. if you want to call the c++ functions from qml site, you need to add Q_PROPERTY before the function or make them become public slots.
    Correction, it's Q_INVOKABLE
    But he doesn't need it.



  • [quote author="Sergei Eliseev" date="1385743150"]@gggfggg

    1. What thread calls MyItem::myItem_update_Slot()? I had similarly sounding problem with update() when it was called from non-UI thread.
    2. For test's sake put an animated object (e.g. flying rect) into your scene to force redrawing and see if scene updates (update content with timer at the same time).

    @stereomatching
    bq. if you want to call the c++ functions from qml site, you need to add Q_PROPERTY before the function or make them become public slots.
    Correction, it's Q_INVOKABLE
    But he doesn't need it.[/quote]

    Thanks for your correction, I didn't notice it



  • [quote author="stereomatching" date="1385677858"]
    What I mean is "please give me a full example which could reproduce your problem with minimum lines of codes"[/quote]
    I thought you need less.

    [quote author="Sergei Eliseev" date="1385743150"]
    What thread calls MyItem::myItem_update_Slot()?[/quote]
    MyItem::myItem_update_Slot() calls on timer signal. I don't use multi-threading in my code. Here I connect timer with update():
    @
    MyModel::MyModel( QObject *parent ) : QObject( parent )
    {
    myitem = new MyItem();
    mytimer = new QTimer;
    mytimer -> setInterval( 100 );
    connect(mytimer, SIGNAL(timeout()), myitem, SLOT(myItem_update_Slot()));
    mytimer -> start();
    }
    @
    And the less of the code once again:
    @
    void MyItem::myItem_update_Slot()
    {
    x = rand() % 1400;
    y = rand() % 1400;
    j = rand() % 100;
    k = rand() % 100;
    update();
    }

    void MyItem::paint( QPainter *painter )
    {
    img = sourceImage.copy( x, y, 100, 100 );
    painter -> drawImage( QPoint( j, k ), img );
    // flying rect
    // painter -> fillRect( j, k, 50, 50, Qt::black );
    }

    // main.qml
    import QtQuick 2.0
    import MyItem 1.0

    Rectangle {
    width: 360
    height: 360
    MyItem{
    width: 300
    height: 300
    }
    }
    @
    [quote author="Sergei Eliseev" date="1385743150"]
    For test's sake put an animated object[/quote]
    Thanks for the idea, I've checked it as demonstrated above (if I got it right). update() is being called, but repaint isn't done. I suspect that it can be related to the visibility scope of vars inside paint() during update() calling.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.