QT使用Poppler库来完成阅读PDF文件

采用异步加载来实现pdf的阅读会大效率的来加载pdf文件,使得加载的过程变得快速。缺点是加载的界面顺序是乱的,因此需要在加载到窗口的时候先排个序。此过程会浪费大约0.6s的时间。使用时需要导入poppler库

实现阅读器的窗口:

PdfReaderWidget::PdfReaderWidget(QWidget *parent)
    : QWidget(parent), m_document(nullptr), m_completedThreads(0)
{
    setAutoFillBackground(true);
    m_threadPool = QThreadPool::globalInstance();
    m_layout = new QVBoxLayout();
    m_scrollArea = new QScrollArea();
    m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    m_scrollArea->setWidgetResizable(true);
    QWidget* scrollAreaContent = new QWidget(this);
    QVBoxLayout *scrollAreaLayout = new QVBoxLayout(scrollAreaContent);
    scrollAreaLayout->setAlignment(Qt::AlignCenter);
    m_scrollArea->setWidget(scrollAreaContent);
    m_scrollArea->viewport()->installEventFilter(this);
    m_layout->addWidget(m_scrollArea);
    setLayout(m_layout);
}

PdfReaderWidget::~PdfReaderWidget()
{
    m_threadPool->clear();  // 清除所有任务
    m_threadPool->waitForDone();  // 等待所有线程完成
    delete m_document;
    m_document = nullptr;
    delete m_layout;
    m_layout = nullptr;
    delete m_scrollArea;
    m_scrollArea = nullptr;
}


h:
#ifndef PDFREADERWIDGET_H
#define PDFREADERWIDGET_H
#include "qmutex.h"
#include <QQueue>
#include <QThreadPool>
#include <QWidget>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QLabel>
#include <poppler-qt5.h>

class PdfReaderWidget : public QWidget
{
    Q_OBJECT

    struct PageData {
        int pageNum;
        QImage image;
        QLabel* label;
    };

public:
    explicit PdfReaderWidget(QWidget *parent = nullptr);
    ~PdfReaderWidget();
    void loadPdf(const QString& filePath);
    void loadVisiblePages();

protected:
    bool eventFilter(QObject* obj, QEvent* event) override;
    void onPageLoaded(int pageNum, QImage image);
    void downloadPdf();
    void closeEvent(QCloseEvent *event)
    {
        m_threadPool->clear();  // 清除所有任务
        m_threadPool->waitForDone();  // 等待所有线程完成
        delete m_document;
        m_document = nullptr;
        for (auto it = m_pages.begin(); it != m_pages.end(); ++it) {
            if(it.value())
            {
                delete it.value();
                it.value() = nullptr;
            }
        }
        //qDeleteAll(m_pages);
        m_pages.clear();
        QWidget::closeEvent(event);
    }

private:
    QVBoxLayout* m_layout;
    QScrollArea* m_scrollArea;
    QString m_filePath;
    Poppler::Document* m_document;
    QThreadPool* m_threadPool;
    QMutex m_mutex;
    QMap<int, QLabel*> m_pages;
    int m_completedThreads;
    QQueue<PageData> m_pageQueue;
    void processPageQueue();

signals:
    void PdfClose();
};


#endif // PDFREADERWIDGET_H


CPP:
#include "pdfreaderwidget.h"
#include "usbthread.h"
#include <QTimer>
#include <QApplication>
#include <QScrollBar>
#include <QMessageBox>
#include <QThreadPool>
#include <QVector>
#include <QDebug>
#include <QFile>
PdfReaderWidget::PdfReaderWidget(QWidget *parent)
    : QWidget(parent), m_document(nullptr), m_completedThreads(0)
{
    setAutoFillBackground(true);
    m_threadPool = QThreadPool::globalInstance();
    m_layout = new QVBoxLayout();
    m_scrollArea = new QScrollArea();
    m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    m_scrollArea->setWidgetResizable(true);
    QWidget* scrollAreaContent = new QWidget(this);
    QVBoxLayout *scrollAreaLayout = new QVBoxLayout(scrollAreaContent);
    scrollAreaLayout->setAlignment(Qt::AlignCenter);
    m_scrollArea->setWidget(scrollAreaContent);
    m_scrollArea->viewport()->installEventFilter(this);
    m_layout->addWidget(m_scrollArea);
    setLayout(m_layout);
}

PdfReaderWidget::~PdfReaderWidget()
{
    m_threadPool->clear();  // 清除所有任务
    m_threadPool->waitForDone();  // 等待所有线程完成
    delete m_document;
    m_document = nullptr;
    delete m_layout;
    m_layout = nullptr;
    delete m_scrollArea;
    m_scrollArea = nullptr;
}


void PdfReaderWidget::loadPdf(const QString& filePath)
{
    QFile file(filePath);
    if (!file.exists()) {
        QMessageBox::warning(nullptr, tr("Error"), tr("PDF file not found."));
        return;
    }

    delete m_document;
    m_document = Poppler::Document::load(filePath);
    if (!m_document || m_document->isLocked()) {
        QMessageBox::warning(this, tr("Error"), tr("Unable to open the PDF."));
        return;
    }

    QFont font = QApplication::font();
    font.setHintingPreference(QFont::PreferFullHinting);
    font.setStyleStrategy(QFont::PreferQuality);
    QApplication::setFont(font);

    m_document->setRenderHint(Poppler::Document::Antialiasing);
    m_document->setRenderHint(Poppler::Document::TextAntialiasing);

    int m_pageHeight = 0;
    int m_pageWidth = 0;

    for (int i = 0; i < m_document->numPages(); ++i) {
        Poppler::Page* pdfPage = m_document->page(i);
        m_pageWidth = qMax(m_pageWidth, pdfPage->pageSize().width());
        m_pageHeight += pdfPage->pageSize().height();
        delete pdfPage;
    }

    int scrollAreaHeight = m_scrollArea->viewport()->height();
    m_scrollArea->setMinimumSize(1000, scrollAreaHeight);

    QScrollBar *scrollBar = m_scrollArea->verticalScrollBar();
    scrollBar->setPageStep(m_scrollArea->viewport()->height());

    int scrollBarMax = m_pageHeight - m_scrollArea->viewport()->height();
    scrollBar->setRange(0, scrollBarMax);

    loadVisiblePages();
}
void PdfReaderWidget::loadVisiblePages()
{
    QTimer::singleShot(100, this, [this] {
        if (!this->isVisible()) return;  // 确保窗口仍然可见

        int viewportHeight = m_scrollArea->viewport()->rect().height();
        int scrollPosition = m_scrollArea->verticalScrollBar()->value();

        int startPage = qMax(0, (scrollPosition * m_document->numPages()) / viewportHeight);
        int endPage = qMin(m_document->numPages() - 1, ((scrollPosition + viewportHeight) * m_document->numPages()) / viewportHeight);

        QLayoutItem* child;
        while ((child = m_scrollArea->widget()->layout()->takeAt(0)) != nullptr) {
            delete child->widget();
            delete child;
        }
        for (int i = startPage; i <= endPage; ++i) {
            QLabel* label = new QLabel;
            label->setMinimumSize(1, 1);
            {
                QMutexLocker locker(&m_mutex);
                m_pages.insert(i, label);
                m_scrollArea->widget()->layout()->addWidget(label);
            }

            PdfLoadThread* loadThread = new PdfLoadThread(m_document, i);
            loadThread->setProperty("pageNum", i);
            connect(loadThread, &PdfLoadThread::pageLoaded, this, [this](int pageNum, QImage image) {
                onPageLoaded(pageNum, image);
            });

            m_threadPool->start(loadThread);
        }
    });
}

void PdfReaderWidget::onPageLoaded(int pageNum, QImage image)
{
    QMutexLocker locker(&m_mutex);

    if (m_pages.contains(pageNum)) {
        QLabel* pageLabel = m_pages.value(pageNum);
        pageLabel->setPixmap(QPixmap::fromImage(image));
        pageLabel->setMinimumSize(image.size());
        if (pageNum == 0) {
            pageLabel->setVisible(true);
        } else {
            pageLabel->setVisible(false);
        }
    }

    m_completedThreads++;
    if (m_completedThreads == m_pages.size()) {
        m_completedThreads = 0;
        for (auto pageLabel : m_pages) {
            pageLabel->setVisible(true);
        }
    }
}




bool PdfReaderWidget::eventFilter(QObject* obj, QEvent* event)
{
    if (obj == m_scrollArea->viewport() && event->type() == QEvent::Paint)
    {
        loadVisiblePages();
    }
    return QObject::eventFilter(obj, event);
}




thread.h:
class PdfLoadThread : public QObject , public QRunnable
{
    Q_OBJECT
public:
    PdfLoadThread(Poppler::Document* document, int pageNum, QObject* parent=nullptr)
        : QObject(parent), QRunnable(), m_document(document), m_pageNum(pageNum) {}

    void run()
    {
        Poppler::Page* pdfPage = m_document->page(m_pageNum);
        if (!pdfPage) {
            return;
        }
        QImage image = pdfPage->renderToImage(195, 195, -1, -1, -1, -1, Poppler::Page::Rotation::Rotate0);//可更改195, 数值越大,渲染率越高
        delete pdfPage;
        emit pageLoaded(m_pageNum, image);
    }

signals:
    void pageLoaded(int pageNum, QImage image);

private:
    Poppler::Document* m_document;
    int m_pageNum;
};

效果图如下:

//打开PDF窗口:
    QString exePath = QCoreApplication::applicationDirPath();
    QString filePath = exePath +QDir::separator()+ your_PDfName;
    if (filePath.isEmpty()) {
        return;
    }
    pdfReader = new PdfReaderWidget;
    pdfReader->loadPdf(filePath);
    QString tittle = tr("电子说明书");
    pdfReader->setWindowTitle(tittle);
    pdfReader->setWindowIcon(QIcon(":/img/2.png"));
    pdfReader->setAttribute(Qt::WA_DeleteOnClose);
    pdfReader->show()

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Poppler是一个用于解析PDF文档的开源,它提供了一系列的API函数供开发使用。而Qt是一个跨平台的应用程序开发框架,可以用于开发图形界面应用程序。当我们使用Linux系统,并借助Qt框架进行应用程序开发时,可以使用Poppler实现PDF阅读器的功能。 使用Poppler,我们可以将PDF文档解析为一系列的页面,然后在Qt的界面上进行显示和操作。通过Qt的绘图功能,可以将PDF页面渲染为图像,并在界面中显示。同时,我们也可以使用Poppler提供的API函数,实现对PDF文档的页面切换、缩放、搜索等操作。 在实现PDF阅读器功能时,我们首先需要在Qt项目中引入Poppler。然后,通过调用Poppler提供的函数,加载PDF文档,获取并渲染PDF页面,并显示在Qt的界面上。可以通过Qt提供的滚动区域和按钮等控件,实现页面切换的功能。通过输入框等控件,实现PDF文档的搜索功能。另外,可以添加缩放功能,使用户能够放大或缩小PDF页面。 使用Linux系统并结合QtPoppler实现PDF阅读器,可以提供功能丰富、易用的界面,使用户可以方便地阅读和操作PDF文档。同时,开放源代码的特性也使得我们可以根据实际需求对PDF阅读器进行二次开发和定制,满足不同用户的需求。 ### 回答2: Linux Qt 是一个开源的跨平台的应用程序开发框架,而 Poppler 是一个广泛用于处理 PDF 文件。通过结合使用 Linux QtPoppler,可以实现功能强大的 PDF 阅读器。 Poppler 是在 Xpdf 项目的基础上发展而来的,它提供了各种用于解析和渲染 PDF 文件的函数和工具。在 Linux Qt使用 Poppler,可以方便地实现对 PDF 文件的加载、解析和渲染。 首先,我们需要在 Linux Qt 项目中引入 Poppler 。可以通过在项目的配置文件中添加相应的依赖项来实现。然后,在代码中使用 Poppler 的接口来读取和处理 PDF 文件。 通过调用 Poppler 提供的函数,我们可以实现以下功能: 1. 加载 PDF 文件使用 Poppler 的函数,可以从文件系统中加载选定的 PDF 文件,并将其读取到内存中进行处理。 2. 解析 PDF 内容:Poppler 提供了解析 PDF 文档的功能,可以分析文件的结构、页面、文本和图像等内容。通过解析 PDF 文档,可以实现对页面、文字和图像的访问和处理。 3. 渲染 PDF 页面:Poppler 支持将 PDF 页面渲染为 QImage 或者 QPixmap 对象,以便在 Linux Qt 中显示和操作。通过渲染页面,可以实现对 PDF 页面的放大、缩小、旋转和平移等操作。 4. 实现用户交互:通过 Linux Qt 提供的用户界面组件,可以实现对 PDF 阅读器的用户交互功能,比如页面滚动、缩放、搜索和标注等。同时,结合 Poppler 提供的函数,可以实现对 PDF 内容的选择、复制和粘贴等操作。 总之,通过结合使用 Linux QtPoppler,我们可以方便地实现功能丰富的 PDF 阅读器。Linux Qt 提供了丰富的界面交互组件,而 Poppler 则提供了强大的 PDF 处理功能,这使得我们能够开发出易用性高、功能强大的 PDF 阅读器应用程序。 ### 回答3: Linux Qt 使用 Poppler 实现 PDF 阅读器可以通过以下步骤进行: 1. 首先,需要安装 Qt 并设置开发环境。Qt 是一个跨平台的应用程序开发框架,可以用于构建图形用户界面和应用程序。可以从 Qt 官网下载并安装 Qt。 2. 接下来,需要安装 Poppler Poppler 是一个开源的 PDF 渲染,可以用于提取和渲染 PDF 文件的内容。可以通过 package manager(例如 apt-get 或 yum)在 Linux 中安装 Poppler 。 3. 打开 Qt 开发环境,创建一个新的项目。可以选择一个基本的 Qt Widgets 应用程序模板。 4. 导入 Poppler 的头文件和链接。在 Qt 项目的.pro 文件中添加对应的头文件路径和链接名。例如: ``` INCLUDEPATH += /usr/include/poppler LIBS += -lpoppler-qt5 ``` 5. 创建一个 QWidget 作为 PDF 阅读器的主窗口。可以使用 QWidget 或 QMainWindow 类作为主窗口,并添加显示 PDF 页面的组件(例如 QScrollArea 或 QPainter)。 6. 在窗口中创建一个按钮或菜单项,用于打开 PDF 文件。当用户点击该按钮时,可以调用 Poppler 库来加载和解析 PDF 文件,并将每个页面渲染到 QWidget 中的组件上。 7. 实现页面导航功能。可以添加翻页按钮或滚动条来切换显示不同的 PDF 页面。在按钮点击事件或滚动条值改变事件中,调用 Poppler 库来渲染并显示相应的页面。 8. 可以进一步实现搜索、缩放、打印等功能,通过调用 Poppler 提供的相应函数来实现。 9. 最后,构建和运行项目。可以使用 Qt Creator 进行构建和调试。在 Linux 终端中导航到项目目录,并使用 qmake 来生成 Makefile,然后使用 make 命令编译项目。 通过以上步骤,我们可以在 Linux Qt 上实现一个简单的 PDF 阅读器,并使用 Poppler 库来加载、解析和渲染 PDF 文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值