为了创建基于Qt的web应用程序,Qt提供了支持各种标准web技术(如HTML、CSS和JavaScript)的接口。这些接口使应用程序能够嵌入来自万维网的内容。
也可以将Qt c++和QML代码与HTML和JavaScript结合起来创建web应用程序。通过Qt WebChannel,远程客户端可以访问Qt的api。
ps:什么是web应用程序?Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。所有这些组件相互协调为用户提供一组完整的服务。常见的计数器、留言版、聊天室和论坛BBS等,都是Web应用程序。
1、Qt为面向桌面和嵌入式平台的应用程序提供基于chrome的Qt WebEngine模块。
Qt WebEngine模块提供了一个web浏览器引擎,可以在没有本地web引擎的平台上轻松地将来自万维网的内容嵌入到Qt应用程序中。就相当于程序是一个浏览器,只不过和专业的浏览器比功能很少而已。
可以在传统的widget中实现Qt WebEngine,也可以在Qt quick中实现。看需求选择。
1)在传统的widget中实现Qt WebEngine。为了实现这个,我花费了1天左右的时间来踩坑,在这里记录下:
在安装Qt时,一定要选msvc编译才行,否则模块会报不存在
然后,得装两个东西,一个是vs2017/vs2015,另一个是WinSDk10,具体参考:https://blog.csdn.net/nupt_zhangtao/article/details/77444132。
这里还有个坑就是 必须在vs中去安装助手,必须在vs中去安装助手,必须在vs中去安装助手。
2)QWebEngineView类:我们能用这个view可以干嘛。如下图,首先通过view获得page对象,page对象下可以获得history历史数据,相关的action动作,等。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QAction *action = menuBar()->addAction("Icon");
QAction *back = menuBar()->addAction("返回");
QAction *forward = menuBar()->addAction("前进");
QAction *reload = menuBar()->addAction("刷新");
QAction *stop = menuBar()->addAction("停止");
QWebEngineView *view = new QWebEngineView;
setCentralWidget(view);
connect(back,&QAction::triggered,[=]{
view->back();
});
connect(forward,&QAction::triggered,[=]{
view->forward();
});
connect(reload,&QAction::triggered,[=]{
view->reload();
});
connect(stop,&QAction::triggered,[=]{
view->stop();
});
//相关信号
//与视图关联的图标(“favicon”)发生更改
connect(view,&QWebEngineView::iconChanged,[=](const QIcon& icon){
action->setIcon(icon);
});
//与视图关联的图标(“favicon”)的URL发生更改
connect(view,&QWebEngineView::iconUrlChanged,[=](const QUrl &url){
action->setText(url.url());
});
//是否加载完毕
connect(view,&QWebEngineView::loadFinished,[=](bool){
qDebug() << "加载完毕";
menuBar()->addAction(view->pageAction(QWebEnginePage::Back));
menuBar()->addAction(view->pageAction(QWebEnginePage::Cut));
});
//加载进度
connect(view,&QWebEngineView::loadProgress,[=](int progress){
qDebug() << "加载进度:" << progress << "%";
});
//开始加载
connect(view,&QWebEngineView::loadStarted,[=]{
qDebug() << "正在加载";
});
//当呈现进程以非零退出状态终止时,将发出此信号
connect(view,&QWebEngineView::renderProcessTerminated,
[=](QWebEnginePage::RenderProcessTerminationStatus terminationStatus,int exitCode){
});
//选择文本时发出信号
connect(view,&QWebEngineView::selectionChanged,[=]{
if(view->hasSelection()){
qDebug() << "选择了文本:" << view->selectedText();
}
});
//视图的标题发生变化
connect(view,&QWebEngineView::titleChanged,[=](const QString& title){
qDebug() << "标题:" << title;
});
//视图的url发生更改
connect(view,&QWebEngineView::urlChanged,[=](const QUrl &url){
qDebug() << "视图的url:" << url.url();
});
view->load(QUrl("http://www.baidu.com"));
view->show();
}
3)它还提供了Qt WebView模块,该模块使用平台的本机web引擎。必要的步骤是:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QtWebEngine::initialize();
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
import QtQuick 2.9
import QtQuick.Window 2.2
import QtWebEngine 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
WebEngineView{
anchors.fill: parent
url: "http://www.jd.com";
}
}
2、Qt WebView提供了一种在QML应用程序中显示web内容的方法,它在有意义的地方使用本机api,而不必包含完整的web浏览器堆栈。这在Android、iOS和WinRT等移动平台上非常有用;特别是在iOS上,策略要求所有web内容都使用操作系统的web view显示。
qml中的简单例子:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QtWebView::initialize();//因为用的是库,所以必须先初始化
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
WebView{
id:webView
anchors.fill: parent
url: "http://www.baidu.com";
//新的页面时响应
onLoadingChanged: {
console.debug(loadRequest.errorString);
console.debug(loadRequest.status);
console.debug(loadRequest.url);
}
}
}
3、
Qt WebChannel模块允许HTML或JavaScript客户端访问Qt api,如QObject。它提供c++或QML API,允许Qt应用程序与JavaScript和HTML前端通信。
API分3种,这3中API可以相互通信:
1、JavaScript API。
主要是qrc:///qtwebchannel/qwebchannel.js.
2、C++ API。
QWebChannel:向远程HTML客户端公开qobject。
QWebChannelAbstractTransport:服务器(c++ 的QWebChannel)与客户端(HTML/JS)之间的通信通道。
3、QML API。
WebChannel:WebChannel提供了一种机制,可以透明地从HTML客户机访问QObject或QML对象。所有属性、信号和公共插槽都可以从HTML客户机中使用。
Qt中的实例:
//该类的一个实例通过WebChannel发布,然后HTML客户机可以访问它
class Core : public QObject
{
Q_OBJECT
public:
Core(Dialog *dialog, QObject *parent = 0):QObject(parent)
{
connect(dialog,&Dialog::sendText,this,&Core::sendText);
}
signals:
//这个信号是从c++端发出的,文本显示在HTML客户端。
void sendText(const QString &text);
public slots:
//这个插槽是从HTML客户端和服务器端显示的文本调用的。
void receiveText(const QString &text)
{
m_dialog->displayMessage(Dialog::tr("Received message: %1").arg(text));
}
private:
Dialog *m_dialog;
};
class QPlainTextEdit;
class QLineEdit;
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
void displayMessage(const QString &message);
signals:
void sendText(const QString &text);
private slots:
void clicked();
private:
QPlainTextEdit *plain;
QLineEdit *edit;
QPushButton *sendBtn;
};
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *mainLayout = new QVBoxLayout;
setLayout(mainLayout);
plain = new QPlainTextEdit;
plain->setReadOnly(true);
mainLayout->addWidget(plain);
QHBoxLayout *layout = new QHBoxLayout;
edit = new QLineEdit;
layout->addWidget(edit);
sendBtn = new QPushButton("Send");
layout->addWidget(sendBtn);
mainLayout->addLayout(layout);
connect(sendBtn,&QPushButton::clicked,this,&Dialog::clicked);
}
Dialog::~Dialog()
{
}
void Dialog::displayMessage(const QString &message)
{
plain->appendPlainText(message);
}
void Dialog::clicked()
{
const QString text = edit->text().trimmed();
if(text.isEmpty())
return;
emit sendText(text);
displayMessage(tr("Send message: %1").arg(text));
edit->clear();
}
class WebSocketTransport;
class QWebSocketServer;
class WebSocketClientWrapper : public QObject
{
Q_OBJECT
public:
WebSocketClientWrapper(QWebSocketServer *server, QObject *parent = 0);
signals:
void clientConnected(WebSocketTransport *client);
private slots:
void handleNewConnection();
private:
QWebSocketServer *m_server;
};
WebSocketClientWrapper::WebSocketClientWrapper(QWebSocketServer *server, QObject *parent)
:QObject(parent),m_server(server)
{
connect(m_server,&QWebSocketServer::newConnection,
this,&WebSocketClientWrapper::handleNewConnection);
}
void WebSocketClientWrapper::handleNewConnection()
{
//把客户端socket放进传输通道对象中 专门处理与远程客户端的通信
emit clientConnected(new WebSocketTransport(m_server->nextPendingConnection()));
}
class QWebSocket;
class WebSocketTransport : public QWebChannelAbstractTransport
{
Q_OBJECT
public:
explicit WebSocketTransport(QWebSocket *socket);
virtual ~WebSocketTransport();
void sendMessage(const QJsonObject &message) override;
private slots:
//接收数据
void textMessageReceived(const QString &message);
private:
QWebSocket *m_socket;
};
WebSocketTransport::WebSocketTransport(QWebSocket *socket)
:m_socket(socket),QWebChannelAbstractTransport(socket)//绑定一个socket 用于读取远程客户端发送来的数据
{
connect(socket,&QWebSocket::textMessageReceived,
this,&WebSocketTransport::textMessageReceived);
connect(socket,&QWebSocket::disconnected,this,&WebSocketTransport::deleteLater);
}
WebSocketTransport::~WebSocketTransport()
{
m_socket->deleteLater();
}
void WebSocketTransport::sendMessage(const QJsonObject &message)
{
QJsonDocument doc;
doc.setObject(message);
m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
}
void WebSocketTransport::textMessageReceived(const QString &message)
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(),&error);
if(error.error != QJsonParseError::NoError){
qDebug() << error.errorString();
return;
}else if(!doc.isObject()){
qDebug() << " is not Object";
return;
}
emit messageReceived(doc.object(),this);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileInfo jsFileInfo(QDir::currentPath()+"/qwebchannel.js");
if(!jsFileInfo.exists())
QFile::copy(":/qtwebchannel/qwebchannel.js",
jsFileInfo.absoluteFilePath());
//创建服务端对象
QWebSocketServer server(QStringLiteral("QWebChannel Standalone Example Server"),
QWebSocketServer::NonSecureMode);
//监听
if(!server.listen(QHostAddress::LocalHost, 12345)){
qDebug() << "监听失败";
return 1;
}
//创建传输通道
WebSocketClientWrapper clientWrapper(&server);
//建立web通道 暴露api给远程客户端调用
QWebChannel channel;
QObject::connect(&clientWrapper,&WebSocketClientWrapper::clientConnected,
&channel,&QWebChannel::connectTo);
Dialog w;
Core core(&w);
//向QWebChannel注册单个对象
//对象的属性、信号和公共方法被发布到远程客户端。然后构造一个具有标识符id的对象
channel.registerObject(QStringLiteral("core"),&core);
//开启html 客户端
QUrl url = QUrl::fromLocalFile(SOURCEDIR "/index.html");
QDesktopServices::openUrl(url);
w.displayMessage(QDialog::tr("Initialization complete, opening browser at %1.").arg(url.toDisplayString()));
w.show();
return a.exec();
}