Qt通过QWebEngineView渲染html页面,可以通过QWebChannel结合javascript与html交互
其中需要2个js文件,一个jquery文件,如果javascript学得好可以不要,一个是qt提供的qwebchannel.js
先看效果图
根据qtmarkdown示例做的, 列表使用了自定义类Student,本来想通过自定义QType做交互,但没实现,获取到的数据为null,后该为了QJsonObject,
首先学生类,其实就是一个简单的c++类
#ifndef STUDENT_H#define STUDENT_H#include <QString>#include <QMetaType>// Creating a Custom Type// Before we begin, we need to ensure that the custom type we are creating meets all the requirements imposed by QMetaType. In other words, it must provide:// a public default constructor,// a public copy constructor, and// a public destructor.// 使用Q_PROPERTY 还需要提供一个!=重载以及一个赋值=重载// The class in Qt responsible for custom types is QMetaType.// To make the type known to this class, we invoke the Q_DECLARE_METATYPE() macro// on the class in the header file where it is defined:// Q_DECLARE_METATYPE(Student);// To enable creation of objects at run-time,// call the qRegisterMetaType() template function to register// it with the meta-object system.// qRegisterMetaType<Student>("Student");class Student{public: Student() = default; // 默认构造函数 Student(const QString& name, int age, const QString& tel); ~Student() = default; // 默认析构函数 Student(const Student&); // 拷贝构造函数 Student& operator=(const Student&); // 赋值重载 bool operator!=(const Student&); QString getName() const; int getAge() const; QString getTel() const;private: QString m_Name; int m_Age; QString m_Tel;};Q_DECLARE_METATYPE(Student);#endif // STUDENT_H
实现
#include "student.h"Student::Student(const QString &name, int age, const QString &tel) : m_Name(name) , m_Age(age) , m_Tel(tel){}Student::Student(const Student& lt){ this->m_Name = lt.m_Name; this->m_Age = lt.m_Age; this->m_Tel = lt.m_Tel;}Student& Student::operator=(const Student& lt){ if (this != <) { this->m_Name = lt.m_Name; this->m_Age = lt.m_Age; this->m_Tel = lt.m_Tel; } return *this;}bool Student::operator!=(const Student& lt){ // 只比较name if (m_Name == lt.m_Name) { return false; } return true;}QString Student::getName() const{ return m_Name;}int Student::getAge() const{ return m_Age;}QString Student::getTel() const{ return m_Tel;}
最重要的是通信类, 这里有点绕,需要理解信号与槽
#ifndef USERDOCUMENT_H#define USERDOCUMENT_H#include <QObject>#include <QJsonObject>#include <QRect>#include "student.h"class UserDocument : public QObject{ Q_OBJECT Q_PROPERTY(QString title MEMBER m_title NOTIFY titleChangedFromQt FINAL) Q_PROPERTY(QJsonObject jsonStudent MEMBER m_jsonStudent NOTIFY studentChangedFromQt FINAL)public: UserDocument(QObject* parent = nullptr) : QObject(parent) {} // 设置标题并发送titleChangedFromQt void setTitle(const QString& title); // 设置学生类并发送studentChangedFromQt void setStudent(const Student& student); Student getStudent() const;signals: // qt端发送信号给web端同步修改标题 void titleChangedFromQt(const QString& title); // qt端发送信号给web端同修改学生 void studentChangedFromQt(const QJsonObject& jsonStudent); // 当web端发送titleChangeFromWeb后同步修改qt端标题 void titleChangedForQt(const QString& title); // 当web端发送studentChangedFromWeb后同步修改qt端学生 void studentChangedForQt(const Student& student);public slots: // web端发送给qt端同步修改标题 void titleChangeFromWeb(const QString& title); // web端发送给qt端同步修改学生 void studentChangedFromWeb(const QJsonObject& jsonStudent);private: QString m_title; Student m_student; QJsonObject m_jsonStudent; QRect m_rect;};#endif // USERDOCUMENT_H
实现
#include "userdocument.h"#include <QDebug>void UserDocument::setTitle(const QString& title){ m_title = title; emit titleChangedFromQt(m_title);}void UserDocument::setStudent(const Student& student){ m_student = student; QJsonObject jsonStudent; jsonStudent.insert("name", student.getName()); jsonStudent.insert("age", student.getAge()); jsonStudent.insert("tel", student.getTel()); m_jsonStudent = jsonStudent; emit studentChangedFromQt(jsonStudent);}Student UserDocument::getStudent() const{ return m_student;}void UserDocument::titleChangeFromWeb(const QString& title){ emit titleChangedForQt(title);}void UserDocument::studentChangedFromWeb(const QJsonObject& jsonStudent){ m_jsonStudent = jsonStudent; QString name = jsonStudent.find("name").value().toString(); int age = jsonStudent.find("age").value().toInt(); QString tel = jsonStudent.find("tel").value().toString(); Student stu{name, age, tel}; m_student = stu; emit studentChangedForQt(m_student);}
主界面,ui需要提供一个QLineEdit用作标题,一个QTableWidget显示列表,一个QLineEdit表示名称,一个QSpinBox表示年龄, 一个QLineEdit表示电话,一个QWdiget提升为QWebEngineView显示网页,一个QProgressBar显示网页加载进度, 当然,这个看自己需求。
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QJsonObject>#include "userdocument.h"QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }QT_END_NAMESPACEclass MainWindow : public QMainWindow{ Q_OBJECTpublic: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void initBrower(); // 初始化浏览器 void setStudentList(); // 设置qt端学生列表 void setWebStudentList(); // 设置web端学生列表,通过jquery设置private slots: void webViewSource(); // 查看web源码 void webViewProgress(int progress); // web加载进度 void webViewLoadFinished(bool finisned); // web加载完毕 void titleChanged(); // qt端标题修改 void titleChangedForQt(const QString& title); // web端标题修改 void studentChangedForQt(const Student& student); // web端学生修改 void selectTableRow(int index); // QTableWidget行选中 void btnSubmitClicked(); // 提交按钮private: Ui::MainWindow *ui; UserDocument m_UserDocument; QStringList m_studentListHeader; QList<Student> m_studentList;};#endif // MAINWINDOW_H
实现
#include "mainwindow.h"#include "ui_mainwindow.h"#include <QToolBar>#include <QTextEdit>#include <QtWebChannel>#include "student.h"#include <QHeaderView>#include <QDebug>MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow){ ui->setupUi(this); ui->pProgressWebLoad->setMaximumHeight(5); ui->pProgressWebLoad->hide(); // 标题改变,html里面的标题也跟着改变 connect(ui->pLeMainTitle, &QLineEdit::textChanged, this, &MainWindow::titleChanged); // QTableWidget行点击 connect(ui->pTbUserList->verticalHeader(), &QHeaderView::sectionClicked, this, &MainWindow::selectTableRow); // 提交按钮 connect(ui->pBtnSubmit, &QPushButton::clicked, this, &MainWindow::btnSubmitClicked); // 初始化浏览器 initBrower();}MainWindow::~MainWindow(){ delete ui;}void MainWindow::initBrower(){ ui->pWebBrower->load(QUrl("qrc:/resources/index.html")); QWebChannel* channel = new QWebChannel(this); // web与qt通信主要手段 channel->registerObject("UserDocument", &m_UserDocument); ui->pWebBrower->page()->setWebChannel(channel); // 初始化web标题 m_UserDocument.setTitle("User Manage"); // 模拟部分学生 Student stu = {"Jone", 18, "028-87930392"}; Student stu2 = {"Jim", 17, "028-46443534"}; Student stu3 = {"Sam", 18, "028-57657446"}; Student stu4 = {"Lucy", 17, "028-78745448"}; m_studentList.append(stu); m_studentList.append(stu2); m_studentList.append(stu3); m_studentList.append(stu4); // 初始化web表单 m_UserDocument.setStudent(stu); // web端学生修改 connect(&m_UserDocument, &UserDocument::studentChangedForQt, this, &MainWindow::studentChangedForQt); // web端标题修改 connect(&m_UserDocument, &UserDocument::titleChangedForQt, this, &MainWindow::titleChangedForQt); // 初始化qt表单 ui->pLeName->setText(stu.getName()); ui->pSbAge->setValue(stu.getAge()); ui->pLeTel->setText(stu.getTel()); // 设置QTableWidget 表头 m_studentListHeader << "Name" << "Age" << "Tel"; ui->pTbUserList->setColumnCount(m_studentListHeader.count()); // 这个要在setHorizontalHeaderLabels前 ui->pTbUserList->setHorizontalHeaderLabels(m_studentListHeader); // 初始化qt端学生列表 setStudentList(); // 把前进、后退、刷新按钮加入到页面 QToolBar* toolBar = addToolBar("Tool"); toolBar->addAction(ui->pWebBrower->pageAction(QWebEnginePage::Forward)); toolBar->addAction(ui->pWebBrower->pageAction(QWebEnginePage::Back)); toolBar->addAction(ui->pWebBrower->pageAction(QWebEnginePage::Reload)); // 添加查看源码按钮 toolBar->addAction("ViewSource", this, &MainWindow::webViewSource); // web加载进度 connect(ui->pWebBrower->page(), &QWebEnginePage::loadProgress, this, &MainWindow::webViewProgress); // web加载完毕 connect(ui->pWebBrower->page(), &QWebEnginePage::loadFinished, this, &MainWindow::webViewLoadFinished);}void MainWindow::setStudentList(){ ui->pTbUserList->setRowCount(m_studentList.count()); int i = 0; for (Student stu : m_studentList) { QTableWidgetItem* item; item = new QTableWidgetItem(stu.getName()); ui->pTbUserList->setItem(i, 0, item); item = new QTableWidgetItem(QString::number(stu.getAge())); ui->pTbUserList->setItem(i, 1, item); item = new QTableWidgetItem(stu.getTel()); ui->pTbUserList->setItem(i, 2, item); i++; }}void MainWindow::setWebStudentList(){ QString html(tr("<tr><th>%1</th><th>%2</th><th>%3</th></tr>") .arg(m_studentListHeader.at(0)) .arg(m_studentListHeader.at(1)) .arg(m_studentListHeader.at(2))); for (Student stu : m_studentList) { QString body(tr("<tr><td>%1</td><td>%2</td><td>%3</td></tr>") .arg(stu.getName()) .arg(stu.getAge()) .arg(stu.getTel())); html += body; } // 使用jquery同步学生列表 QString code(tr("$qt.jQuery('#users').html('%1')").arg(html)); // 执行javascript ui->pWebBrower->page()->runJavaScript(code);}void MainWindow::webViewSource(){ // 查看源码 QTextEdit* source = new QTextEdit(nullptr); source->setAttribute(Qt::WA_DeleteOnClose); // 关闭自动delete ui->pWebBrower->page()->toHtml([source](const QString& html) { source->setPlainText(html); }); source->resize(600, 500); source->show();}void MainWindow::webViewProgress(int progress){ // 当在加载中时显示进度条,否则不显示 if (progress < 100) { ui->pProgressWebLoad->show(); } else { ui->pProgressWebLoad->hide(); } ui->pProgressWebLoad->setValue(progress);}void MainWindow::webViewLoadFinished(bool finisned){ if (finisned) { // web加载完毕就初始化学生列表 setWebStudentList(); }}void MainWindow::titleChanged(){ // qt端标题修改同步修改web端标题 m_UserDocument.setTitle(ui->pLeMainTitle->text());}void MainWindow::titleChangedForQt(const QString& title){ // web端标题修改同步修改qt端标题 ui->pLeMainTitle->setText(title);}void MainWindow::studentChangedForQt(const Student& student){ // web端x学生添加同步添加qt端学生, 修改原理一样 ui->pLeName->setText(student.getName()); ui->pSbAge->setValue(student.getAge()); ui->pLeTel->setText(student.getTel()); m_studentList.append(student); setStudentList(); setWebStudentList();}void MainWindow::selectTableRow(int index){ // QTableWidget行选中 QTableWidgetItem* itemName = ui->pTbUserList->item(index, 0); QTableWidgetItem* itemAge = ui->pTbUserList->item(index, 1); QTableWidgetItem* itemTel = ui->pTbUserList->item(index, 2); QString name = itemName->text(); int age = itemAge->text().toInt(); QString tel = itemTel->text(); // 获取到的数据同步到qt表单 ui->pLeName->setText(name); ui->pSbAge->setValue(age); ui->pLeTel->setText(tel); // 获取到的数据同步到web表单 Student stu = {name, age, tel}; m_UserDocument.setStudent(stu);}void MainWindow::btnSubmitClicked(){ // 提交表单 QString name = ui->pLeName->text(); int age = ui->pSbAge->value(); QString tel = ui->pLeTel->text(); // 只做添加 Student stu = {name, age, tel}; m_studentList.append(stu); setStudentList(); setWebStudentList();}
html页面比较简单,懂前端的应该不难看懂
<html> <head> <meta charset="utf-8" /> <title>User Manage</title> <style> .container { width: 1000px; margin: 0 auto; margin-top: 30px; } .header { text-align: center; margin: 20px auto; } .main { display: flex; } .list { flex: 0 0 60%; } .users { border-left: 1px solid #cccccc; border-top: 1px solid #cccccc; border-spacing: 0; width: 100%; } .users td { border-right: 1px solid #cccccc; border-bottom: 1px solid #cccccc; padding: 5px; } .user-form { flex: 0 0 38%; } .user-form .form-group { display: flex; margin-bottom: 20px; } .user-form .form-group .label { flex: 0 0 120px; text-align: right; padding-right: 5px; line-height: 32px; } .user-form .form-group .input-box { flex: 1; } .user-form .form-group .input-box input { width: 100%; height: 32px; line-height: 32px; border-radius: 5px; border: 1px solid #cccccc; } .user-form .form-group .input-box button { width: 120px; height: 32px; line-height: 32px; text-align: center; } #log { position: bottom; height: 150px; left: 0; right: 0; bottom: 0; border-left: 1px solid #cccccc; } </style> <script src="jquery-3.5.1.min.js"></script> <script src="qwebchannel.js"></script> </head> <body> <div class="container"> <div class="header"> <h1><input type="text" name="title" id="title" value="User Manage" /></h1> </div> <div class="main"> <div class="list"> <table class="users" id="users"></table> </div> <div class="user-form"> <form> <div class="form-group"> <div class="label">Name:</div> <div class="input-box"> <input type="text" name="name" id="name" value="" /> </div> </div> <div class="form-group"> <div class="label">Age:</div> <div class="input-box"> <input type="number" name="age" id="age" value="" /> </div> </div> <div class="form-group"> <div class="label">Tel:</div> <div class="input-box"> <input type="text" name="tel" id="tel" value="" /> </div> </div> <div class="form-group"> <div class="label"> </div> <div class="input-box"> <button type="button" id="changeStudent">Submit</button> </div> </div> </form> </div> </div> </div> <div id="log"> </div> </body> <script> var $qt = { 'jQuery': jQuery.noConflict(true) }; $qt.jQuery('#log').html("test"); var updateTitle = function(text) { if (text != "") { $qt.jQuery('#title').val(text); } } var updateStudent = function(student) { $qt.jQuery('#name').val(student.name); $qt.jQuery('#age').val(student.age); $qt.jQuery('#tel').val(student.tel); } var userDocument; new QWebChannel(qt.webChannelTransport, function(channel) { userDocument = channel.objects.UserDocument; $qt.jQuery('#log').html(JSON.stringify(userDocument)); //$qt.jQuery('#log').html(JSON.stringify(userDocument.jsonStudent)); // 同步修改web标题 updateTitle(userDocument.title); userDocument.titleChangedFromQt.connect(updateTitle); // 同步显示web学生表单 updateStudent(userDocument.jsonStudent); userDocument.studentChangedFromQt.connect(updateStudent); }); // 同步修改qt标题 $qt.jQuery('#title').on("input propertychange", function() { $qt.jQuery('#log').html($qt.jQuery(this).val()); userDocument.titleChangeFromWeb($qt.jQuery(this).val()); }) // 同步显示qt学生表单 $qt.jQuery('#changeStudent').click(function() { var stu = { name: $qt.jQuery('#name').val(), age: parseInt($qt.jQuery('#age').val()), tel: $qt.jQuery('#tel').val() } userDocument.studentChangedFromWeb(stu); }) </script></html>
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/112068.html