QT 使用 QWebChannel 与 Web 端通信展示文件信息

前言

本文将展示如何使用 QWebChannel 来实现 Web 端与 QT 端之间的交互,同时会通过一个在浏览器端展示文件夹信息的简单例子来展示其具体使用,其功能如下:

  • 获取指定文件夹下的文件信息。
  • 通过使用 QT 的 QFileSystemWatcher 对指定文件夹进行监听,以实现可以获取到文件变更信息并实时展示在浏览器端。

最终实现的效果如下:

本文涉及到的完整代码已上传到GitHub

阅读本文前需要对 QT 的基础知识(比如信号槽)及 QWebChannel 模块有基本的了解。

实现

QT 端

在创建完项目后,首先需要确保引入了WebChannelWebSockets模块,这里以 MSVC 为例:

完成以上操作后,我们首先需要创建一个WebChannelObj类用于管理所有开放给前端调用方法,头文件内容如下(为方便查看,内容有删减,后续文件都会有些许删减,完整代码可在Github查看):

c++ 复制代码
class WebChannelObj : public QObject
{

signals:

    /// <summary>
    /// 信息变更事件
    /// </summary>
    /// <param name="info">文件信息</param>
    void infoChanged(const QJsonDocument& info);

public slots:

    /// <summary>
    /// 获取文件信息
    /// </summary>
    /// <returns>转为 JSON 格式的文件信息</returns>
    QJsonDocument getInfo();

};

这里的getInfo方法是直接提供给 Web 端调用的方法,需要定在slots中,infoChanged是提供给 Web 端监听的事件,需要定义在signals中。

对应的 C++ 文件如下:

c++ 复制代码
// 监听的路径
QString dirPath = "D:\\code\\temp\\";

WebChannelObj* WebChannelObj::getInstance()
{
    // 配置监听
	QFileSystemWatcher* m_FileWatcher = new QFileSystemWatcher;
	m_FileWatcher->addPath(dirPath);
	connect(m_FileWatcher, &QFileSystemWatcher::directoryChanged, obj, &WebChannelObj::directoryChanged);
	return obj;
}

QJsonDocument WebChannelObj::getInfo()
{
	return dirInfo(dirPath);
}

void WebChannelObj::directoryChanged(const QString& path)
{
    // 触发变更事件
	emit infoChanged(dirInfo(path));
}

QJsonDocument WebChannelObj::dirInfo(const QString& path)
{
    // 根据给定的路径获取目录下的文件信息
    // 然后封装信息为 JSON 对象并返回
	QDir dir(path);
	dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
	QFileInfoList list = dir.entryInfoList();
	QJsonArray jsonArray;
	for (auto& file : list)
	{
		QJsonObject obj;
		obj["isDir"] = file.isDir();
		obj["fileName"] = file.fileName();
		obj["fileSize"] = file.size();
		obj["lastModified"] = file.lastModified().toMSecsSinceEpoch();
		jsonArray.append(obj);
	}
	QJsonDocument doc(jsonArray);
	return doc;
}

这里使用了QFileSystemWatcher来实现对指定路径进行监听,这样就可以实时获取路径下的最新文件信息,然后通过连接directoryChangedinfoChanged事件,将信息实时返回给前端。

然后我们需要创建一个 WebSocket 服务端用于 QT 与 Web 端通信的桥梁,并在其中将QWebChannelWebSocket进行连接,内容如下:

c++ 复制代码
WebChannelServer::WebChannelServer()
{
    // 将定义的 WebChannelObj 注册到 QWebChannel 中
    m_webChannel = new QWebChannel(this);
    m_webChannel->registerObject("server", WebChannelObj::getInstance());
    startServer();
}

void WebChannelServer::startServer()
{
    // 启动一个 websocket 服务, 端口为 9999
    m_websocketServer = new QWebSocketServer("Webchannel", QWebSocketServer::NonSecureMode, this);
    if (m_websocketServer->listen(QHostAddress::Any, 9999))
    {
        connect(m_websocketServer, &QWebSocketServer::newConnection, this, &WebChannelServer::onNewConnection);
    }
}

void WebChannelServer::onNewConnection()
{
    // 将 QWebChannel 与 WebSocket 进行连接
    QWebSocket* client = m_websocketServer->nextPendingConnection();
    m_webChannel->connectTo(new WebSocketTransport(client));
}

其中WebSocketTransport类是我们自定义的用于处理QWebChannelWebSocket和 Web 端通信的类,实现自QWebChannelAbstractTransport这个抽象类,其内容较为固定,就不再介绍其内容,可在GitHub中进行查看。

完成以上操作后,只需要在 main 方法中使用即可:

c++ 复制代码
int main(int argc, char *argv[])
{

    QGuiApplication app(argc, argv);

    //...省略内容

    // 使用
    WebChannelServer webChannelServer;

    return app.exec();
}

到此为止, QT 端的工作就算完成了。

Web 端

相比于 QT 端,Web 端的使用就很简单了,首先我们需要先引用一个qwebchannel.js文件,这个文件由官方提供,可以在 QT 安装目录的 Examples 中找到:

然后就可以直接使用了:

vue 复制代码
<script>
import { QWebChannel } from '@/assets/qwebchannel'

export default {
  name: 'App',
  data() {
    return {
      // 文件列表
      fileList: []
    }
  },
  mounted() {
    const websocket = new WebSocket('ws://localhost:9999')
    websocket.onopen = () => {
      new QWebChannel(websocket, channel => {
        // server 就是前文中通过 m_webChannel->
        //   registerObject("server", WebChannelObj::getInstance()); 注册的名字  
        const webChannelObj = channel.objects.server
        
        // infoChanged 是我们在 signals 中定义的提供给前端的监听事件
        // 只要有文件变更事件, 这里通过 connect 就可以获取到相应的回调事件
        webChannelObj.infoChanged.connect(data => {
          this.fileList = data
        })
        
        // getInfo 是我们在 slots 中定义的提供给前端的方法
        // 返回值需要通过回调获取  
        webChannelObj.getInfo(data => {
          this.fileList = data
        })
      })
    }
  }
}
</script>

关于上述代码的含义参考相应的注释即可,这里只需要知道如果我们想给前端提供监听事件,只需要参考 infoChanged 的定义,在前端通过 xxx.connect(data => {})就可以在回调中获取相关信息。如果想给前端提供可以直接调用的方法,只需要参考 getInfo 的定义,同时需要注意返回值也需要通过回调函数进行获取,如果方法包含参数,则按照正常传参(如果传递了对应,QT 中可以使用 QJsonObject 进行接收),同时把回调函数始终作为最后一个参数即可。

总结

本文通过一个简单的例子展示了如何使用 QWebChannel 及 WebSocket 来实现 QT 与 Web 端的通信,通过使用 QWebChannel ,我们可以很方便地在 Web 端调用 QT 中的定义方法以及实现两端的通信(例如在 Web 端通过 QT 获取摄像头然后展示在 Web 端)。本文如果有错误或不当之处,也欢迎一起交流讨论。

相关推荐
shawxlee1 小时前
vue3在public下封装config.js自定义配置动态数据,可在打包后直接修改,方便后端部署及后续维护
前端·javascript·经验分享·vue·团队开发·js·项目优化
腾讯云云开发1 小时前
腾讯云 CloudBase 登上 WAIC:我们为 Agent 重新设计了云的生产线
后端
星栈2 小时前
从装一堆工具到看懂 Node 工程化思维:我的项目复盘记录
后端·node.js
用户059540174462 小时前
把记忆存储的回归测试从手工换成 Playwright + GitHub Actions,线上缺陷降低 80%
前端·css
触底反弹2 小时前
🚀 从 DOM0 级到 React 合成事件:前端事件监听的 20 年演进史
前端·react.js·面试
65岁退休Coder2 小时前
LangChain v1.3.4 笔记 - 05 Agent 上下文记忆
后端
kyriewen2 小时前
我给前端项目的接口请求套了6层保护——才发现以前一直在裸奔
前端·javascript·面试
颜酱2 小时前
04 | 召回前置准备:搭好召回所需的四个数据库
前端·人工智能·后端
JaneConan2 小时前
鸿蒙 韶非 UI 系列:能力调用 startAbilityForResult,跳能力拿回参,鸿蒙能力路由入门
后端·harmonyos
晴空了无痕2 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes