QML學習筆記——圖片動態顯示(Image與QQuickImageProvider)

寫在前面

  • 項目中需要一個預覽和回顯的功能,需要通過udp接收圖片數據,並在界面上動態顯示,在網上研究一番後,最終選擇了ImageQQuickImageProvider來實現。
  • 這裏只是記錄下關鍵機制,實際應用場景更復雜,需要拆包幷包,考慮超時,掉線,多設備同時預覽的效率等問題。

實際使用

實際使用

環境

  • Qt 5.9.3 + MinGW
  • window 10

Image

  • Image來實現圖片的顯示,可以顯示本地圖片和網絡圖片。
Image {
    id: img_preview;
    width: 170;
    height: 100;
    source: "qrc:/bord/img/block.jpg"; //加載資源文件
    //source: "file:///E:/bord/img/block.jpg"; //加載本地圖片
    //cache:false;  //是否緩存 
}

QQuickImageProvider

  • The QQuickImageProvider class provides an interface for supporting pixmaps and threaded image requests in QML.
    可以將QPixmap或者QImage的圖片提供給QML進行顯示。

具體實現

新建UDP監聽線程

  • 建立一個UDP線程單獨監聽圖片數據,這裏寫的比較簡單,假設udp直接發的一包完整jpg圖片數據,data_image 爲全局變量。mutex_prest爲互斥鎖(QMutex),因爲data_image會被其他線程訪問(不然程序容易崩潰)。

  • QByteArray data_image QMutex mutex_prest我是定義在config.h裏面的,可以根據實際使用自行安排。

void MyThread::run()
{
    receiver_udp=new QUdpSocket;
    receiver_udp->bind(00000,QUdpSocket::ShareAddress);  //綁定需要監聽的端口
    connect(receiver_udp,SIGNAL(readyRead()),this,SLOT(processPendingDatagram()),Qt::DirectConnection);
    exec();
}

void MyThread::processPendingDatagram()
{
    while(receiver_udp->hasPendingDatagrams())  //擁有等待的數據報
    {
        QByteArray datagram;
        datagram.resize(receiver_udp->pendingDatagramSize());
        receiver_udp->readDatagram(datagram.data(),datagram.size());

        mutex_prest.lock();  //加鎖

        //data_image是全局變量,這邊datagram爲一張圖片的包,實際可能需對數據進行處理
        data_image.clear();
        data_image = datagram;

        mutex_prest.unlock();  //解鎖
    }
}

繼承QQuickImageProvider類

  • 頭文件
#ifndef MYIMAGEPROVIDER_H
#define MYIMAGEPROVIDER_H

#include <QQuickImageProvider>
//#include <QQuickWindow>
#include <QImage>

class MyImageProvider : public QQuickImageProvider
{

public:
    MyImageProvider();
    ~MyImageProvider();

    QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize);
};

#endif // MYIMAGEPROVIDER_H
  • cpp
  • 利用函數requestImage(const QString &id, QSize *size, const QSize &requestedSize)完成圖片請求,在多個請求或者多個圖片時,可以用 id 變量進行進一步判斷。
#include "myimageprovider.h"
#include "config.h"
#include <QImageReader>
#include <QBuffer>

MyImageProvider::MyImageProvider()
    : QQuickImageProvider(QQuickImageProvider::Image)
{
}

MyImageProvider::~MyImageProvider()
{
}

QImage MyImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    //按IP和類型進行處理
    //qDebug() << "id: " << id;
    //QString type;
    //type=id.mid(0,id.indexOf(":"));
    //QString str;
    //str=id.mid(id.indexOf(":")+1,id.indexOf("/")-id.indexOf(":")-1);

    QImage image;

    if(data_image.size()!=0)
    {
        mutex_prest.lock();
        //QByteArray 轉 QImage
        QBuffer buffer(&(data_image.image));
        buffer.open(QIODevice::ReadOnly);
        QImageReader reader(&buffer,"JPG");
        image = reader.read();
        mutex_prest.unlock();
    }
    else
    {
        //若圖片數據爲空,則加載默認圖片
        image.load(":/bord/img/block.jpg");
    }

    return image;
}

註冊MyImageProvider

  • main.cpp中將MyImageProvider註冊到QML中。

MyImageProvider *myImg = new MyImageProvider();
QQmlApplicationEngine engine;
engine.addImageProvider(“MyProvider”, myImg);
engine.load(QUrl(QLatin1String(“qrc:/main.qml”)));

Image動態顯示

  • cache是否緩存,設爲false。默認爲true
  • 使用定時器動態刷新,定時時間根據具體接收效率和顯示幀率設置。或者也可以採用信號與槽的方式通知刷新。
Image {
    id: img_preview
    cache: false;  //取消緩存
    width: 170;
    height: 100;
    source: "qrc:/bord/img/block.jpg" //默認圖片
}

Timer{
    //定時器觸發時間 單位毫秒
    interval: 100;
    //觸發定時器
    running: true;
    //不斷重複
    repeat: true;
    //定時器觸發時執行
    onTriggered: {
        img_preview.source = "";
        img_preview.source = "image://myprovider/ip:xxx.xxx.xxx.xxx";
    }
}

小結

  • 當某個變量被多個線程訪問時,注意使用QMutex來保護訪問安全。
  • Image在刷新source時,注意先置空,且取消緩存,不然會出現圖片刷新無反應的情況,或者使用加隨機數的方式
    source = "image://myprovider/ip:"+Math.random();

知識共享許可協議
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章