Qt之Web内容的展示

为了创建基于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应用程序。

一、WebEngine in Qt

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和HTML-JavaScript混合应用程序

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();
}

猜你喜欢

转载自blog.csdn.net/wei375653972/article/details/86527694