diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-12-16 12:11:13 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-12-16 12:11:13 +0100 |
commit | 61138411a87d1c155fcb33f1966387ebdeaae3e2 (patch) | |
tree | a9f3bd78249d942e26dc02a0e11119c7ae194248 | |
parent | 188d4a072bdca8facad1b1e0670ed440050384ae (diff) |
WIP: network loadingwip/network-loading
-rw-r--r-- | examples/widgets/pdfviewer/main.cpp | 2 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/mainwindow.cpp | 7 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/pagerenderer.cpp | 113 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/pagerenderer.h | 30 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/pdfviewer.pro | 2 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/sequentialpagewidget.cpp | 12 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/sequentialpagewidget.h | 1 | ||||
-rw-r--r-- | src/pdf/qpdfdocument.cpp | 25 | ||||
-rw-r--r-- | src/pdf/qpdfdocument.h | 2 | ||||
-rw-r--r-- | src/pdf/qpdfdocument_p.h | 2 |
10 files changed, 133 insertions, 63 deletions
diff --git a/examples/widgets/pdfviewer/main.cpp b/examples/widgets/pdfviewer/main.cpp index 51a3f98..271c25f 100644 --- a/examples/widgets/pdfviewer/main.cpp +++ b/examples/widgets/pdfviewer/main.cpp @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) QStringList args = a.arguments(); w.show(); if (args.length() > 1) - w.open(QUrl::fromLocalFile(args[1])); + w.open(QUrl::fromUserInput(args[1])); return a.exec(); } diff --git a/examples/widgets/pdfviewer/mainwindow.cpp b/examples/widgets/pdfviewer/mainwindow.cpp index f3fa000..0836012 100644 --- a/examples/widgets/pdfviewer/mainwindow.cpp +++ b/examples/widgets/pdfviewer/mainwindow.cpp @@ -45,12 +45,7 @@ MainWindow::~MainWindow() void MainWindow::open(const QUrl &docLocation) { - if (docLocation.isLocalFile()) - m_pageWidget->openDocument(docLocation); - else { - qCDebug(lcExample) << docLocation << "is not a valid local file"; - QMessageBox::critical(this, tr("Failed to open"), tr("%1 is not a valid local file").arg(docLocation.toString())); - } + m_pageWidget->openDocument(docLocation); qCDebug(lcExample) << docLocation; ui->scrollArea->ensureVisible(0, 0, 0, 0); } diff --git a/examples/widgets/pdfviewer/pagerenderer.cpp b/examples/widgets/pdfviewer/pagerenderer.cpp index fca4a0f..5270728 100644 --- a/examples/widgets/pdfviewer/pagerenderer.cpp +++ b/examples/widgets/pdfviewer/pagerenderer.cpp @@ -4,69 +4,104 @@ #include <QLoggingCategory> #include <QElapsedTimer> #include <QUrl> +#include <QNetworkRequest> +#include <QNetworkReply> Q_DECLARE_LOGGING_CATEGORY(lcExample) PageRenderer::PageRenderer() - : QThread(Q_NULLPTR) - , m_doc(new QPdfDocument(this)) - , m_page(0) - , m_zoom(1.) + : m_networkAccessManager(new QNetworkAccessManager(this)) , m_minRenderTime(1000000000.) , m_maxRenderTime(0.) , m_totalRenderTime(0.) , m_totalPagesRendered(0) { + qRegisterMetaType<QVector<QSizeF> >(); + + m_workerThread.start(); + moveToThread(&m_workerThread); + + connect(&m_doc, SIGNAL(documentLoadStarted()), this, SLOT(reportPageSizes())); + connect(&m_doc, SIGNAL(pageAvailable(int)), this, SLOT(renderPageIfRequested(int))); } PageRenderer::~PageRenderer() { + m_workerThread.exit(); } -QVector<QSizeF> PageRenderer::openDocument(const QUrl &location) +void PageRenderer::openDocument(const QUrl &location) { - if (location.isLocalFile()) - m_doc.load(location.toLocalFile()); - else { - qCWarning(lcExample, "non-local file loading is not implemented"); - return QVector<QSizeF>(); - } - // TODO maybe do in run() if it takes too long - QVector<QSizeF> pageSizes; - for (int page = 0; page < m_doc.pageCount(); ++page) - pageSizes.append(m_doc.pageSize(page)); - return pageSizes; + QMetaObject::invokeMethod(this, "loadDocumentImpl", Qt::QueuedConnection, Q_ARG(QUrl, location)); +} + +void PageRenderer::requestPage(int page, qreal zoom) +{ + QMetaObject::invokeMethod(this, "requestPageImpl", Qt::QueuedConnection, Q_ARG(int, page), Q_ARG(qreal, zoom)); +} + +void PageRenderer::reportPageSizes() +{ + QVector<QSizeF> sizes; + for (int i = 0, count = m_doc.pageCount(); i < count; ++i) + sizes << m_doc.pageSize(i); + emit pageSizesAvailable(sizes); + if (!m_renderRequests.isEmpty()) + QMetaObject::invokeMethod(this, "renderPages", Qt::QueuedConnection); } -void PageRenderer::requestPage(int page, qreal zoom, Priority priority) +void PageRenderer::loadDocumentImpl(const QUrl &url) { - // TODO maybe queue up the requests - m_page = page; - m_zoom = zoom; - start(priority); + m_renderRequests.clear(); + QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkRequestError())); + m_doc.load(reply); + connect(&m_doc, SIGNAL(documentLoadFinished()), reply, SLOT(deleteLater())); } -void PageRenderer::run() +void PageRenderer::requestPageImpl(int page, qreal zoom) { - renderPage(m_page, m_zoom); + RenderRequest newRequest; + newRequest.page = page; + newRequest.zoom = zoom; + m_renderRequests << newRequest; + if (!m_doc.isLoading()) + QMetaObject::invokeMethod(this, "renderPages", Qt::QueuedConnection); } -void PageRenderer::renderPage(int page, qreal zoom) +void PageRenderer::renderPages() { - QSizeF size = m_doc.pageSize(page) * m_zoom; - QElapsedTimer timer; timer.start(); - const QImage &img = m_doc.render(page, size); - qreal secs = timer.nsecsElapsed() / 1000000000.0; - if (secs < m_minRenderTime) - m_minRenderTime = secs; - if (secs > m_maxRenderTime) - m_maxRenderTime = secs; - m_totalRenderTime += secs; - ++m_totalPagesRendered; - emit pageReady(page, zoom, img); + for (int i = 0; i < m_renderRequests.count(); ++i) { + if (!m_doc.canRender(m_renderRequests.at(i).page)) + continue; + RenderRequest request = m_renderRequests.takeAt(i); - qCDebug(lcExample) << "page" << page << "zoom" << m_zoom << "size" << size << "in" << secs << - "secs; min" << m_minRenderTime << - "avg" << m_totalRenderTime / m_totalPagesRendered << - "max" << m_maxRenderTime; + QSizeF size = m_doc.pageSize(request.page) * request.zoom; + QElapsedTimer timer; timer.start(); + QImage img = m_doc.render(request.page, size); + qreal secs = timer.nsecsElapsed() / 1000000000.0; + if (secs < m_minRenderTime) + m_minRenderTime = secs; + if (secs > m_maxRenderTime) + m_maxRenderTime = secs; + m_totalRenderTime += secs; + ++m_totalPagesRendered; + emit pageReady(request.page, request.zoom, img); + + qCDebug(lcExample) << "page" << request.page << "zoom" << request.zoom << "size" << size << "in" << secs << + "secs; min" << m_minRenderTime << + "avg" << m_totalRenderTime / m_totalPagesRendered << + "max" << m_maxRenderTime; + break; + } +} + +void PageRenderer::renderPageIfRequested(int page) +{ + renderPages(); +} + +void PageRenderer::handleNetworkRequestError() +{ + qDebug() << static_cast<QNetworkReply*>(sender())->errorString(); } diff --git a/examples/widgets/pdfviewer/pagerenderer.h b/examples/widgets/pdfviewer/pagerenderer.h index 35c8939..a35bc74 100644 --- a/examples/widgets/pdfviewer/pagerenderer.h +++ b/examples/widgets/pdfviewer/pagerenderer.h @@ -7,10 +7,11 @@ #include <QRunnable> #include <QThread> #include <QPdfDocument> +#include <QNetworkAccessManager> class QPdfDocument; -class PageRenderer : public QThread +class PageRenderer : public QObject { Q_OBJECT public: @@ -18,24 +19,31 @@ public: ~PageRenderer(); public slots: - QVector<QSizeF> openDocument(const QUrl &location); - void requestPage(int page, qreal zoom, Priority priority = QThread::NormalPriority); + void openDocument(const QUrl &location); + void requestPage(int page, qreal zoom); signals: + void pageSizesAvailable(const QVector<QSizeF> &sizes); void pageReady(int page, qreal zoom, QImage image); -protected: - void run() Q_DECL_OVERRIDE; - -private: - void renderPage(int page, qreal zoom); +private slots: + void reportPageSizes(); + void loadDocumentImpl(const QUrl &url); + void requestPageImpl(int page, qreal zoom); + void renderPages(); + void renderPageIfRequested(int page); + void handleNetworkRequestError(); private: + QThread m_workerThread; QPdfDocument m_doc; - // current request only - int m_page; - qreal m_zoom; + struct RenderRequest { + int page; + qreal zoom; + }; + QVector<RenderRequest> m_renderRequests; + QNetworkAccessManager *m_networkAccessManager; // performance statistics qreal m_minRenderTime; diff --git a/examples/widgets/pdfviewer/pdfviewer.pro b/examples/widgets/pdfviewer/pdfviewer.pro index e8269c4..9107983 100644 --- a/examples/widgets/pdfviewer/pdfviewer.pro +++ b/examples/widgets/pdfviewer/pdfviewer.pro @@ -1,4 +1,4 @@ -QT += core gui widgets pdf +QT += core gui widgets pdf network TARGET = pdfviewer TEMPLATE = app diff --git a/examples/widgets/pdfviewer/sequentialpagewidget.cpp b/examples/widgets/pdfviewer/sequentialpagewidget.cpp index c85f784..4073302 100644 --- a/examples/widgets/pdfviewer/sequentialpagewidget.cpp +++ b/examples/widgets/pdfviewer/sequentialpagewidget.cpp @@ -21,6 +21,7 @@ SequentialPageWidget::SequentialPageWidget(QWidget *parent) , m_zoom(1.) , m_screenResolution(QGuiApplication::primaryScreen()->logicalDotsPerInch() / 72.0) { + connect(m_pageRenderer, SIGNAL(pageSizesAvailable(QVector<QSizeF>)), this, SLOT(preparePages(QVector<QSizeF>))); connect(m_pageRenderer, SIGNAL(pageReady(int, qreal, QImage)), this, SLOT(pageLoaded(int, qreal, QImage)), Qt::QueuedConnection); } @@ -31,9 +32,7 @@ SequentialPageWidget::~SequentialPageWidget() void SequentialPageWidget::openDocument(const QUrl &url) { - m_pageSizes = m_pageRenderer->openDocument(url); - m_topPageShowing = 0; - invalidate(); + m_pageRenderer->openDocument(url); } void SequentialPageWidget::setZoom(qreal factor) @@ -66,6 +65,13 @@ void SequentialPageWidget::invalidate() update(); } +void SequentialPageWidget::preparePages(const QVector<QSizeF> &pageSizes) +{ + m_pageSizes = pageSizes; + m_topPageShowing = 0; + invalidate(); +} + void SequentialPageWidget::pageLoaded(int page, qreal zoom, QImage image) { Q_UNUSED(zoom) diff --git a/examples/widgets/pdfviewer/sequentialpagewidget.h b/examples/widgets/pdfviewer/sequentialpagewidget.h index 2a3025d..edf7d53 100644 --- a/examples/widgets/pdfviewer/sequentialpagewidget.h +++ b/examples/widgets/pdfviewer/sequentialpagewidget.h @@ -29,6 +29,7 @@ signals: void zoomChanged(qreal factor); private slots: + void preparePages(const QVector<QSizeF> &pageSizes); void pageLoaded(int page, qreal zoom, QImage image); private: diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp index b48bbda..4d7990d 100644 --- a/src/pdf/qpdfdocument.cpp +++ b/src/pdf/qpdfdocument.cpp @@ -57,6 +57,7 @@ void QPdfDocumentPrivate::clear() avail = 0; loadComplete = false; + reportedAvailablePages.clear(); asyncBuffer.close(); asyncBuffer.setData(QByteArray()); @@ -169,8 +170,11 @@ void QPdfDocumentPrivate::tryLoadDocument() updateLastError(); if (lastError == QPdfDocument::IncorrectPasswordError) emit q->passwordRequired(); - else if (doc) + else if (doc) { emit q->documentLoadStarted(); + reportedAvailablePages.resize(FPDF_GetPageCount(doc)); + reportedAvailablePages.fill(false); + } } void QPdfDocumentPrivate::checkComplete() @@ -187,8 +191,14 @@ void QPdfDocumentPrivate::checkComplete() loadComplete = true; for (int i = 0, count = FPDF_GetPageCount(doc); i < count; ++i) - if (!FPDFAvail_IsPageAvail(avail, i, this)) + if (FPDFAvail_IsPageAvail(avail, i, this)) { + if (!reportedAvailablePages.at(i)) { + reportedAvailablePages[i] = true; + emit q->pageAvailable(i); + } + } else { loadComplete = false; + } if (loadComplete) emit q->documentLoadFinished(); } @@ -285,6 +295,14 @@ QSizeF QPdfDocument::pageSize(int page) const return result; } +bool QPdfDocument::canRender(int page) const +{ + QMutexLocker lock(pdfMutex()); + if (!d->doc) + return false; + return d->reportedAvailablePages.at(page); +} + QImage QPdfDocument::render(int page, const QSizeF &pageSize) { if (!d->doc) @@ -292,6 +310,9 @@ QImage QPdfDocument::render(int page, const QSizeF &pageSize) QMutexLocker lock(pdfMutex()); + if (!FPDFAvail_IsPageAvail(d->avail, page, d.data())) + return QImage(); + FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); if (!pdfPage) return QImage(); diff --git a/src/pdf/qpdfdocument.h b/src/pdf/qpdfdocument.h index 6459f39..c846333 100644 --- a/src/pdf/qpdfdocument.h +++ b/src/pdf/qpdfdocument.h @@ -44,11 +44,13 @@ public: QSizeF pageSize(int page) const; + bool canRender(int page) const; QImage render(int page, const QSizeF &pageSize); Q_SIGNALS: void passwordRequired(); void documentLoadStarted(); + void pageAvailable(int page); void documentLoadFinished(); void pageCountChanged(); diff --git a/src/pdf/qpdfdocument_p.h b/src/pdf/qpdfdocument_p.h index 7850c65..2e19fb0 100644 --- a/src/pdf/qpdfdocument_p.h +++ b/src/pdf/qpdfdocument_p.h @@ -9,6 +9,7 @@ #include <qbuffer.h> #include <qnetworkreply.h> #include <qpointer.h> +#include <qbitarray.h> QT_BEGIN_NAMESPACE @@ -23,6 +24,7 @@ public: FPDF_AVAIL avail; FPDF_DOCUMENT doc; bool loadComplete; + QBitArray reportedAvailablePages; QPointer<QIODevice> device; QScopedPointer<QIODevice> ownDevice; |