采用Qt + QML + Vue构建应用程序(附源码)

使用了Qt多年,从widget到qml,尽管qml相比widget的确是方便了不少,但是目前仍然缺乏成熟组件库,稍复杂或美观的界面都需要自己造轮子,相对于HTML这边就丰富了很多,其发展已经多年,社区很活跃,有很多成熟的方法和技术可以选择,如vue和react,它们都有其成熟的UI组件库,可拿来即用,要方便很多。

本文将介绍一种基于qml加载vue的方法来构建跨平台应用程序,利用qml作为UI框架的优势,结合vue实现更加灵活和便捷的开发。这种组合将为开发者提供更多的选择和方便。

目录

1 建立qt qml工程

2 qml加载vue

2.1 先准备vue工程

2.2 将vue编译成html

2.3 加载编译后的html到qml

3 qml和vue之间接口的相互调用

3.1 建立通信

3.1.1 qml中添加webchannel

3.1.2 vue中添加webchannel

3.2 vue调用qml接口

3.3 qml调用vue接口

4 源代码


运行效果如下图:

下面介绍构建过程

1 建立qt qml工程

本文用Qt5.15.2版本

编译工具的时候不能选MingGW,这里我选VS2019 64bit编译工具

 建完之后的工程

在资源中加入一个简单的login_test.html登陆作为测试页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login Page</title>
    <style>
        /* 添加一些基本的样式 */
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
        }
        form {
            background-color: #fff;
            padding: 20px;
            margin: 20px auto;
            max-width: 500px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
        }
        label {
            display: block;
            margin-bottom: 10px;
        }
        input[type="text"],
        input[type="password"] {
            display: block;
            width: 100%;
            padding: 10px;
            margin-bottom: 20px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button[type="submit"] {
            background-color: #4CAF50;
            color: #fff;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button[type="submit"]:hover {
            background-color: #3e8e41;
        }
    </style>
</head>
<body>
    <form>
        <h1>Login</h1>
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username">

        <label for="password">密码:</label>
        <input type="password" id="password" name="password">

        <button type="submit">登陆</button>
    </form>
</body>
</html>

 修改main.qml,加入webengineView,url设置为资源文件中的html页

编译并运行测试

qml已成功拉起了html

接下来我们利用vue工程生成一个html单页应用,然后替换这个测试的html页

2 qml加载vue

2.1 先准备vue工程

运行以下命令创建新的Vue项目:

npm install -g @vue/cli
vue create ui-prj

然后按照提示进行配置,选择需要的特性,其中vue router必须,其他可选。

创建之后,在src/views目录新建两个页

  • userInfoReg.vue:用户注册
  • dataMgr.vue:数据管理

我们用vue的组件库element-ui来构造页面。然后将这两个页挂载到vue router中:

import Vue from 'vue'
import Router from 'vue-router'
import userInfoReg from '@/views/userInfoReg'
import datamgr from '@/views/dataMgr'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/userInfoReg',
      name: 'userInfoReg',
      component: userInfoReg
    },
    {
      path: '/dataMgr',
      name: 'dataMgr',
      component: dataMgr
    }
  ]
})

其中userInfoReg.vue界面部分如下:

<template>
	<ContainerExt title="数据管理">
    <div class="main_c">
      <div class="headinfo_c">
        <div class="searchinput_c">
          <div class="searchname_c">
            <span>
              姓名:
            </span>
            <el-input
              placeholder="请输入姓名"
              prefix-icon="el-icon-search"
              v-model="searchname"
              style="width: 150px;"
              clearable>
            </el-input>
          </div>

          <div class="searchdate_c">
            <span>
            检查时间:
            </span>
            <el-date-picker
              v-model="searchdate"
              type="daterange"
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期">
            </el-date-picker>
          </div>

        </div>
        <div class="searchbotton_c">
            <el-button type="primary"  @click="handleSearch">查询</el-button>
        </div>
      </div>
      <div class="databoard_c">
            <el-table
                :data="tableData"
                v-loading="listLoading"
                highlight-current-row
                border
                style="width: 100%" height="100%"
                @row-click="handleRowClick"
                :header-cell-style="{background:'#f5f7fa'}">
                <el-table-column
                  prop="cid"
                  label="ID"
                  align="center">
                </el-table-column>
                <el-table-column
                  prop="name"
                  label="姓名"

                  align="center">
                </el-table-column>

                <el-table-column
                  prop="birthday"
                  label="生日"

                  align="center">
                </el-table-column>

                <el-table-column
                  prop="sex"
                  label="性别"
                  align="center">
                </el-table-column>

                <el-table-column
                  prop="zs"
                  label="检测值"
                  align="center">
                </el-table-column>

                <el-table-column
                  prop="checktime"
                  label="检查时间"

                  align="center">
                </el-table-column>

                <template slot="empty">
                    <el-empty :image-size="100" description="空空的"></el-empty>
                </template>
            </el-table>
      </div>

      <div class="tail_c">
          <div  class="tail2_c">
            <div>
              <el-button type="primary" style="width:113px;height: 55px;font-size: 18px;" @click="handlehistory">历史检查</el-button>
            </div>
            <div>
              <el-button type="primary" style="width:113px;height: 55px;font-size: 18px;" @click="handlegoon">继续检查</el-button>
            </div>
            <div>
              <el-button type="primary" style="width:113px;height: 55px;font-size: 18px;" @click="handleback">回首页</el-button>
            </div>
          </div>
      </div>
    </div>
	</ContainerExt>

</template>

详细的代码可在本文底部下载链接中获取。在编写前端之后,用webpack去编译,将vue转化成html

2.2 将vue编译成html

运行如下命令

npm run install
npm run build

这将使用Webpack等构建工具将Vue应用程序转换为HTML、CSS和JavaScript文件,并将这些文件放置在一个名为“dist”的目录中。

2.3 加载编译后的html到qml

将dist目录中的文件全部拷贝至qt qml工程中的html目录下,替换之前的html文件,并将这些文件加入qt资源文件中。

3 qml和vue之间接口的相互调用

现在qml和vue之间还是相互独立的,qml无法调用vue的接口,vue也无法调用qml中的函数。为了实现互调功能,需要实现两点,一,建立qml和vue之间的通信;二,约定调用的接口规则。

通讯通道可以有一条,也可以是多条,我们这里只建立一条共享通道,供vue页面和qml之间共享使用。所有接口消息都走共享通道,用消息路由做分离,区分何种消息路由到何处做处理,我们这里用hash表做消息分离,用接口名做key,查找到对应接口处理函数即转发调用。

3.1 建立通信

建立通信的方案有一些,比如有:1)websocket、2)webchannel,这里我们采用qt自己提供的webchannel。

我们只建一个webchannel通道,让所有vue页面和qml共享,提高复用减少开销。

3.1.1 qml中添加webchannel

添加webchannel,并让webengine引用

import "webChFunc.js" as WebChFunc

    QtObject {
        id: qtJS
        WebChannel.id: "QTJS"
        
        //供qml调用vue的共享接口
        signal callhtml(string func, string args)

        //vue->qml共享处理,将对应接口消息路由到qml对应接口
        function callqml(func, args) {
            //用nameToFunc哈希表做消息分离
            WebChFunc.nameToFunc[func](args);
        }
    }

    WebChannel {
        id:webch
        registeredObjects: [qtJS]
    }

    WebEngineView {
        id:webview
        z:100
        anchors.fill: parent
        webChannel: webch

        url:"qrc:/html/index.html"
    }

3.1.2 vue中添加webchannel

在qt自带的webchannel html例子下找到qwebchannel.js,本文用例是在C:\Qt\Examples\Qt-5.15.2\webchannel\shared\qwebchannel.js,将此文件拷贝至qml vue工程src/目录,并更名qt5.15webchannel.js(因为各版本的webchannel略有不同,如果你换了qt版本比如qt5.12,需要取这个版本中的js文件),直接使用会报错:

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

需要修改一下导出方式:

//注释掉这块
//required for use with nodejs
// if (typeof module === 'object') {
//     module.exports = {
//         QWebChannel: QWebChannel
//     };
// }

//改成这种导出方式
export default {
  QWebChannel
};

然后在vue中引用webchannel,并使用,修改main.js,加入如下代码:

// 导入 qwebchannel.js
import qwebchannel from './qt5.15webchannel.js';

//定义一个全局共享的qtCall,供所有页面调用qml的接口
export var qtCall = null;
//定义一个全局共享qtHandle哈希表,用来路由qml->vue到对应页面处理
export var qtHandle = {};

if (process.env.NODE_ENV === 'production')
{
  new qwebchannel.QWebChannel(qt.webChannelTransport, (channel) => {
    qtCall = channel.objects.QTJS.callqml;

    channel.objects.QTJS.callhtml.connect((funcname, args) => {
      qtHandle[funcname](args);
    });
  });
}

3.2 vue调用qml接口

根据事先设计的方式,按如下规则定义和调用接口:

  • 定义qml的接口

将定义写入文件webChFunc.js中,如添加一条数据库记录

nameToFunc["addCheckTb"] = function(args) {
    var argobj = JSON.parse(args);
    var cid, checktime, name, sex, birthday, height, weight, ts = 0, zs = 0, sos = 0, reportdir = '', conclusion = '', checkpicdir = '', sfz = '';

    cid = argobj.cid;
    checktime = argobj.checktime;
    name = argobj.name;
    sex = argobj.sex;
    birthday = argobj.birthday;
    height = argobj.height;
    weight = argobj.weight;

    console.log("addCheckTb", cid, checktime, name, sex, birthday, height, weight, ts, zs, sos, reportdir, conclusion, checkpicdir, sfz);

    DB.insertCheckTb(cid, checktime, name, sex, birthday, height, weight, ts, zs, sos, reportdir, conclusion, checkpicdir, sfz);
}

addCheckTb就是接口名,参数是JSON格式的字符串

注意:如果这个处理函数非常消耗CPU,可能阻塞UI线程,影响UI的响应,出现假死现象。因此某些耗资源的操作需要借助qt C++里的线程,要用qml调用C++的接口方式处理

  • vue调用qml某接口

在vue某页需要调用的地方,写如下形式代码,例如调用上例的接口:

    if (qtCall != null) {
        qtCall("addCheckTb", JSON.stringify(qtarg));
    }

qtCall第一个参数是qml接口名,第二个参数是接口的,JSON字符串。

3.3 qml调用vue接口

按如下规则定义和调用接口:

  • 定义vue的接口

首先在vue的methods下定义方法,例如列表的返回结果的处理

methods: {
    getCheckListRsp(rsp) {
       console.log("getCheckListRsp: " + rsp);
       let rspobj = JSON.parse(rsp);
       this.tableData = rspobj.data;
       this.listLoading = false;
    },

}

然后在初始时将getCheckListRsp挂载到消息路由表中

created() {
      //挂载getCheckListRsp到消息路由表中
      qtHandle["getCheckListRsp"] = this.getCheckListRsp;

}
  • qml调用vue的接口

在qml中调用vue接口的方式如下:

    qtJS.callhtml("getCheckListRsp", JSON.stringify(rsp));

在需要返回数据的地方调用qtJS.callhtml,第一个参数是vue中的接口名,第二个参数是JSON字符串格式的参数。

4 源代码

https://download.csdn.net/download/chyly/87781328

本文介绍的方法是通过使用HTML来完全接管Qt QML的前端。这种方法允许您在Qt应用程序中利用HTML和CSS来构建用户界面,从而提供更大的灵活性和自定义性。接下来的文章将探讨一种更加灵活的方法,即让Qt QML接管部分前端,并与Qt自带的组件进行混合显示。这种混合显示的方式可以在需要结合Qt的强大功能和自定义UI的场景中发挥作用,为应用程序带来更广泛的应用范围。

技术交流微信:shangfengkj

长沙尚峰https://www.sungfun.com

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值