目录
2.2 QML中尽量少的调用业务内容(Q_INVOKABLE尽量少)
基础知识
1. Debug和Release区别
今天在自测过程中发现Debug和Release二者测试结果不同,
原因是Debug模式比Release模式运行略慢,在某些场景下,会造成测试结果不同。比如,存储文件时,写入结果可能会与程序运行速度有关,那么在这两种不同情况下就会结果不同。
2. 信号与槽函数
MainCtrl mainCtr;
mainCtr.registerAllContext(app, engine);
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated, &app,
[url, &mainCtr](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) {
QCoreApplication::exit(-1);
} else {
mainCtr.sigUiloadEnded();
}
},
Qt::QueuedConnection);
engine.load(url);
return app.exec();
load(url)使得程序先展示主界面main.qml 给用户,避免等待,提升客户体验度。load完成后
QQmlApplicationEngine才会触发信号,从而使得mainCtr对象去加载一些必要的逻辑代码。
return app.exec();是为了将主界面阻塞住,不会一闪而过。
3. 回调函数
auto func0 = [=](const QJsonObject &obj) { reportCheck(obj); };
taskBase->setFunReportInfo(func0);
如上回调函数为 reportCheck(obj),setFunReportInfo(func0)将回调函数赋值给某个参数,func0为lambda表达式的写法。
mFunReportInfo,当某处使用该参数时便会调用回调函数reportCheck(obj)。如下code中通过 mFunReportInfo(val.toObject());将val值传给回调函数并执行reportCheck。
if ("funReport" == type) {
if (mFunReportInfo) {
mFunReportInfo(val.toObject());
} else {
qDebug() << "mFunReportInfo nullptr";
}
}
开发有感
1. 善用内存
最近在做数据查询的业务。对于要展示到qml中的部分数据(如通过时间段从库中查询的数据),也即是从数据库中取出的数据要存放到内存中去取用、展示等,而不是再另存入文件中从而增加IO次数,降低页面展示效率。
1.1 DB查询优化
在NoSQL中使用key:value的存储方式,查询时先查出所有key值(allKeyList),(而不是连key带value所有查出)然后根据需要再从allKeyList中拿key在DB中查询value,放入内存中进行取用,可大大减少内存的使用。
1.2 海量数据(千万级以上)的优化
基于上一条’DB查询优化’的思想,当需要的数据的海量级的时候 ,我们可以采取随用随查的方法减轻内存压力。如在一个带有分页功能的查询页面中,少量数据可以采用上一条方式查出所有需要的value后放入内存中使用,而海量数据的时候可以在点击某一页的时候再去从DB中查找相应Value,便可以大大减少内存的使用。
2. 善于解耦
2.1 高度可重复使用QML控件
qml控件使用时,各个属性、信号、业务功能处理要放置在使用控件的外层,而不是定义时的该文件中,以便于控件的后续维护和重复使用。
如在有多个搜索功能的场景中:
该页面对应SelectTimePeriodItem.qml:
Item {
property var curTabType: ""
property bool enableInput: false
signal clickLoadBtn(var obj)
Row {
spacing: 5
SelectTime {
id: idStartSelectTime
width: 130
height: keyHeight
}
Item {
width: 10
height: 30
Text {
width: parent.width
height: parent.height
text: "-"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
SelectTime {
id: idEndSelectTime
width: 130
height: keyHeight
}
RectButton {
id: idImportBtn
borderColor: "#d3d3d3"
colorArray: ["orange", "transparent", "transparent"]
width: 60
height: 30
borderWidth:1
bText: gTrans.getTrans("load")
onBtnClicked: {
//业务处理
clickLoadBtn({"st": idStartSelectTime.selectedTime, "et": idEndSelectTime.selectedTime})
}
}
RectButton {
id: idOutputBtn
colorArray: ["orange", "transparent", "transparent"]
width: 60
height: 30
borderWidth:1
borderColor: "#d3d3d3"
bText: gTrans.getTrans("output")
onBtnClicked: {
//业务处理
gDefectDataCtrl.outputCSVFile({"st": idStartSelectTime.selectedTime, "et": idEndSelectTime.selectedTime,"type":curTabType})
}
}
InputCtrlWithBtn {
trans: gTrans.getTrans("enterQRcode")
width: keyWidth + valueHeight
height: keyHeight
keyWidth: 110
keyHeight: 30
valueWidth: 200
valueHeight: 30
keyFontSize: 10
valueFontSize: 10
keyTxtColor: "black"
borderColor: "black"
valueTxtColor: "black"
btnText: gTrans.getTrans("search")
btnWidth: 60
enabled: enableInput
onSigBtnClicked: {
//业务处理
console.log("value", value)
gDefectDataCtrl.getDataByQRcode(value)
}
}
}
function enableInputTxt(curTabType) {
return ("liXunDetectionRecord" === curTabType)
}
}
以上SelectTimePeriodItem.qml中有三处需要处理的功能业务 ,且都在该控件本身编写这些业务,这样的方式造成后续再次使用SelectTimePeriodItem.qml控件时,就需要改写三处不同的业务功能。这种编码方式灵活性、可维护性较低,优化后如下:
Item {
property var curTabType: ""
property bool enableInput: false
signal sigOp(var obj)
Row {
spacing: 5
SelectTime {
id: idStartSelectTime
width: 130
height: keyHeight
}
Item {
width: 10
height: 35
Text {
width: parent.width
height: parent.height
text: "-"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
SelectTime {
id: idEndSelectTime
width: 130
height: keyHeight
}
RectButton {
id: idImportBtn
borderColor: "#d3d3d3"
colorArray: ["orange", "transparent", "transparent"]
width: 60
height: 35
borderWidth: 1
bText: gTrans.getTrans("load")
onBtnClicked: {
//交给信号统一处理
sigOp({"type": "findByDateTime", "st": idStartSelectTime.selectedTime, "et": idEndSelectTime.selectedTime})
}
}
RectButton {
id: idOutputBtn
colorArray: ["orange", "transparent", "transparent"]
width: 60
height: 35
borderWidth:1
borderColor: "#d3d3d3"
bText: gTrans.getTrans("output")
onBtnClicked: {
//交给信号统一处理
sigOp({"type": "outPutData"})
}
}
InputCtrlWithBtn {
trans: gTrans.getTrans("enterQRcode")
width: keyWidth + valueHeight
height: keyHeight
keyWidth: 110
keyHeight: 35
valueWidth: 200
valueHeight: 35
keyFontSize: 10
valueFontSize: 10
keyTxtColor: "black"
borderColor: "black"
valueTxtColor: "black"
btnText: gTrans.getTrans("search")
btnWidth: 60
enabled: enableInput
onSigBtnClicked: {
console.log("value", value)
//交给信号统一处理
sigOp({"type": "findByDmCode", "dmCode": value})
}
}
}
function enableInputTxt(curTabType) {
return ("liXunDetectionRecord" === curTabType)
}
}
使用 SelectTimePeriodItem:
SelectTimePeriodItem {
id: idSelectTimePeriodItem
curTabType: idTabDataTable.curType()
enableInput: enableInputTxt(curTabType)
//统一处理功能业务
onSigOp: {
var type = obj["type"]
obj["tabName"] = curTabType
if ("findByDateTime" === type || "findByDmCode" === type) {
console.log("for clearListMode")
idTabDataTable.clearListModel()
idTabDataTable.showPageIndicator(false)
idTabDataTable.setPageCnt(0)
if ("findByDmCode" === type) {
if ("" === obj["dmCode"]) {
tipTxt.textChanged(gTrans.getTrans("inputDmCodeNull"))
return;
}
}
gStatisticsTable.searchData(obj)
} else if("outPutData" === type) {
gStatisticsTable.outPutCsv(curTabType)
}
}
}
优化后,控件SelectTimePeriodItem的封装性更高,code耦合度降低,易于使用和维护。
2.2 QML中尽量少的调用业务内容(Q_INVOKABLE尽量少)
如:当有多种类型的 搜索功能时(根据时间载入、输入二维码等)
可将QML中的多个Q_INVOKABLE函数合并到一个函数中进行统一处理
优化前:
//根据时间载入
Q_INVOKABLE void getDataByTime(const QJsonObject &obj);
//根据二维码搜索
Q_INVOKABLE void getDataByQRcode(const QString &QRcode);
优化后:
Q_INVOKABLE void searchData(const QJsonObject &obj);
void StatisticsTable::searchData(const QJsonObject &obj) {
qDebug() << "searchData obj=" << obj;
if (obj.contains("type")) {
auto &&type = obj.value("type").toString();
if ("findByDateTime" == type) {
if (!mSearchingFlag) {
findByDateTime(obj); //处理时间条件
} else {
qDebug() << "mSearchingFlag true do not searchData";
}
} else if ("findByDmCode" == type) {
if (!mSearchingFlag) {
findByDmcode(obj);//处理二维码条件
} else {
qDebug() << "mSearchingFlag true do not searchData";
}
}
} else {
qDebug() << "not contain type=" << obj;
}
}
2.3 规范&简洁化coding
命名规范统一、易理解:函数名、属性名
同一个页面的多个功能保持function独立化,如下页面包括多种搜索及分页功能
搜素和分页的数据源相同,理论上可以并入同一个function中进行查询处理,但会造成功能耦合,code可阅读性降低。优化后将二者分离,各自处理控件查询功能:
Q_INVOKABLE void searchData(const QJsonObject &obj);
Q_INVOKABLE void switchPage(const QJsonObject &obj);
3. 多查询控件的查询控制
当涉及到多个查询业务(根据时间载入,根据二维码搜索),如下场景:
3.1 频繁的查询点击控制
以限制上一次查询操作未完成又开始下一轮查询操作,即用户点击‘载入’按钮后,随即多次点击输入二维码后的‘搜索’按钮。所以在每次查询操作时判通过断段flag 来决定是否执行该轮查询操作。如下:
void StatisticsTable::searchData(const QJsonObject &obj) {
qDebug() << "searchData obj=" << obj;
if (obj.contains("type")) {
auto &&type = obj.value("type").toString();
if ("findByDateTime" == type) {
if (!mSearchingFlag) { //查询标志为false便可进入查询操作
findByDateTime(obj);
} else {
qDebug() << "mSearchingFlag true do not searchData";
}
} else if ("findByDmCode" == type) {
if (!mSearchingFlag) { //查询标志为false便可进入查询操作
findByDmcode(obj);
} else {
qDebug() << "mSearchingFlag true do not searchData";
}
}
} else {
qDebug() << "not contain type=" << obj;
}
}
或者在点击查询控件,查询结果未得到时将所有控件置为不可点击状态,让用户等待,使UI友好交互。
3.2 并发控制
在上述查询功能中,每处查询都需要设置多线程的并发功能,在查询数据的同时放置ui加载卡顿,进行多线程调度,提高程序运行效率。
QtConcurrent::run([=]() {
mSearchingFlag = true;
auto &&list = pDbCtrl->getDbInfo(obj, "liXunDmCode");
mHashData.insert(tabName, list);
if (0 == list.size()) {
sigUpdate("noData", {});
} else {
QJsonObject objRes;
objRes.insert("recordNum", list.size());
objRes.insert("pageCnt", getPageCnt(list.size()));
sigReport("searchDmCodeResult", objRes);
sendDataAfterSearch(list);
}
mSearchingFlag = false;
});