采用Qt + QML + Vue构建应用程序附源码
使用了Qt多年,从widget到qml,尽管qml相比widget的确是方便了不少,但是目前仍然缺乏成熟组件库,稍复杂或美观的界面都需要自己造轮子,相对于HTML这边就丰富了很多,其发展已经多年,社区很活跃,有很多成熟的方法和技术可以选择,如vue和react,它们都有其成熟的UI组件库,可拿来即用,要方便很多。
本文将介绍一种基于qml加载vue的方法来构建跨平台应用程序,利用qml作为UI框架的优势,结合vue实现更加灵活和便捷的开发。这种组合将为开发者提供更多的选择和方便。
目录
运行效果如下图:
下面介绍构建过程
1 建立qt qml工程
本文用Qt5.15.2版本
编译工具的时候不能选MingGW,这里我选VS2019 64bit编译工具
建完之后的工程
在资源中加入一个简单的login_test.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的场景中发挥作用,为应用程序带来更广泛的应用范围。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgebjib
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01