寫在前面
- 項目中需要一個預覽和回顯的功能,需要通過udp接收圖片數據,並在界面上動態顯示,在網上研究一番後,最終選擇了
Image
和QQuickImageProvider
來實現。 - 這裏只是記錄下關鍵機制,實際應用場景更復雜,需要拆包幷包,考慮超時,掉線,多設備同時預覽的效率等問題。
實際使用
環境
- 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 國際許可協議進行許可。