一、前言
Qt6.4版本的在线安装器列表,有PDF模块,不清楚6.0到6.3是否有,反正5.X是没有的,以前用5.9版本,写过一个小例子,当时用的是MuPDF的源码下载自己编译的dll库,过程之艰辛,一把辛酸泪。。。
本文就以Qt最新版内置的PDF模块简单做一个PDF阅读器
二、说明
1、环境
MSVC2019 64bit
Qt6.4.2
Windows 10 64位
Qt6.4安装时需要勾选PDF模块
三、代码
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QPdfDocument>
#include <QPdfView>
#include <QPdfLink>
#include <QPdfSearchModel>
#include <QPdfBookmarkModel>
#include <QPdfPageNavigator>
#include <QFileDialog>
#include <QDebug>
#include <QTreeView>
#include <QListView>
#include <QMutex>
#include <QBoxLayout>
#include <QStandardItemModel>
#include <QDateTime>
#include <QScrollBar>
#include <QWheelEvent>
#include <QInputDialog>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
#include "qlistdelegate.h"
#include <QScrollArea>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QPdfView* pdfview=nullptr;
QPdfPageNavigator* mNav=nullptr;
QPdfSearchModel* mSerachmodel=nullptr;
protected:
bool eventFilter(QObject *watched, QEvent *event);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug()<<"中文";
ui->pushButton->setText("打开");
QPdfDocument* doc = new QPdfDocument(this);
pdfview = new QPdfView(this);
mNav = pdfview->pageNavigator();
pdfview->viewport()->installEventFilter(this);
pdfview->installEventFilter(this);
pdfview->setPageMode(QPdfView::PageMode::MultiPage);
pdfview->setZoomMode(QPdfView::ZoomMode::Custom);
pdfview->setDocument(doc);
QVBoxLayout* layout = new QVBoxLayout();
ui->centralwidget->setLayout(layout);
QHBoxLayout* lay = new QHBoxLayout;
QTabWidget* tabViewContainer = new QTabWidget(this);
tabViewContainer->setTabPosition(QTabWidget::TabPosition::North);
QTreeView* bookview = new QTreeView(this);
QListView* imgview = new QListView(this);
imgview->setDragEnabled(false);
imgview->setEditTriggers(QAbstractItemView::NoEditTriggers);
imgview->setSelectionBehavior(QAbstractItemView::SelectItems);
imgview->setSelectionMode(QAbstractItemView::SingleSelection);
connect(imgview,&QListView::clicked,this,[=](const QModelIndex& index){
int page = index.data(Qt::UserRole+1).toInt();
qDebug()<<"clicked page is"<<page;
mNav->jump(page,QPointF());
});
QListView *serchView = new QListView(this);
mSerachmodel = new QPdfSearchModel;
mSerachmodel->setDocument(doc);
serchView->setModel(mSerachmodel);
connect(serchView,&QListView::clicked,this,[&](const QModelIndex &index){
int page = mSerachmodel->data(index,int(QPdfSearchModel::Role::Page)).toInt();
qDebug()<<mSerachmodel->data(index,int(QPdfSearchModel::Role::IndexOnPage));
QPointF loc = mSerachmodel->data(index,int(QPdfSearchModel::Role::Location)).toPointF();
qDebug()<<mSerachmodel->data(index,int(QPdfSearchModel::Role::ContextBefore));
qDebug()<<mSerachmodel->data(index,int(QPdfSearchModel::Role::ContextAfter));
qDebug()<<mSerachmodel->data(index,int(QPdfSearchModel::Role::NRoles));
mNav->jump(page,loc);
});
tabViewContainer->addTab(bookview,"book");
tabViewContainer->addTab(imgview,"img");
tabViewContainer->addTab(serchView,"find");
QPdfBookmarkModel* bookmodel = new QPdfBookmarkModel;
bookmodel->setDocument(doc);
bookview->setModel(bookmodel);
connect(bookview,&QTreeView::doubleClicked,this,[=](const QModelIndex &index){
qDebug()<<"#######"<<bookmodel->data(index,int(QPdfBookmarkModel::Role::Title));
qDebug()<<"#######"<<bookmodel->data(index,int(QPdfBookmarkModel::Role::Level));
int jumpPage = bookmodel->data(index,int(QPdfBookmarkModel::Role::Page)).toInt();
QPointF jumppos = bookmodel->data(index,int(QPdfBookmarkModel::Role::Location)).toPointF();
qDebug()<<"#######"<<jumpPage;
qDebug()<<"#######"<<jumppos;
qDebug()<<"#######"<<bookmodel->data(index,int(QPdfBookmarkModel::Role::Zoom));
qDebug()<<"#######"<<bookmodel->data(index,int(QPdfBookmarkModel::Role::NRoles));
mNav->jump(jumpPage,jumppos);
mNav->update(jumpPage,jumppos,0);
});
lay->addWidget(tabViewContainer,1);
lay->addWidget(pdfview,3);
layout->addLayout(lay,1);
QHBoxLayout* pL = new QHBoxLayout();
pL->addWidget(ui->pushButton);
pL->addWidget(ui->pushButton_2);
layout->addLayout(pL,0);
QStandardItemModel *imgmodel = new QStandardItemModel;
imgview->setModel(imgmodel);
QListDelegate *delegate = new QListDelegate;
imgview->setItemDelegate(delegate);
imgview->setItemAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
connect(ui->pushButton,&QPushButton::clicked,this,[=](){
QString filePath = QFileDialog::getOpenFileName(this,"open PDF",QCoreApplication::applicationDirPath(),"*.pdf");
if(filePath.isEmpty()) return;
QPdfDocument::Error e = doc->load(filePath);
qDebug()<<"load result:"<<e;
int pagecount = doc->pageCount();
qDebug()<<"### pageCount is:"<<pagecount;
imgmodel->clear();
QFuture<void> fu = QtConcurrent::run([=,&imgmodel](){
for(int i=0;i<pagecount;i++){
// qDebug()<<doc->getAllText(i).text().simplified();
QImage image = doc->render(i,QSize(150,250));
QStandardItem* it = new QStandardItem;
it->setData(image,Qt::UserRole);
it->setData(i,Qt::UserRole+1);
imgmodel->setItem(i,it);
}
});
});
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if(pdfview->viewport() == watched && event->type()==QEvent::Wheel){
QWheelEvent* ev = static_cast<QWheelEvent*>(event);
if(ev->modifiers() == Qt::ControlModifier){
int dy = ev->angleDelta().y();
qreal cdelta = pdfview->zoomFactor();
if(dy>0) pdfview->setZoomFactor(cdelta*1.1);
else pdfview->setZoomFactor(cdelta*0.9);
}
}else if(pdfview == watched && event->type()==QEvent::KeyPress){
QKeyEvent* ev=static_cast<QKeyEvent*>(event);
if(ev->modifiers() == Qt::ControlModifier){
switch (ev->key()) {
case Qt::Key_F:{
qDebug()<<"Ctrl+F";
QString text = QInputDialog::getText(this,"查找","关键字");
if(!text.isEmpty()) mSerachmodel->setSearchString(text);
}
break;
case Qt::Key_Z:qDebug()<<"Ctrl+Z";
break;
case Qt::Key_Y:qDebug()<<"Ctrl+Y";
break;
case Qt::Key_S:qDebug()<<"Ctrl+S";
break;
default:
break;
}
}
}
return QMainWindow::eventFilter(watched,event);
}
qlistdelegate.h
#ifndef QLISTDELEGATE_H
#define QLISTDELEGATE_H
#include <QObject>
#include <QStyledItemDelegate>
#include <QStyleOptionViewItem>
#include <QDebug>
#include <QPainter>
#include <QBrush>
#include <QDebug>
class QListDelegate : public QStyledItemDelegate
{
Q_OBJECT
signals:
public:
explicit QListDelegate(QObject *parent = 0);
~QListDelegate();
//重写重画函数
void paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
const qreal radius = 10;
};
#endif // QLISTDELEGATE_H
qlistdelegate.cpp
#include "qlistdelegate.h"
QListDelegate::QListDelegate(QObject *parent) :
QStyledItemDelegate(parent)
{
}
QListDelegate::~QListDelegate()
{
}
void QListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.isValid())
{
painter->save();
#if 0
painter->setPen(QColor(200,200,200));
painter->setBrush(QBrush(QColor(200,200,200)));
painter->drawRect(option.rect);
QImage qimg = index.data(Qt::UserRole).value<QImage>();
painter->setRenderHints(QPainter::Antialiasing,true);
int sx = option.rect.x()+ ( option.rect.width() - qimg.width() ) /2 ;
int sy = option.rect.y()+ ( option.rect.height()- qimg.height()) /2 ;
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setBrush(QColor(255,255,255));
painter->drawImage(sx,sy,qimg);
#else
//绘制第一张图片
painter->setBrush(QColor(200, 200, 200));
painter->drawRect(option.rect);
QImage qimg = index.data(Qt::UserRole).value<QImage>();
int pageindex = index.data(Qt::UserRole+1).toInt();
int sx = option.rect.x()+ ( option.rect.width() - qimg.width() ) /2 ;
int sy = option.rect.y()+ ( option.rect.height()- qimg.height()-15 ) /2 ;
//绘制第二张图片
painter->setBrush(QColor(255, 255, 255));
painter->drawRect(sx, sy, qimg.width(), qimg.height());
//设置图片的重叠模式
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
//重叠图片
painter->drawImage(sx, sy, qimg);
#endif
painter->drawText(QRect(sx,sy + qimg.height()+5,qimg.width(),15), Qt::AlignCenter, QString("第%1页").arg(pageindex+1));
painter->restore();
}
}
QSize QListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index)
// return QSize(option.rect.width(), 40);
return QSize(200, 300);
}
QListDelegate这个类是为了给QListView提供视图的委托,以显示我们文档每页的缩略图,如果不需要缩略图,可以移除这部分代码
四、效果
1、书签目录
2、页缩略图
3、简介
点击打开按钮,选择你要打开的pdf文件即可,pushbutton没啥用,可移除
tab页签:
book为书签
img为缩略图
find为查找结果
find界面有点粗糙,你可以自行实现好看的界面去展示
最后,码字不易,作者原创文章,如需转载,请注明出处。