當前項目為c/s客戶端,采用qt4.8.7,需要使用儀表盤、折線圖、柱狀圖等,曾經使用過qwt和自定義的圖形控件,但是都不盡如人意。最近發現ECharts控件不錯。為此就要在qt端使用web的技術。為此使用了QWebview的控件。關於它的使用網上有很多,一開始也沒有深究,借鑒了前人的經驗立即就使用了,而且也能正常使用。當時主要使用view->page()->mainFrame()->evaluateJavaScript這種方式。使用的形式是將需要顯示的數據由qt主程序讀取數據庫,將返回的數據組成json格式的字符串傳遞給js執行。但是由於定時刷新頻率較快1~5s,數據量較大(光數據就有172800~34560個)。所以導致程序內存增長很快。這是始料未及的,原以為qt的瀏覽器控件應該會像瀏覽器一樣有垃圾回收,但是無奈,貌似沒有或者不起作用,找了很久也沒有相關資料。網上很多說早期的QWebKit存在內存泄漏、不完美、兼容性不好等諸多問題,看來是的了,畢竟早期qt團隊貌似只是簡單的應用,融合的不是特別好,尤其是4.8版本,好像5以后稍微好點(這些也就是網上道聽途說的,是否為真,讀者自判,o(* ̄︶ ̄*)o)。
於是乎就要解決這個內存持續增長的問題,網上既然如前面說QWebKit不是很好,qt5.6以后改為了QtWebEngine,本想忍痛升級吧,結果看到下圖
居然mingw版不支持,好吧,這不改動太大了,還要裝msvc版,算了,另尋它圖吧。於是又回到QWebKit/QWebview ,看有沒有好的方式。
在於做網頁的同事交流后,試過各種的辦法(有考慮是否可以加入定期垃圾回收,貌似不可行,在對QWebKit/QWebview運行機制不了解的情況下作罷,關於qt的書實在太少了,關於有介紹QWebKit/QWebview的書就更沒有了)。
最終還是繼續查看qt的幫助文檔(類的介紹英文版),查到QWebFrame類中還有下面的函數(作用:把窗體對象給js用來調用。具體看help)
進而,查到void javaScriptWindowObjectCleared ()函數(作用:This signal is emitted whenever the global window object of the JavaScript environment is cleared, e.g., before starting a new load.)
以上的函數合起來給我們的思路就是讓js來主動調用qt程序的對象。而evaluateJavaScript的模式是qt調用js。剛好兩者有點類似互逆的形式。
這個網上有資料,搜索QT和JavaScript互調就會有資料,隨便列出兩份,讀者可以自己看。
http://blog.csdn.net/b711183612/article/details/50593068
http://blog.csdn.net/allenjiao/article/details/44963131
以下是我項目中的應用,簡單列點代碼。
構造函數中:
1 connect(ui->webLine->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), 2 this, SLOT(sltJavaScriptFromWinObject())); 3 ui->webLine->load(QUrl("file:///"+qApp->applicationDirPath()+"/Echart/page/commLineChart.html")); 4 5 ui->webLine->settings()->setObjectCacheCapacities(0,0,0); 6 7 connect(ui->webLine,SIGNAL(loadFinished(bool)),this,SLOT(sltInitial()));
槽函數:
void 類名::sltJavaScriptFromWinObject() { ui->webLine->page()->mainFrame()->addToJavaScriptWindowObject("mywebkit",this); }
js將要調用的函數(重點注意:這個函數必須是公共槽函數public slots:,注:其實根據其他文章說明“方法前需要Q_INVOKABLE修飾”,具體見參考。此外這里又有兩種方式,1、getDataLine是由qt獲得數據最后讓js來直接獲取數據字符串。2、其它兩個函數則是直接讓js調用獲取數據的函數。最后證明內存的消耗是差不多的。但是1的使用需要js定時調用,qt主程序也要定期刷新數據(頻率要比前者高),而2則直接在js中設置刷新頻率即可,故在消耗差不多的前提下最終選擇2方法)
1 public slots: 2 QString TestrefreshRTData(); 3 QString TestrefreshLine(); 4 QString getDataLine();
TestrefreshLine函數(包括cjson的使用):
1 QString 類名::TestrefreshLine() 2 { 3 float fMinVol=qAbs(mpDataNHour.values().first().value(KEY_VOL)); 4 float fMaxVol=qAbs(mpDataNHour.values().first().value(KEY_VOL)); 5 float fMinCur=qAbs(mpDataNHour.values().first().value(KEY_CUR)); 6 float fMaxCur=qAbs(mpDataNHour.values().first().value(KEY_CUR)); 7 //先創建空對象 8 cJSON *json = cJSON_CreateObject(); 9 cJSON *array=NULL; 10 cJSON_AddItemToObject(json,"commLineData",array=cJSON_CreateArray()); 11 QMap<QString,MP_VALUE>::iterator it; 12 for ( it = mpDataNHour.begin(); it != mpDataNHour.end(); ++it ) 13 { 14 QString datetime=it.key(); 15 float vol=qAbs(it.value().value(KEY_VOL)); 16 float cur=qAbs(it.value().value(KEY_CUR)); 17 fMinVol=fMinVol>vol?vol:fMinVol; 18 fMaxVol=fMaxVol<vol?vol:fMaxVol; 19 fMinCur=fMinCur>cur?cur:fMinCur; 20 fMaxCur=fMaxCur<cur?cur:fMaxCur; 21 cJSON *obj=cJSON_CreateObject(); 22 cJSON_AddItemToArray(array,obj); 23 cJSON_AddStringToObject(obj,"date",datetime.toStdString().c_str()); 24 cJSON_AddStringToObject(obj,"vol",QString("%1").arg(vol).toStdString().c_str()); 25 cJSON_AddStringToObject(obj,"cur",QString("%1").arg(cur).toStdString().c_str()); 26 } 27 28 fMinCur=fMinCur*(1-PROPORTION_CUR); 29 fMaxCur=fMaxCur*(1+PROPORTION_CUR); 30 fMinVol=fMinVol*(1-PROPORTION_VOL); 31 fMaxVol=fMaxVol*(1+PROPORTION_VOL); 32 cJSON_AddStringToObject(json,"minCur",QString::number(fMinCur, 'f', DECIMAL_CUR).toStdString().c_str()); 33 cJSON_AddStringToObject(json,"maxCur",QString::number(fMaxCur, 'f', DECIMAL_CUR).toStdString().c_str()); 34 cJSON_AddStringToObject(json,"minVol",QString::number(fMinVol, 'f', DECIMAL_VOL).toStdString().c_str()); 35 cJSON_AddStringToObject(json,"maxVol",QString::number(fMaxVol, 'f', DECIMAL_VOL).toStdString().c_str()); 36 37 char *out=cJSON_Print(json); // json對象轉字符串 38 39 cJSON_Minify(out); // 去掉字符串中的換行和縮進 40 QString sz = QString(out); 41 free(out);//注意:這個千萬別忘記,網上的很多人的資料把這個忘記了,造成內存泄漏 42 cJSON_Delete(json); 43 44 /*********qt調用js,內存持續增長**********/ 45 // sz.replace(QRegExp("\""), "\\\""); 46 // QString szValue = QString("optionData(\"%1\");").arg(sz); 47 // ui->webLine->page()->mainFrame()->evaluateJavaScript(szValue); 48 49 /***********js調用qt對象***************/ 50 QString szValue = QString("%1").arg(sz); 51 return szValue; 52 // return szValue.toStdString();//不用轉換成string,QString可識別。 53 54 }
js:
1 //------------------------------------折線圖--------------------------------------------------> 2 $("#clc").css("width",window.innerWidth).css("height",window.innerHeight); 3 4 //var lineChart=echarts.getInstanceByDom(document.getElementById("clc")); 5 //echarts.dispose(lineChart); 6 var lineChart = echarts.init(document.getElementById("clc")); 7 var xData=[]; 8 var dyData=[]; 9 var dlData=[]; 10 var maxVol=10; 11 var minVol=0; 12 var maxCur=10; 13 var minCur=0; 14 var timeSplit=5;//時間間隔 每5秒執行一次 15 16 var lineOption = { 17 legend: { 18 data:["單位:V","單位:A"], 19 x:"center", 20 y:"top", 21 textStyle:{ 22 color:"#fff" 23 }, 24 formatter: function (name) { 25 if(name=="單位:V"){ 26 return "電壓"; 27 }else{ 28 return "電流"; 29 } 30 } 31 }, 32 grid:{ 33 x:45, 34 x2:45, 35 y:45, 36 y2:45 37 }, 38 tooltip: { 39 trigger: 'axis', 40 formatter: function (params,ticket,callback) { 41 if(params.length==2){ 42 return "時間="+params[0].data[0]+"<br/><span style='color:#1e96f4;'>電壓值="+params[0].data[1]+"</span>" + 43 "<br/><span style='color:#fd4171;'>電流值="+params[1].data[1]+"</span>"; 44 }else { 45 if(params[0].seriesName=="單位:V"){ 46 return "時間="+params[0].data[0]+"<br/><span style='color:#1e96f4;'>電壓值="+params[0].data[1]+"</span>"; 47 }else{ 48 return "時間="+params[0].data[0]+"<br/><span style='color:#fd4171;'>電流值="+params[0].data[1]+"</span>"; 49 } 50 } 51 52 53 }, 54 axisPointer: { 55 type: 'line' 56 } 57 }, 58 xAxis: [ 59 { 60 type: 'time',//#8296a2 61 show:true, 62 boundaryGap: false, 63 splitLine: { 64 show: false 65 }, 66 axisLine:{ 67 lineStyle:{ 68 color:"#fff" 69 } 70 }, 71 axisTick:{ 72 alignWithLabel:true 73 }, 74 splitNumber:30, 75 min:0, 76 max:0 77 //data:xData 78 } 79 ], 80 yAxis: [ 81 { 82 name:"單位:V", 83 nameTextStyle:{ 84 color:"#fff" 85 }, 86 type: 'value', 87 splitLine: { 88 show: true, 89 lineStyle:{ 90 color:"#5c6f85" 91 } 92 }, 93 position:"left", 94 axisLine:{ 95 lineStyle:{ 96 color:"#fff" 97 } 98 }, 99 min:minVol, 100 max:maxVol 101 }, 102 { 103 name:"單位:A", 104 nameTextStyle:{ 105 color:"#fff" 106 }, 107 nameLocation:"end", 108 type: 'value', 109 position:"right", 110 boundaryGap: [0, '100%'], 111 splitLine: { 112 show: false, 113 }, 114 position:"right", 115 axisLine:{ 116 lineStyle:{ 117 color:"#fff" 118 } 119 }, 120 min:minCur, 121 max:maxCur 122 } 123 ], 124 series: [ 125 { 126 name:"單位:V", 127 type: 'line', 128 smooth: true, 129 showSymbol: false, 130 hoverAnimation: false, 131 itemStyle:{ 132 normal:{ 133 color:"#1e96f4", 134 lineStyle: { 135 width: 1, 136 } 137 } 138 }, 139 data: dyData 140 141 } , 142 { 143 name:"單位:A", 144 yAxisIndex:1, 145 type: 'line', 146 smooth: true, 147 showSymbol: false, 148 hoverAnimation: false, 149 itemStyle:{ 150 normal:{ 151 color:"#fd4171", 152 lineStyle: { 153 width: 1, 154 } 155 } 156 }, 157 data: dlData 158 } 159 160 ] 161 }; 162 //lineChart.setOption(lineOption); 163 164 function optionData(obj){ 165 var json=$.parseJSON(obj.toString()); 166 minVol=parseFloat(json.minVol); 167 maxVol=parseFloat(json.maxVol); 168 minCur=parseFloat(json.minCur); 169 maxCur=parseFloat(json.maxCur); 170 var startDate=json.startDate; 171 var endDate=json.endDate; 172 173 dyData=[]; 174 dlData=[]; 175 datas=json.commLineData; 176 for(var i=0;i<datas.length;i++){ 177 var vol=datas[i].vol; 178 var cur=datas[i].cur; 179 var dateTime=datas[i].date; 180 var volArr=[dateTime,vol]; 181 var curArr=[dateTime,cur]; 182 dyData.push(volArr); 183 dlData.push(curArr); 184 } 185 186 if(datas.length==0){ 187 188 lineOption.xAxis[0].show=false; 189 } 190 lineOption.xAxis[0].min=startDate; 191 lineOption.xAxis[0].max=endDate; 192 //lineOption.xAxis[0].data=xData; 193 lineOption.yAxis[0].min=minVol; 194 lineOption.yAxis[0].max=maxVol; 195 lineOption.yAxis[1].min=minCur; 196 lineOption.yAxis[1].max=maxCur; 197 lineOption.series[0].data=dyData; 198 lineOption.series[1].data=dlData; 199 //lineChart.setOption(lineOption); 200 lineChart.setOption(lineOption); 201 } 202 203 204 function reset(){ 205 lineOption.series[0].data=[]; 206 lineOption.series[1].data=[]; 207 lineChart.setOption(lineOption); 208 } 209 210 211 function setBorder(width,height){ 212 $("#clc").css("width",width+"px").css("height",height+"px"); 213 $(lineChart).resize(); 214 } 215 216 217 /*****js調用qt對象,調試時可以加入try...catch...捕獲錯誤***/ 218 $(function(){ 219 var jsonObj=window.mywebkit.refreshRTData(); 220 optionData(obj); 221 setInterval(function(){ 222 jsonObj=window.mywebkit.refreshRTData(); 223 optionData(obj); 224 },timeSplit*1000); 225 }); 226 227 /*$(function(){ 228 try{ 229 // alert("start"); 230 setInterval(function(){ 231 try{ 232 var obj=window.mywebkit.getDataLine(); 233 // alert(obj); 234 optionData(obj); 235 } 236 catch(e) 237 { 238 alert("Error_2!"); 239 } 240 },5000); 241 // alert("ok"); 242 } 243 catch(e) { 244 alert("Error_1!"); 245 } 246 });*/
最后分別列出qt調用js、js調用qt對象的內存比較數據。
qt調用js內存情況如下:
2017-11-7 107268KB 13:10 (開始運行) 200852KB 15:33 102888KB 2017-11-7 17:31(對遠程QT客戶端重啟后開始運行) 138712KB 2017-11-8 08:37 451120KB 2017-11-8 17:08 538248KB 2017-11-9 09:56 連續運行后,內存持續增長425.16M
js調用qt對象內存情況(啟動后會有一段時間的增長(10分鍾內),包括鼠標在圖形上操作會增長到60M多,之后穩定,沒有持續增長,成功):
1方法:
55600KB 2017-11-16 16:57 61056KB 2017-11-17 08:11 穩定在這個范圍
2方法: 46588KB 2017-11-16 17:07 61064KB 2017-11-17 08:11 穩定在這個范圍
參考:http://www.cnblogs.com/liushui-sky/p/7851654.html