View PDF in QML WebView - qt

I would like to view a PDF file in a WebView created in my QML code. I am importing QtWebView 1.1 and setting the url property to the path of the PDF file but I am getting this error:
[13044:12820:0314/144814.854:ERROR:in_progress_cache_impl.cc(192)]
Cache is not initialized, cannot RetrieveEntry.
[13044:12820:0314/144814.854:ERROR:in_progress_cache_impl.cc(176)]
Cache is not initialized, cannot AddOrReplaceEntry.
[13044:12820:0314/144814.854:ERROR:in_progress_cache_impl.cc(192)]
Cache is not initialized, cannot RetrieveEntry.
When I use the same code to view an image it works. This question: Display PDF file with QWebView is close to what I want but QML does not seem to give me access to the settings method the way that C++ does (WebView docs). Is there some other way to do this?

It seems that you are confusing elements, QWebView belongs to QtWebkit (uses Webkit) that no longer exists in Qt and that was replaced by Qt WebEngine (uses chromium). And another thing is WebView of Qt WebView that uses the native APIs (for example Android does not support Qt WebEngine but if WebView).
Qt WebEngine and Qt WebView does not support PDF visualization (Qt WenEngine will support it very soon) natively so a solution is to use some js library that does it as PDF.js so that is the alternative that I propose in base to an old answer.
*.pro
QT += quick webview
CONFIG += c++11
SOURCES += main.cpp
RESOURCES += qml.qrc
COPY_CONFIG = 3rdParty example.pdf
copy_cmd.input = COPY_CONFIG
copy_cmd.output = ${QMAKE_FILE_IN_BASE}${QMAKE_FILE_EXT}
copy_cmd.commands = $$QMAKE_COPY_DIR ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
copy_cmd.CONFIG += no_link_no_clean
copy_cmd.variable_out = PRE_TARGETDEPS
QMAKE_EXTRA_COMPILERS += copy_cmd
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtWebView>
class PDFJS: public QObject
{
Q_OBJECT
Q_PROPERTY(QString version READ version WRITE setVersion NOTIFY versionChanged)
QString m_version;
public:
QString version() const{
return m_version;
}
void setVersion(QString version){
if (m_version == version)
return;
m_version = version;
Q_EMIT versionChanged(m_version);
}
Q_SIGNAL void versionChanged(QString version);
Q_INVOKABLE QUrl getUrl(const QUrl & path){
QString pdfjs_path = QDir::current().filePath(QString("3rdParty/pdfjs-%1-dist/web/viewer.html").arg(m_version));
QUrl pdf_url = QUrl::fromLocalFile(pdfjs_path);
QUrlQuery query;
query.addQueryItem("file", path.toString());
pdf_url.setQuery(query);
return pdf_url;
}
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QtWebView::initialize();
QQmlApplicationEngine engine;
PDFJS pdfjs;
engine.rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath());
engine.rootContext()->setContextProperty("PDFJS", &pdfjs);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtWebView 1.1
Window {
visible: true
width: 640
height: 480
title: qsTr("PDFJS Example")
WebView{
id: webview
anchors.fill: parent
}
Component.onCompleted:{
PDFJS.version = "2.1.266"
webview.url = PDFJS.getUrl("file://" + applicationDirPath + "/example.pdf")
}
}
You can find the complete project here
Update: only QML
.
|-- 3rdParty
| `-- pdfjs-2.1.266-dist
|-- example.pdf
`-- main.qml
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtWebView 1.1
Window {
visible: true
width: 640
height: 480
title: qsTr("PDFJS Example")
WebView{
id: webview
anchors.fill: parent
}
Component.onCompleted:{
var pdfjs_path = Qt.resolvedUrl("3rdParty/pdfjs-2.1.266-dist/web/viewer.html")
var path = Qt.resolvedUrl("example.pdf");
var url = pdfjs_path + "?file=%1".arg(path)
console.log(url)
webview.url = url
}
}
This part of the project you find here

Another option to this problem is to look into the Poppler Library. This library can be used to convert a page in a PDF to an image. Take a look into the Poppler::Page object's renderToImage. The image can then be displayed on a QQuickPaintedItem. Hope this helps someone.

Related

Why Qt Quick Application couldn't dynamically create a control by the Method Qt.createComponent

In my Qt Quick Application, I wanna use Qt.createComponent to create component dynamically——the effect I expect: when I click the button"create checkbox" ,the window will create a CheckBox dymamically
I made it in a Qt Quick UI prototype(qml-only project),but failed in the Qt Quick Application
After running the app, I click the button"create checkbox" many times, but the window has no response and the program stuck
// app.pro
QT += quick
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Refer to the documentation for the
# deprecated API to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
//main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
// CheckBox.qml
import QtQuick 2.0
import QtQuick.Controls 2.12
CheckBox{
text: "checkcheck"
}
//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id:root
visible: true
width: 200
height: 200
title: qsTr("Hello World")
Button{
text: "create checkbox"
anchors.centerIn:parent
onClicked: loadCheckBox(root)
}
function loadCheckBox(parent){
var obj=Qt.createComponent("CheckBox.qml");
if(obj.status==Component.Ready){
var checkBox=obj.createObject(parent)
}
}
}
My environment: Win10 QtCreator Qt5.13.2_MSVC2017_64bit
Anyone knows how to modify it?

Qt qml crash if QQuickWidget is created inside a QWidget

There's a strange issue that might as well be a bug, but before posting that bug I want to assure it first - or find my own mistake. Basically I want to create a QWidget-subclass that contains, amongst other subwidgets, a QQuickWidget that loads a qml file.
I could boil the crash down to the minimal version below. The crash only occurs
if the qml actually sets an url or calls loadhtml AND
if the QQuickWidget is created inside the Viewer`s constructor.
It doesn't even make a difference if the QQuickWidget is initialized without a parent.
To reproduce, run the app and close it then via the green icon in the system tray.
crashtest.pro
QT += core gui widgets qml quickwidgets webview
TARGET = webviewcrash
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
CONFIG += qml_debug
SOURCES += main.cpp
RESOURCES += res.qrc
main.cpp:
#include <QApplication>
#include <QMenu>
#include <QSystemTrayIcon>
#include <QtWebView>
#include <QWebSocketServer>
#include <QQuickWidget>
#include <qboxlayout.h>
#include <qqmlapplicationengine.h>
#include <QQuickView>
#include <QtQml/QQmlContext>
#include <QDebug>
class Viewer : public QWidget
{
Q_OBJECT
public:
explicit Viewer(QWidget *parent = nullptr) : QWidget(parent){
setLayout(new QVBoxLayout);
}
void init(QQuickWidget* viewer){
if ( viewer ){
this->layout()->addWidget(viewer);
}
else{
QQuickWidget* viewer = new QQuickWidget(this);
viewer->setSource(QUrl("qrc:/viewer.qml"));
viewer->setResizeMode(QQuickWidget::SizeRootObjectToView);
this->layout()->addWidget(viewer);
}
}
};
#include "main.moc"
#define TRY_VIEWER_INIT_WITH_POINTER 0 // Crash
#define TRY_VIEWER_INIT_WITH_REFERENCE 0 // OK
#define TRY_VIEWER_INIT_INSIDE_VIEWER 0 // Crash
#define TRY_STAND_ALONE 0 // Crash
#define TRY_WITHOUT_QQUICKWIDGET 0 // OK - but this is a qml ApplicationWindow, not an Item.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtWebView::initialize();
#if TRY_VIEWER_INIT_WITH_POINTER
auto v1 = new Viewer();
v1->resize(500,300);
v1->move(100,100);
v1->show();
QQuickWidget *v1_viewer = new QQuickWidget;
v1_viewer->setResizeMode(QQuickWidget::SizeRootObjectToView);
v1_viewer->setSource(QUrl("qrc:/viewer.qml"));
v1->init(v1_viewer);
#endif
#if TRY_VIEWER_INIT_WITH_REFERENCE
auto v1 = new Viewer();
v1->resize(500,300);
v1->move(100,100);
v1->show();
QQuickWidget v1_viewer;
v1_viewer.setResizeMode(QQuickWidget::SizeRootObjectToView);
v1_viewer.setSource(QUrl("qrc:/viewer.qml"));
v1->init(&v1_viewer);
#endif
#if TRY_VIEWER_INIT_INSIDE_VIEWER
auto v1 = new Viewer();
v1->resize(500,300);
v1->move(100,100);
v1->show();
v1->init(nullptr);
#endif
#if TRY_STAND_ALONE
QQuickWidget viewer;
viewer.setSource(QUrl("qrc:/viewer.qml"));
viewer.setResizeMode(QQuickWidget::SizeRootObjectToView);
viewer.show();
#endif
#if TRY_WITHOUT_QQUICKWIDGET
QQmlApplicationEngine engine;
engine.load(QUrl("qrc:/viewer2.qml"));
if (engine.rootObjects().isEmpty())
return -1;
#endif
// MENU
QMenu trayMenu;
QSystemTrayIcon tray;
trayMenu.addAction("Exit",[&](){
qApp->quit();
});
tray.setContextMenu(&trayMenu);
QPixmap pix(32,32);
pix.fill(QColor(Qt::green));
tray.setIcon(QIcon(pix));
return a.exec();
}
viewer.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtWebView 1.1
Item {
WebView{
id: webView
anchors.fill: parent
// Crash on app exit, if you uncomment this line:
//url: "https://www.qt.io"
}
Component.onCompleted: {
// Crash on app exit, if you uncomment this line:
webView.loadHtml("<html><head></head><body>Simple body</body></html>","")
}
}
viewer2.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtWebView 1.1
ApplicationWindow {
visible: true
x: 100
y: 100
width: 500
height: 400
WebView{
id: webView
anchors.fill: parent
// Crash on app exit, if you uncomment this line:
//url: "https://www.qt.io"
}
Component.onCompleted: {
// Crash on app exit, if you uncomment this line:
webView.loadHtml("<html><head></head><body>Simple body</body></html>","")
}
}
I am using Qt5.12.0 on a Win7 desktop, and the used compiler is MSVC2017 32 bit.
Here is the Backtrace:
[11328:19036:0223/150952.613:FATAL:render_process_host_impl.cc(887)] Check failed: map_.empty().
Backtrace:
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11B4E857+810583]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11B5BD11+865041]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11A93A9E+45214]
QtWebEngineCore::JavaScriptDialogController::qt_static_metacall [0x1166ED0A+32480538]
QtWebEngineCore::JavaScriptDialogController::qt_static_metacall [0x1167185F+32491631]
GetHandleVerifier [0x12ED8CE9+20263193]
IsSandboxedProcess [0x1403328B+16485723]
IsSandboxedProcess [0x13A93802+10588882]
IsSandboxedProcess [0x1406445F+16686895]
IsSandboxedProcess [0x14063EDD+16685485]
QtWebEngineCore::JavaScriptDialogController::qt_static_metacall [0x10406C6D+13180029]
QtWebEngineCore::JavaScriptDialogController::qt_static_metacall [0x10406C41+13179985]
IsSandboxedProcess [0x13D95555+13742117]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11AC020E+227342]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11AC0148+227144]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11ABF94F+225103]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11ABF96F+225135]
QWebEngineUrlSchemeHandler::_q_destroyedUrlSchemeHandler [0x11ABFA2C+225324]
QtWebEngineCore::JavaScriptDialogController::qt_static_metacall [0x1115CC69+27163769]
QtWebEngineCore::ProfileAdapterClient::downloadInterruptReasonToString [0x0F70855F+2623]
QtWebEngineCore::ProfileAdapterClient::downloadInterruptReasonToString [0x0F7086A6+2950]
QtWebEngineCore::ProfileAdapterClient::downloadInterruptReasonToString [0x0F70888F+3439]
QtWebEngineCore::ProfileAdapter::checkPermission [0x0F705B56+230]
QtWebEngineCore::FilePickerController::mode [0x0F71EDBA+33066]
QtWebEngineCore::ProfileAdapter::~ProfileAdapter [0x0F7054E6+246]
QtWebEngineCore::ProfileAdapter::~ProfileAdapter [0x0F7055DD+493]
QtWebEngineCore::WebEngineSettings::setWebContentsAdapter [0x0F69BCEA+3418]
QtWebEngineCore::WebContentsAdapter::requestedUrl [0x0F741E5A+746]
QWebEngineUrlScheme::operator!= [0x0F74BB8B+3707]
QWebEngineUrlScheme::operator!= [0x0F74BCB7+4007]
QTextCodec::codecForHtml [0x5F98A3C2+3390559]
QTreeViewPrivate::layout [0x609525D6+456449]
main [0x001F6D24+628] (d:\anonymous\qml-user-js-crash\userjs\main.cpp:67)
WinMain [0x001F664D+173] (c:\users\qt\work\qt\qtbase\src\winmain\qtmain_win.cpp:104)
invoke_main [0x001F50AE+30] (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:107)
__scrt_common_main_seh [0x001F4F47+343] (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
__scrt_common_main [0x001F4DDD+13] (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:331)
WinMainCRTStartup [0x001F5128+8] (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_winmain.cpp:17)
BaseThreadInitThunk [0x7694343D+18]
RtlInitializeExceptionChain [0x776A9802+99]
RtlInitializeExceptionChain [0x776A97D5+54]
I might be wrong, but maybe this' got something to do with:
class PySide2.QtQuickWidgets.QQuickWidget(engine, parent)¶
PySide2.QtQuickWidgets.QQuickWidget([parent=None])
PySide2.QtQuickWidgets.QQuickWidget(source[, parent=None])
param parent PySide2.QtWidgets.QWidget
param source PySide2.QtCore.QUrl
param engine PySide2.QtQml.QQmlEngine
Constructs a QQuickWidget with the given QML engine and parent .
Note: In this case, the QQuickWidget does not own the given engine
object; it is the caller’s responsibility to destroy the engine. If
the engine is deleted before the view, status() will return Error .
source: https://doc.qt.io/qtforpython-5/PySide2/QtQuickWidgets/QQuickWidget.html#PySide2.QtQuickWidgets.PySide2.QtQuickWidgets.QQuickWidget

QT 5.8 how does Webview do a request and how can I intercept it?

I am trying to pass QnetworkRequest to a webView located in my main.qml file instead of a "url". I pass the url by referecing the webView object and the setproperty function. However, haven't found the right function and really do not know where to start in order to either create a new function or modify the existing webView code in order to get this to work. Is there a way to edit the source code to the webView in the .qml file. Of course i am just learning the QT framework.
I have tried WebengineView but the Webview is MUCH faster in loading a page. and that is critical for the application
I guess the real question is how does Webview do a request and how can I intercept it?
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtWebView 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2
ApplicationWindow {
flags: Qt.FramelessWindowHint
visible: true
x: 600
y: 400
width: 500
height: 500
title: webView.title
WebView {
id: webView
anchors.fill: parent
objectName: "webView"
//setting this value through main.cpp
// url: "https://www.google.com"
onLoadingChanged: {
if (loadRequest.errorString)
console.error(loadRequest.errorString);
}
}
}
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtWebView::initialize();
QQmlApplicationEngine engine;
//How to Pass THIS request to the webview instead of url?
QNetworkRequest request;
request.setUrl(QUrl("http://google.com"));
request.setRawHeader("Accept-Charset", "UTF-8,*;q=0.5");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *qmlObject = rootObject->findChild<QObject*>("webView");
//Able to set the URL for the webView:
qmlObject->setProperty("url", "https://www.google.com" );
return app.exec();
}
AFAIK, all QML components use QNetworkAccessManager. You can register your own factory to have you own class handle the networking. You could overload the get method and change the header if the url matches google, or whatever you want to do:
class MyNetworkManager : public QNetworkAccessManager {
public:
QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) {
if (request.url.contains("google")) {
request.setRawHeader("Accept-Charset", "UTF-8,*;q=0.5");
}
return QNetworkAccessManager::get(request)
}
}
Of course, here request is const so you would have to construct a new request, but I hope this shows the basic idea!
Example on how to register your own NetworkAccessManagerFactory in main.cpp:
http://doc.qt.io/qt-5/qtqml-networkaccessmanagerfactory-example.html

qt quick 2 printing with console

I can't figure out how to print with console.log inside a qt quick application.
I have this .pro file:
TEMPLATE = app
QT += qml quick
CONFIG += c++11
CONFIG += console
SOURCES += main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
this is main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
this is main.qml:
import QtQuick 2.5
import QtQuick.Window 2.0
Window {
visible: true
Text {
anchors.centerIn: parent
text: "Hello World"
}
Component.onCompleted: console.log("foo")
}
Why it doens't print "foo"?
solved it was caused by the fact that Fedora has *.debug=false inside /etc/xdg/QtProject/qtlogging.ini and that prevents the messages to be printed. To "solve" this it's enough to create the file ~/.config/QtProject/qtlogging.ini with this content:
[Rules]
default=true

Using QtCreator 5.3 for Qt Quick UI project, how to link QML button resource to C++ function call

This is my first post on StackOverflow so please excuse any formatting mistakes I might have made.
I'm building a Qt Quick UI project using Qt Quick Controls 1.1 and I have a simple Button in my QML code that I would like to call into my C++ action class. I see a number of examples on this with earlier versions of Qt, but they do not seem to work in 5.3. I chose Qt Quick Controls 1.1 in the project settings. I know this must not be too complicated to do, but I can't seem to find examples using QtCreator 5.3
Here is my main.qml file:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
ApplicationWindow {
id: parentWnd
visible: true
width: 640
height: 480
Action {
id: actionSend
onTriggered: console.log("SEND")
}
Button {
id: send
text: "Send Request"
action: actionSend
signal sendSignal()
}
}
Here is my main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
Here is the action class where I would like the QML button to call "doSend":
#include<QDebug>
#include<QObject>
class Action : public QObject
{
Q_OBJECT
public:
Action();
public slots:
void doSend();
};
Finally here is my project file:
TEMPLATE = app
QT += qml quick widgets
SOURCES += main.cpp \
action.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
HEADERS += \
action.h
When I run this, I see the button and I see the logging of "SEND" to the console so I know the QML Action is setup correctly. Any help on how to make the Action call into my action class would be much appreciated!
There are three issues that you're running into here.
The first is that you haven't registered your Action class with QML in main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<Action>("StevesModule", 1, 0, "Action");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
The second is that Action is a QML type in the Qt Quick Controls module. It doesn't know anything about your Action class in C++. If you want to use your Action class instead of Qt Quick Controls' Action type, you must import it in your QML file:
import StevesModule 1.0
The third is that you're not calling the doSend() slot anywhere. You can do this in the onClicked handler of Button:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import StevesModule 1.0
ApplicationWindow {
id: parentWnd
visible: true
width: 640
height: 480
Action {
id: actionSend
}
Button {
id: send
text: "Send Request"
onClicked: actionSend.doSend()
}
}

Resources