EntryNavi : « Qt名古屋勉強会#3が開催されました。 | メイン | 高専カンファレンス in 岐阜でQtの発表してきました »

CategoryNavi : メイン -> コンピューター -> 開発 -> Qt

QML(Qt)でSSLをアプリ内で初めて使う時に時間かかりますよ

QMLからhttpアクセスする場合は「XMLHttpRequest」を使用します。

よく出来ててURLに「https://hogehoge」とすれば自動的にopensslのプラグインを使用してくれます。
配布する場合は当然DLLを追加で同梱する必要があります。

実際に必要になるファイルはこちらを参照してください。
 「QML(Qt)でWeb系機能を使用した場合に必要になるライブラリ(Dll)


で、タイトルの通りなのですがアプリから初めて使用する場合に、自分の開発環境(Windows 7 / Core i7 / Memory 16GByte)では1秒程度かかりました。
細かく行くと「XMLHttpRequest::send()」の呼出の時に時間がかかってました。
Nokia N9では、100msec程度。

Httpアクセスのところを非同期で実行してくれるのにキックする所がブロックして時間かかるんじゃ本末転倒だよっ!という状況です。
WorkerScript」エレメントを使ってアプリ起動時に裏で1回httpsで空振りさせておけば良いような気もしますが、負けた気がしたので下記のようなアクセス用のスクリプト?を作ってみた。


/// サンプル仕様? ///
・WorkerScriptエレメントを使用する。
・上位の呼び出し元へのコールバック関数の情報を管理するためのIDを作成する。
・WorkerScriptへHttpアクセスに必要な情報と上記IDをJavaScript Objectで渡す。(※1
・別のJavaScriptファイルでコールバック関数の情報を管理する。(※2
 ここで上記IDとコールバックを結びつけて登録と引き出し用のインターフェースを用意。
 これで呼出とコールバックの順番が入れ子になっても大丈夫。(な、予定)

※1 WorkerScriptには関数オブジェクトシグナルを渡せないため。
※2 エレメントのプロパティには関数オブジェクトシグナルが保存できないため。
追記 関数オブジェクトを.toString()して保存してevalで戻せばOKでした。
さすがにシグナルはダメっぽいです。


いちおう、POSTデータも送り込めるように変数は用意していますが実際に試してません。
WorkerScriptの制限で上手に渡して工夫しないとダメかも知れません。
とりあえずってことで。



サンプル本体
import QtQuick 1.0

Rectangle {
    width: 360
    height: 360

    Text {
        id: _text
        text: qsTr("Http test")
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        property int counter: 1

        onClicked: {
            var url;
            url = "https://www.google.co.jp/";
            //url = "http://www.google.co.jp/"


            _text.text = counter;
            counter ++;

            // Httpアクセス
            url = "https://www.google.co.jp/";
            _http.requestHttp("", "GET", url, response);

//            url = "https://www.twitter.com/";
//            _http.requestHttp("", "GET", url, response2);
        }
    }

    // HTTPアクセス補助エレメント
    HttpAccess{
        id: _http
    }

    function response(text){
        console.debug("response1:");
        console.debug(text.substring(0,100));
    }
    function response2(text){
        console.debug("response2:");
        console.debug(text.substring(0,100));
    }
}
Httpアクセスの補助エレメント
// HttpAccess.qml
import QtQuick 1.0
import "httpAccessParam.js" as HttpAccessParam

WorkerScript {
    id: _root
    source: "HttpAccess.js"
    onMessage: {
        // Workerスレッドからの通信イベント
        var func = HttpAccessParam.shift(messageObject.id).func;
        func(messageObject.text);
    }

    // 呼出し簡略化用
    function requestHttp( data , method , url, on_func){
        // ユニークID作成
        var id = HttpAccessParam.getId();
        // httpアクセス情報作成
        var request = {"data": data
            , "method": method
            , "url": url
            , "id": id};
        // コールバック情報保存
        var callback = {"id" : id
            , "func": on_func};
        HttpAccessParam.push(callback);
        // スレッド開始
        _root.sendMessage(request);
    }
}
WorkerScriptの本体
// HttpAccess.js
// data
// method
// url
// id
WorkerScript.onMessage = function(message) {
    console.debug("start worker:" + message.id);

    requestHttp(message.data, message.method, message.url, message.id);
}
function requestHttp( data , method , url, param)
{
    //XMLHttpRequestオブジェクト生成
    var httpoj = new XMLHttpRequest();

    //open メソッド
    httpoj.open( method , url , true );

    //受信時に起動するイベント
    httpoj.onreadystatechange = function(){
        //readyState値は4で受信完了
        if (httpoj.readyState==4){
            //コールバック
            responseHttp(httpoj, param);
        }
    }
    //send メソッド
    httpoj.send( data );
}
function responseHttp(httpoj, param)
{
    console.debug("finish worker:" + param);
    WorkerScript.sendMessage({ "text": httpoj.responseText, "id": param })
}
コールバック情報の管理
// HttpAccessParams.js
var params = [];

//ユニークなIDを作るっぽい
var id_counter = 0;
function getId(){
    return (id_counter++) + "";
}
// 後ろに追加
function push(obj){
    params.push(obj);
}
// 特定のIDの情報を取り出す
// 基本は先入れ先だしの予定
// 後から登録したのより遅く来るのはきっと後ろに入れても問題ない・・・
function shift(id){
    var obj = params.shift();
    while(obj.id !== id){
        // 目的のものじゃなければ後ろに入れなおす
        params.push(obj);
        obj = params.shift();
    }
    return obj;
}


/// 追記 ///
※1・2についてKenji Sugitaさんよりアドバイスをいただきましたので調べて見ました。
もともと試してできないと思ってたのですが若干乗り越えました。
関数オブジェクトはプロパティに保存したりWorkerScriptへ渡せることがわかりました。
ただし、シグナルは渡せませんでした。そもそもシグナルの型は「object」で「function」じゃないので当然かもしれませんが・・・。
上のサンプルにこんなコードを追加して試してみました。
    property variant test: ""
    signal response3(string text)

    onResponse3: {
        console.debug("response3:");
        console.debug(text.substring(0,100));
        _text.text = text;
    }

// 中略

        // 通常の関数を渡せるか
        var param = {"func": response.toString()};
        console.debug("typeof param = " + typeof param);
        console.debug("typeof param.func = " + typeof param.func);
        test = param;
        console.debug("typeof test = " + typeof test);
        console.debug("typeof test.func = " + typeof test.func);
        var func = eval("(" + test.func + ")");
        console.debug("typeof test.func(eval) = " + typeof func);
        func("call 1 ok?(eval)");

        // シグナルを渡せるか
        param = {"func": response3.toString()};
        console.debug("typeof param = " + typeof param);
        console.debug("typeof param.func = " + typeof param.func);
        test = param;
        console.debug("typeof test = " + typeof test);
        console.debug("typeof test.func = " + typeof test.func);
        func = eval("(" + test.func + ")");     // ここでパースエラー
        console.debug("typeof test.func(eval) = " + typeof func);
        func("call 3 ok?(eval)");

実行するとこんな感じ
  typeof param = object
  typeof param.func = string
  typeof test = object
  typeof test.func = string
  typeof test.func(eval) = function
  response1:
  call 1 ok?(eval)
  typeof param = object
  typeof param.func = string
  typeof test = object
  typeof test.func = string
  :1: SyntaxError: Parse error





おまけ
 QtSDK 4.8RCでは、sslに必要なdllが「C:\QtSDK\Desktop\Qt\4.8.0\mingw\bin」にありませんでした。
 なのでエラーがでます。
 4.7.4の環境から以下のファイルをコピーすれば使えます。
  libeay32.dll
  libssl32.dll
  ssleay32.dll


<Category : Qt>

コメント (2)

Kenji Sugita:

※1※2eval したら関数オブジェクトになるものを設定または渡すようにしてできませんか。

あやね:

> Kenji Sugita さん
アドバイスありがとうございます。
その方法でダメだと思ってましたが関数オブジェクトは渡せることがわかりましたので記事を修正しました。
自分としては最終的にシグナルを渡したかったので、それをstringに一旦変換出来るのか?を調べないとです。

コメントを投稿

検索

Google

サイドフィード

track feed 理ろぐ
人気ブログランキング - 理ろぐ
Powered by
Movable Type 3.34