はじめに
QML の WebView 内をゴニョゴニョしたいなと思って調べると、evaluateJavaScript すれば出来るよ!と書いてあったのですが、どうやら Qt Quick 2.0 の QtWebKit 3.0 からは API が変わったようです。
そこでもう少し調べてみると experimental で evaluateJavaScript が使えることが分かりました。
そこで色々試してみた内容をメモします。
使い方
WebView の使い方は以下を参照:
例えば背景を白くするとかだったらこんな感じです。
import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 Rectangle { width: 960 height: 540 WebView { url: 'https://twitter.com/' anchors.fill: parent onLoadProgressChanged: { progressBar.width = parent.width * loadProgress / 100; } onLoadingChanged: { if (loadRequest.status == WebView.LoadSucceededStatus) { var js = 'document.body.style.backgroundColor = "#fff";'; experimental.evaluateJavaScript(js); } } } Rectangle { id: progressBar anchors.left: parent.left anchors.top: parent.top width: 0 height: 5 color: (width == parent.width) ? 'transparent' : '#aa000000' } }
外と中の JavaScript のやり取りの仕方
2種類あります。
- evaluateJavaScript の第2引数に指定するコールバックで値を受け取る
- navigator.qt.postMessage で外側の onMessageReceived で受け取る
◆ evaluateJavaScript の第2引数に指定するコールバックで値を受け取る
eval で最後に評価した値が第2引数のコールバックの引数へ渡されます。以下のようにすると Tweet の中身を取得出来ます。
import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 Rectangle { width: 960 height: 540 WebView { url: 'https://twitter.com/' anchors.fill: parent onLoadProgressChanged: { progressBar.width = parent.width * loadProgress / 100; } onLoadingChanged: { if (loadRequest.status == WebView.LoadSucceededStatus) { var js = '(function() {' + 'var i, result = "";' + 'var tweets = document.getElementsByClassName("tweet-text");' + 'return Array.prototype.map.call(tweets, function(t) {return t.innerText;});' + '})()' experimental.evaluateJavaScript(js, function(tweets) { for (var i in tweets) console.log(i, tweets[i]); }); } } } ...(略) }
result には Array でなくて Object が入っているのに注意。ちょっと試してみましたが、中から外へは Object は単一のキーを持っているもの(e.g. {hoge: 'piyo'}) しか無理?なようです。
◆ navigator.qt.postMessage で外側の onMessageReceived で受け取る
長いですが experimental.preferences.navigatorQtObjectEnabled を true にする必要があります。また postMessage でやり取りできるデータは文字列のみのようです。
import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 Rectangle { width: 960 height: 540 WebView { url: 'https://twitter.com/' anchors.fill: parent onLoadProgressChanged: { progressBar.width = parent.width * loadProgress / 100; } experimental.preferences.navigatorQtObjectEnabled: true onLoadingChanged: { if (loadRequest.status == WebView.LoadSucceededStatus) { var js = 'navigator.qt.onmessage = function(ev) {' + 'navigator.qt.postMessage(ev.data);' + '}'; experimental.evaluateJavaScript(js); } experimental.postMessage('fugafuga'); } experimental.onMessageReceived: { console.log(message.data); } } ...(略) }
これで QML 側から experimental.postMessage で 'fugafuga' を送って、それが WebView 内の navigator.qt.onmessage を経由して navigator.qt.postMessage され、QML 側の experimental.onMessageReceieved に送られてきました。
おまけ
experimental を使わなくても WebView.data[1] に evaluateJavaScript が入ってました。
var js = '(function() {' + 'var i, result = "";' + 'var tweets = document.getElementsByClassName("tweet-text");' + 'return Array.prototype.map.call(tweets, function(t) {return t.innerText;});' + '})();'; data[1].evaluateJavaScript(js, function(tweets) { for (var i in tweets) console.log(i, tweets[i]); });
おわりに
QtWebKit 3.0 用に Qt Creator の補完が働いてくれなくてイライラしてます。