關於qt QWebKit/QWebview 使用心得


當前項目為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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM