admin管理员组文章数量:1648972
一个简单的观看远程pdf文件的demo
准备第三方库qtpdfium
参考博客:基本用法在这里,包括库的下载和编译
服务端准备
服务端打开文件,选择要展示的pdf文件
全局定义头文件
#ifndef GLOBALDEFINE_H
#define GLOBALDEFINE_H
#include <QObject>
enum E_MSG_ID:int
{
E_MSG_ID_FILEINFO = 0x1000, //文件信息
E_MSG_ID_NEXTPAGE, //下一页
E_MSG_ID_PREPAGE, //上一页
E_MSG_ID_TURNPAGE, //跳转 帧头,加页数即可
E_MSG_ID_CURPAGE, //当前页信息,用于刷新吧
};
#pragma pack(1)
struct stNetFrameHead
{
int nMsgID; //消息ID
int nLength; //消息长度,不包括帧头
int nCount; //总包数
int nSeqNum; //包序号
char cReserve[32]; //预留,待后续添加字段后,不用频繁更新
stNetFrameHead()
{
memset(this,0,sizeof(stNetFrameHead));
}
};
//服务的返回的格式stNetFrameHead+stFileInfo
struct stFileInfo
{
char cFileName[50]; //文件名
int nTotalPages; //文件总页数
stFileInfo()
{
memset(this,0,sizeof(stFileInfo));
}
};
//cur next pre turn数据格式为 服务端返回的格式
//stNetFrameHead+curPage+pdfImageData
//客户端请求为:stNetFrameHead
#pragma pack()
#endif // GLOBALDEFINE_H
pdf操作类代码文件
头文件
#ifndef PDFOPERATOR_H
#define PDFOPERATOR_H
#include <QPixmap>
class QPdfium;
class CPdfOperator
{
public:
CPdfOperator(const QString &strFilePath);
~CPdfOperator();
/**
* @brief getFileInfo 获取pdf文件信息
* @param strFileNmae 文件名
* @param nTotalPages 总页数
* @return 获取结果
*/
bool getFileInfo(QString& strFileNmae,int& nTotalPages);
/**
* @brief getPrePage 获取前一页pdf内容
* @param pageData pdf内容
* @param cruPage 前端展示的当前页号
* @return 获取结果
*/
bool getPrePage(QByteArray& pageData,int& cruPage);
/**
* @brief getNextPage 获取下一页pdf内容
* @param pageData pdf内容
* @param cruPage 前端展示的当前页号
* @return 获取结果
*/
bool getNextPage(QByteArray& pageData,int& cruPage);
/**
* @brief getTurn 获取跳转到指定页的pdf内容
* @param turnPage 跳转的页码
* @param pageData pdf内容
* @param cruPage 前端展示的当前页号
* @return 获取结果
*/
bool getTurn(const int& turnPage,QByteArray& pageData,int& cruPage);
/**
* @brief getCurPageData 获取当前页的pdf内容
* @param pageData pdf内容
* @param cruPage 前端展示的当前页号
* @return 获取结果
*/
bool getCurPageData(QByteArray& pageData,int& cruPage);
private:
QPdfium *m_pPdfMgr; //开源库pdf管理对象指针
int m_nTotalPages; //总页数
int m_nCurPage; //当前页码从0开始
QString m_strFilePath; //pdf文件路径
};
#endif // PDFOPERATOR_H
cpp文件
#include "PdfOperator.h"
#include "qpdfium.h"
#include <QFileInfo>
#include <QDataStream>
#define DATASTREAM 1
CPdfOperator::CPdfOperator(const QString &strFilePath)
{
m_strFilePath = strFilePath;
m_pPdfMgr = new QPdfium;
m_nTotalPages = 0;
m_nCurPage = 0;
m_pPdfMgr->loadFile(strFilePath); //path为pdf路径
if(!m_pPdfMgr->isValid())
{
return;
}
m_nTotalPages = m_pPdfMgr->pageCount();
if(m_nTotalPages<=0)
{
m_pPdfMgr->loadFile("");
return;
}
}
CPdfOperator::~CPdfOperator()
{
if(m_pPdfMgr)
{
delete m_pPdfMgr;
m_pPdfMgr = nullptr;
}
}
bool CPdfOperator::getFileInfo(QString &strFileNmae, int &nTotalPages)
{
if(!m_pPdfMgr->isValid())
{
return false;
}
QFileInfo tmpInfo(m_strFilePath);
strFileNmae = tmpInfo.fileName();
nTotalPages = m_nTotalPages;
return true;
}
bool CPdfOperator::getPrePage(QByteArray &pageData, int &cruPage)
{
if(!m_pPdfMgr->isValid())
{
return false;
}
if(m_nCurPage == 0)//表示已经是第一页了,无法获取前一页
{
return false;
}
QPdfiumPage page = m_pPdfMgr->page(m_nCurPage - 1); //表示获取第n页,这里表示不是第一页,那么则获取当前页的前一页
QImage image = page.image(1.0); //将pdf转化为Qimage,后面的1.0代表显示比例
m_nCurPage-=1;
#if DATASTREAM
QDataStream ds(&pageData,QIODevice::WriteOnly);
//将图片读入array,方便发送
ds<<image;
#else
pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif
cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1
return true;
}
bool CPdfOperator::getNextPage(QByteArray &pageData, int &cruPage)
{
if(!m_pPdfMgr->isValid())
{
return false;
}
if(m_nCurPage+1 == m_nTotalPages)//表示已经最后一页了,无法获取下一页
{
return false;
}
QPdfiumPage page = m_pPdfMgr->page(m_nCurPage + 1); //读取每一页pdf
QImage image = page .image(1.0); //将pdf转化为Qimage,后面的1.0代表显示比例
m_nCurPage+=1;
#if DATASTREAM
QDataStream ds(&pageData,QIODevice::WriteOnly);
//将图片读入array,方便发送
ds<<image;
#else
pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif
cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1
return true;
}
bool CPdfOperator::getTurn(const int& nTurnPage,QByteArray &pageData, int &cruPage)
{
if(!m_pPdfMgr->isValid())
{
return false;
}
if((nTurnPage > m_nTotalPages) || (nTurnPage <=0))
{
return false;
}
QPdfiumPage page = m_pPdfMgr->page(nTurnPage-1); //读取每一页pdf
QImage image = page .image(1.0); //将pdf转化为Qimage,后面的1.0代表显示比例
m_nCurPage = nTurnPage -1;
#if DATASTREAM
QDataStream ds(&pageData,QIODevice::WriteOnly);
//将图片读入array,方便发送
ds<<image;
#else
pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif
cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1
return true;
}
bool CPdfOperator::getCurPageData(QByteArray &pageData, int &cruPage)
{
if(!m_pPdfMgr->isValid())
{
return false;
}
QPdfiumPage page = m_pPdfMgr->page(m_nCurPage); //读取每一页pdf
QImage image = page .image(1.0); //将pdf转化为Qimage,后面的1.0代表显示比例
#if DATASTREAM
QDataStream ds(&pageData,QIODevice::WriteOnly);
//将图片读入array,方便发送
ds<<image;
#else
pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif
cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1
return true;
}
网络udp操作类代码文件
头文件
#ifndef UDPOPERATOR_H
#define UDPOPERATOR_H
#include <QUdpSocket>
#include <QFuture>
#include <QSemaphore>
#include <QQueue>
#include <QPixmap>
class CPdfOperator;
class CUdpOperator:public QObject
{
Q_OBJECT
public:
CUdpOperator();
~CUdpOperator();
/**
* @brief setPdfFile 设置pdf文件路径
* @param strFilePath 文件路径
*/
void setPdfFile(const QString& strFilePath);
private slots:
/**
* @brief recvNetData 接收udp数据的槽函数
*/
void recvNetData();
private:
/**
* @brief onCheckThread 处理udp数据的线程函数
*/
void onCheckThread();
/**
* @brief parseNetWork 解析网络数据函数
* @param netData 网络数据
*/
void parseNetWork(const QByteArray& netData);
/**
* @brief init 初始化函数
*/
void init();
/**
* @brief parseFileInfo 解析收到获取文件信息的请求
*/
void parseFileInfo();
/**
* @brief parsePages 解析获取某一页pdf内容的请求
* @param nType 请求类型【前一页、后一页、当前页】
*/
void parsePages(const int& nType);
/**
* @brief parseTurnPages 解析获取跳转页请求
* @param nTurnPage 跳转页码
*/
void parseTurnPages(const int& nTurnPage);
/**
* @brief sendNetData 发送网络数据
* @param nMsgId 消息ID
* @param netData 消息数据
*/
void sendNetData(const int& nMsgId,const QByteArray& netData);
private:
QUdpSocket m_udpSocket; //和前端通信的套接字对象
QFuture<void> m_future; //处理数据线程
QSemaphore m_semaphoreData; //处理数据信号量
QQueue<QByteArray> m_queueNetData; //网络数据队列
QMutex m_mutexQueue; //队列锁
bool m_bExitThread = false; //是否退出解析线程
CPdfOperator *m_pPdfMgr = nullptr; //pdf操作对象指针
};
#endif // UDPOPERATOR_H
cpp文件
#include "UdpOperator.h"
#include "GlobalDefine.h"
#include <QImage>
#include <QHostAddress>
#include "PdfOperator.h"
#include <QtConcurrent>
#include <QThread>
#define MAXNETPAGE 40960
CUdpOperator::CUdpOperator()
{
init();
}
CUdpOperator:: ~CUdpOperator()
{
m_udpSocket.abort();
if(m_pPdfMgr)
{
delete m_pPdfMgr;
m_pPdfMgr = nullptr;
}
if(m_future.isRunning())
{
m_bExitThread = true;
m_semaphoreData.release();
m_future.waitForFinished();
}
}
void CUdpOperator::setPdfFile(const QString &strFilePath)
{
if(m_pPdfMgr == nullptr)
{
m_pPdfMgr = new CPdfOperator(strFilePath);
}
else
{
delete m_pPdfMgr;
m_pPdfMgr = nullptr;
m_pPdfMgr = new CPdfOperator(strFilePath);
}
}
void CUdpOperator::init()
{
m_future = QtConcurrent::run(this,&CUdpOperator::onCheckThread);
m_udpSocket.bind(QHostAddress::AnyIPv4,10010);
connect(&m_udpSocket,SIGNAL(readyRead()),this,SLOT(recvNetData()));
}
void CUdpOperator::parseFileInfo()
{
if(!m_pPdfMgr)
{
return;
}
QString strFileName;int nTotalPages = 0;
if(!m_pPdfMgr->getFileInfo(strFileName,nTotalPages))
{
return;
}
stFileInfo tmpInfo;
memcpy(tmpInfo.cFileName,strFileName.toUtf8().data(),strFileName.toUtf8().length());
tmpInfo.nTotalPages = nTotalPages;
QByteArray tmpArray;
tmpArray.append((char*)&tmpInfo,sizeof(tmpInfo));
sendNetData(E_MSG_ID_FILEINFO,tmpArray);
QByteArray imageData;int curPage = 0;
if(!m_pPdfMgr->getCurPageData(imageData,curPage))
{
return;
}
QByteArray tmpArraySendFirstPage;
tmpArraySendFirstPage.append((char*)&curPage,sizeof(curPage));
tmpArraySendFirstPage.append(imageData);
sendNetData(E_MSG_ID_CURPAGE,tmpArraySendFirstPage);
}
void CUdpOperator::parsePages(const int& nType)
{
if(!m_pPdfMgr)
{
return;
}
QByteArray imageData;int curPage = 0;
switch (nType)
{
case E_MSG_ID_NEXTPAGE:
if(!m_pPdfMgr->getNextPage(imageData,curPage))
{
return;
}
break;
case E_MSG_ID_PREPAGE:
if(!m_pPdfMgr->getPrePage(imageData,curPage))
{
return;
}
break;
default:
return;
break;
}
int nLength = imageData.length();
if(nLength <=0)
{
return;
}
QByteArray tmpArray;
tmpArray.append((char*)&curPage,sizeof(curPage));
tmpArray.append(imageData);
sendNetData(nType,tmpArray);
}
void CUdpOperator::parseTurnPages(const int &nType)
{
if(!m_pPdfMgr)
{
return;
}
QByteArray imageData;int curPage = 0;
if(!m_pPdfMgr->getTurn(nType,imageData,curPage))
{
return;
}
int nLength = imageData.length();
if(nLength <=0)
{
return;
}
QByteArray tmpArray;
tmpArray.append((char*)&curPage,sizeof(curPage));
tmpArray.append(imageData);
sendNetData(E_MSG_ID_TURNPAGE,tmpArray);
}
void CUdpOperator::sendNetData(const int &nMsgId, const QByteArray &netData)
{
//每一包最大字节数为MAXNETPAGE,包括帧头的长度,udp最大发送字节数不能超过65535
int nDataLength = netData.length();
int nFrameHeadLen = sizeof(stNetFrameHead);
int nPacketAlivable = MAXNETPAGE - nFrameHeadLen;
if(nDataLength + nFrameHeadLen > MAXNETPAGE)
{
int nTotalLength = nDataLength;
int nPackCount = nTotalLength % nPacketAlivable?nTotalLength/nPacketAlivable+1:nTotalLength/nPacketAlivable;
for(int i = 0;i< nPackCount;i++)
{
if(i != nPackCount -1)
{
stNetFrameHead tmpHead;
tmpHead.nCount = nPackCount;
tmpHead.nLength = MAXNETPAGE - nFrameHeadLen;
tmpHead.nMsgID = nMsgId;
tmpHead.nSeqNum = i;
QByteArray tmpArray;
tmpArray.append((char*)&tmpHead,nFrameHeadLen);
tmpArray.append(netData.data()+(MAXNETPAGE - nFrameHeadLen)*i,MAXNETPAGE - nFrameHeadLen);
m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
}
else //最后一包
{
stNetFrameHead tmpHead;
tmpHead.nCount = nPackCount;
tmpHead.nLength = nDataLength - i*(MAXNETPAGE - nFrameHeadLen);
tmpHead.nMsgID = nMsgId;
tmpHead.nSeqNum = i;
QByteArray tmpArray;
tmpArray.append((char*)&tmpHead,nFrameHeadLen);
tmpArray.append(netData.data()+(MAXNETPAGE - nFrameHeadLen)*i,nDataLength - i*(MAXNETPAGE - nFrameHeadLen));
m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
}
QThread::msleep(100);//发送太快会失败//真实代码需要优化,用线程发送,或者用c++原始UDP套接字
}
}
else
{
stNetFrameHead tmpHead;
tmpHead.nCount = 1;
tmpHead.nLength = netData.length();
tmpHead.nMsgID = nMsgId;
tmpHead.nSeqNum = 0;
QByteArray tmpArray;
tmpArray.append((char*)&tmpHead,nFrameHeadLen);
tmpArray.append(netData);
m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
}
}
void CUdpOperator::recvNetData()
{
QByteArray array;
QHostAddress address;
quint16 port;
array.resize(m_udpSocket.bytesAvailable());//根据可读数据来设置空间大小
m_udpSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据
m_mutexQueue.lock();
m_queueNetData.enqueue(array);
m_mutexQueue.unlock();
m_semaphoreData.release();
}
void CUdpOperator::onCheckThread()
{
while(true)
{
if(m_bExitThread)
{
break;
}
m_semaphoreData.acquire();
if(m_bExitThread)
{
break;
}
m_mutexQueue.lock();
QByteArray array = m_queueNetData.dequeue();
m_mutexQueue.unlock();
parseNetWork(array);
}
}
void CUdpOperator::parseNetWork(const QByteArray& netData)
{
int nFrameLength = sizeof(stNetFrameHead);
if(netData.length() < nFrameLength)
{
return;
}
stNetFrameHead tmpHead;
memcpy(&tmpHead,netData.constData(),nFrameLength);
switch (tmpHead.nMsgID)
{
case E_MSG_ID_FILEINFO:
parseFileInfo();
break;
case E_MSG_ID_NEXTPAGE:
case E_MSG_ID_PREPAGE:
parsePages(tmpHead.nMsgID);
break;
case E_MSG_ID_TURNPAGE:
{
int nTurnPage = 0;
memcpy(&nTurnPage, netData.data()+ nFrameLength,netData.length() - nFrameLength);
parseTurnPages(nTurnPage);
}
break;
default:
return;
break;
}
}
界面文件就一个函数
QString strFileName = QFileDialog::getOpenFileName(this,"open pdf","/home"
,"*.pdf");
if(strFileName.isEmpty())
{
return;
}
m_udpoperator.setPdfFile(strFileName);
这里用宏定义区分开了代码,前后端的界面是一样的,我懒,就copy了一份代码作为服务端,宏定义为1时,就是本地操作pdf的演示代码,详情下载源码了解。
客户端准备
客户端点击打开文件,可以获取服务端pdf文件信息,文件名和总页数,获取成功后,就可以进行操作了,上一页,下一页,以及跳转,进行文件的缩放【暂时还有bug,后续解决完善】
全局定义头文件
同服务端一样
udp操作类
头文件
#ifndef UDPOPERATOR_H
#define UDPOPERATOR_H
#include <QUdpSocket>
#include <QFuture>
#include <QSemaphore>
#include <QQueue>
#include <QPixmap>
#include <QMap>
enum E_MSG_ID;
class CUdpOperator:public QObject
{
Q_OBJECT
public:
CUdpOperator();
~CUdpOperator();
/**
* @brief PdfOpertorType pdf亲求类型
* @param eType 请求类型
* @param nTurnPage 如果为跳转请求,则表示跳转页数,其他请求不用
*/
void PdfOpertorType(const E_MSG_ID eType,const int& nTurnPage = 0);
signals:
/**
* @brief signalFileInfo pdf文件信息请求结果
* @param strFileNmae 文件名
* @param nTotalPages 文件页数
*/
void signalFileInfo(const QString& strFileNmae,const int& nTotalPages);
/**
* @brief signalCurPageInfo pdf内容请求结果
* @param pixMap pdf内容
* @param nCurPage 当前显示页码
*/
void signalCurPageInfo(const QPixmap& pixMap,const int& nCurPage);
private slots:
/**
* @brief recvNetData 结束服务端网络数据的槽函数
*/
void recvNetData();
private:
/**
* @brief onCheckThread 处理服务端数据的线程函数
*/
void onCheckThread();
/**
* @brief parseNetWork 解析网络数据
* @param netData 网络数据
*/
void parseNetWork(const QByteArray& netData);
/**
* @brief init 初始化操作
*/
void init();
/**
* @brief parseFileInfo 解析请求文件信息结果
* @param nLength 数据长度
* @param pData 数据起始地址指针
*/
void parseFileInfo(const int& nLength,const char* pData);
/**
* @brief parsePages 解析请求获取pdf页码内容结果
* @param nLength 数据长度
* @param pData 数据起始地址指针
*/
void parsePages(const int& nLength,const char* pData);
private:
QUdpSocket m_udpSocket; //和服务端通信的套接字对象
QFuture<void> m_future; //处理数据线程
QSemaphore m_semaphoreData; //处理数据信号量
QQueue<QByteArray> m_queueNetData; //数据队列
QMutex m_mutexQueue; //队列锁
bool m_bExitThread = false; //退出处理线程标志
QMap<int ,QMap<int,QByteArray>> m_mapNetData; //组包使用的容器,分别表示消息id,消息序号,消息内容
};
#endif // UDPOPERATOR_H
cpp文件
#include "UdpOperator.h"
#include "GlobalDefine.h"
#include <QImage>
#include <QHostAddress>
#include <QtConcurrent>
CUdpOperator::CUdpOperator()
{
init();
}
CUdpOperator::~CUdpOperator()
{
m_udpSocket.abort();
if(m_future.isRunning())
{
m_bExitThread = true;
m_semaphoreData.release();
m_future.waitForFinished();
}
}
void CUdpOperator::PdfOpertorType(const E_MSG_ID eType, const int &nTurnPage)
{
stNetFrameHead tmpHead;
tmpHead.nMsgID = eType;
tmpHead.nCount = 1;
tmpHead.nLength = 0;
tmpHead.nSeqNum = 0;
if(eType == E_MSG_ID_TURNPAGE)
{
QByteArray tmpArray;
tmpArray.append((char*)&tmpHead,sizeof(tmpHead));
tmpArray.append((char*)&nTurnPage,sizeof(nTurnPage));
m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10010);
}
else
{
m_udpSocket.writeDatagram((char*)&tmpHead,sizeof(tmpHead),QHostAddress("127.0.0.1"),10010);
}
}
void CUdpOperator::init()
{
m_future = QtConcurrent::run(this,&CUdpOperator::onCheckThread);
m_udpSocket.bind(QHostAddress::AnyIPv4,10086);
connect(&m_udpSocket,SIGNAL(readyRead()),this,SLOT(recvNetData()));
}
void CUdpOperator::parseFileInfo(const int &nLength, const char *pData)
{
int nStInfoLength = sizeof(stFileInfo);
if(pData == NULL || nLength != nStInfoLength)
{
return;
}
stFileInfo tmpInfo;
memcpy(&tmpInfo,pData,nStInfoLength);
emit signalFileInfo(tmpInfo.cFileName,tmpInfo.nTotalPages);
}
void CUdpOperator::parsePages(const int &nLength, const char *pData)
{
int nCurLength = sizeof(int);
if(pData == NULL || nLength <= nCurLength)
{
return;
}
int curPage;
memcpy(&curPage,pData,nCurLength);
// QImage image = QImage::fromData();
QByteArray msg = QByteArray(pData+nCurLength,nLength-nCurLength);
QImage img;
QDataStream ds(&msg,QIODevice::ReadOnly);
ds>>img;
emit signalCurPageInfo(QPixmap::fromImage(img),curPage);
}
void CUdpOperator::recvNetData()
{
QByteArray array;
QHostAddress address;
quint16 port;
array.resize(m_udpSocket.bytesAvailable());//根据可读数据来设置空间大小
m_udpSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据
m_mutexQueue.lock();
m_queueNetData.enqueue(array);
m_mutexQueue.unlock();
m_semaphoreData.release();
}
void CUdpOperator::onCheckThread()
{
while(true)
{
if(m_bExitThread)
{
break;
}
m_semaphoreData.acquire();
if(m_bExitThread)
{
break;
}
m_mutexQueue.lock();
QByteArray array = m_queueNetData.dequeue();
m_mutexQueue.unlock();
parseNetWork(array);
}
}
void CUdpOperator::parseNetWork(const QByteArray& netData)
{
int nFrameLength = sizeof(stNetFrameHead);
if(netData.length() < nFrameLength)
{
return;
}
stNetFrameHead tmpHead;
memcpy(&tmpHead,netData.constData(),nFrameLength);
if(tmpHead.nCount != 1)
{
if(m_mapNetData.contains(tmpHead.nMsgID))
{
m_mapNetData[tmpHead.nMsgID][tmpHead.nSeqNum] = QByteArray(netData.data()+nFrameLength,netData.length() - nFrameLength);
if(m_mapNetData[tmpHead.nMsgID].size() == tmpHead.nCount)//组包完成 todo这里没考虑超时,丢包的情况,真实项目代码需要考虑,可以参考博客https://blog.csdn/qq_44771391/article/details/132258308?spm=1001.2014.3001.5502
{
QList<QByteArray> tmpLst = m_mapNetData[tmpHead.nMsgID].values();
QByteArray tmpArray;
for(int i = 0;i<tmpLst.size();i++)
{
tmpArray.append(tmpLst.at(i));
}
parsePages(tmpArray.length(),tmpArray.data());//这里消息没做区分,是因为文件信息不会组包,不会超过65535
m_mapNetData.remove(tmpHead.nMsgID);
}
}
else
{
m_mapNetData[tmpHead.nMsgID][tmpHead.nSeqNum] = QByteArray(netData.data()+nFrameLength,netData.length() - nFrameLength);
}
}
else
{
switch (tmpHead.nMsgID)
{
case E_MSG_ID_FILEINFO:
parseFileInfo(netData.length() - nFrameLength,netData.data()+nFrameLength);
break;
case E_MSG_ID_NEXTPAGE:
case E_MSG_ID_PREPAGE:
case E_MSG_ID_TURNPAGE:
case E_MSG_ID_CURPAGE:
parsePages(netData.length() - nFrameLength,netData.data()+nFrameLength);
break;
default:
return;
break;
}
}
}
界面文件
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPixmap>
class QPdfium;
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class CUdpOperator;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
/**
* @brief on_btn_openFile_clicked 发送文件请求
*/
void on_btn_openFile_clicked();
/**
* @brief on_btn_prePage_clicked 发送前一页请求
*/
void on_btn_prePage_clicked();
/**
* @brief on_btn_nextPage_clicked 发送下一页请求
*/
void on_btn_nextPage_clicked();
/**
* @brief on_btn_turn_clicked 发送跳转请求
*/
void on_btn_turn_clicked();
/**
* @brief on_FileInfo 得到文件内容结果
* @param strFileNmae 文件名
* @param nTotalPages 总页数
*/
void on_FileInfo(const QString& strFileNmae,const int& nTotalPages);
/**
* @brief on_CurPageInfo 当前展示页结果
* @param pixMap pdf内容
* @param nCurPage 当前页码
*/
void on_CurPageInfo(const QPixmap& pixMap,const int& nCurPage);
protected:
//事件过滤器,用于Pdf内容缩放使用,目前不完善待后续完善
bool eventFilter(QObject *watched, QEvent *event) override;
private:
Ui::Widget *ui;
CUdpOperator* m_pUdpOpertor = nullptr; //udp操作类
qreal m_zoomFactor; //缩放系数
QPixmap m_curPdf; //当前Pdf内容
};
#endif // WIDGET_H
cpp文件
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QWheelEvent>
#include "UdpOperator.h"
#include "GlobalDefine.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
m_zoomFactor = 1;
ui->label_showPdf->installEventFilter(this);
m_pUdpOpertor = new CUdpOperator;
connect(m_pUdpOpertor,SIGNAL(signalFileInfo(const QString& ,const int& )),this,SLOT(on_FileInfo(const QString& ,const int& )));
connect(m_pUdpOpertor,SIGNAL(signalCurPageInfo(const QPixmap& ,const int& )),this,SLOT(on_CurPageInfo(const QPixmap& ,const int& )));
setWindowTitle("Client");
}
Widget::~Widget()
{
if(m_pUdpOpertor)
{
delete m_pUdpOpertor;
m_pUdpOpertor = nullptr;
}
delete ui;
}
void Widget::on_btn_openFile_clicked()
{
m_pUdpOpertor->PdfOpertorType(E_MSG_ID_FILEINFO);
}
void Widget::on_btn_prePage_clicked()
{
m_pUdpOpertor->PdfOpertorType(E_MSG_ID_PREPAGE);
}
void Widget::on_btn_nextPage_clicked()
{
m_pUdpOpertor->PdfOpertorType(E_MSG_ID_NEXTPAGE);
}
void Widget::on_btn_turn_clicked()
{
m_pUdpOpertor->PdfOpertorType(E_MSG_ID_TURNPAGE,ui->spinBox_turnPage->value());
}
void Widget::on_FileInfo(const QString &strFileNmae, const int &nTotalPages)
{
ui->label_FileName->setText(strFileNmae);
ui->spinBox_totalPage->setValue(nTotalPages);
}
void Widget::on_CurPageInfo(const QPixmap &pixMap, const int &nCurPage)
{
m_curPdf = pixMap;
ui->label_showPdf->setPixmap(m_curPdf);
ui->spinBox_curPage->setValue(nCurPage);
}
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() != QEvent::Wheel)
{
return QWidget::eventFilter(watched,event);
}
if(watched == ui->label_showPdf)//判断是否是label_showPdf委托
{
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
//获取滚轮角步数
int value = wheelEvent->angleDelta().y();
qreal deltzZoom = 0.1;//缩放因子增量
if(value > 0) //放大
{
m_zoomFactor += deltzZoom;
}
else //缩小
{
m_zoomFactor -= deltzZoom;
}
if(m_zoomFactor<0.1)
{
m_zoomFactor = 0.1;
}
// ui->label_showPdf->setPixmap(m_curPdf.scaled(m_zoomFactor*m_curPdf.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
ui->label_showPdf->setPixmap(m_curPdf.scaled(m_zoomFactor*ui->label_showPdf->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
update();
return true;//阻止传递到对象
}
else
{
return QWidget::eventFilter(watched,event);//继续传递到对象
}
}
结果展示
其他
1.基本完成了pdf内容的浏览,pdf在网络中主要以图片的形式进行发送,pdf质量取决于图片的质量,代码中有两种方式发送QImage,一种发送原始数据,一种是QDataStream序列化的方式,用宏定义区分开了,还有其他方式没有进行尝试,待后续完善。
2.该程序还不算完善,pdf的缩放不是很好,pdf质量可能是我没用原始数据的原因,也不是很好,待后续完善。
3.服务端程序可以进行本地pdf展示,将宏定义值在0和1切换即可。
4.资源文件里面包含第三方pdf操作库的源码,在参考博客里面和源码里面可以看到用法,另外高版本Qt里面有自带pdf操作库,不知道是不是这个。如果版本较低的Qt想简单操作pdf可以使用这个库,比较里面的变量都是Qt相关的兼容度比较好。
更新【20241013】
1.更新是缩放界面,前后端均更新;后端在本地模式(LOCAL_PDF宏开关0 1切换)的时候可以查看,并且添加缩放功能。
后端界面
2.缩放解决方案不是很好,将就能用,但是前端显示速度还待提高,后续是性能方面的提高,以及缩放看是否有更好的方案,目前是将pdf内容直接提取较大放大倍速发送到前端,前端通过还原比例,这样画质不会太离谱,也算一种方式,后续看是否有更好的方案再更新,比如用矢量图。
后端核心代码
QImage image = page.image(INISIZE); //将pdf转化为Qimage,后面的1.0代表显示比例,这里INISIZE初始化为3.0
前端核心代码
QSvgRenderer tmpSvg;
tmpSvg.load(m_curSvg);
QPixmap pixMap(tmpSvg.defaultSize()/3*m_zoomFactor);//这里的3对应前面的INISIZE宏定义,后期优化可以在网络阶段进行配置
pixMap.fill(Qt::transparent);
QPainter painter1(&pixMap);
painter1.setRenderHints(QPainter::Antialiasing);
tmpSvg.render(&painter1);
ui->label_showPdf->resize(pixMap.size());
ui->label_showPdf->setPixmap(pixMap);
这样操作后,前端的画质基本和后端持平,但是最大放大倍数理论应该和INISIZE一样,否则也会画质模糊,前后的INISIZE一样,则能保持画质质量。
另:上面的svg相关,发现不太行,应该还是和原始画质有关,QImage image = page.image(INISIZE);这里相当于是原始数据,如果INISIZE是1.0,转为Svg,发送到前端,前端放大还是会模糊,但是我如果INISIZE为3.0,到前端在缩小放大画质就还可以【不太了解图像处理这一一块,后续学习一下】。
版权声明:本文标题:Qt远程预览pdf文件 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1729505294a1203555.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论