Qt を使用すると、(非同期の) HTTP リクエストを簡単に処理できます。このガイドでは、Qt コアと Qml でそれを行う方法を示します。 2 つの例では、ボタンを押した後に HTTP GET 要求の出力を画面に出力します。 Qml メソッドは JavaScript を使用するため、少しごまかしています。もう 1 つのメソッドは、ネットワーク用の Qt のライブラリでプレーンな C++ を使用します (QNetworkAccessManager
) および非同期部分のシグナルとスロット。
このガイドは主に、私がこれを頻繁に行っていることに気づき、コードをコピーするために既にこれを行った他のプロジェクトを探し続けているために書かれています。仲間の同僚でさえ、この特定のことについて私の GitHub を覗き見していました。
Qt を使用しない場合、おそらく curl
を使用してネットワーク リクエストを処理するでしょう。 またはヘッダーのみの http クライアント/サーバーである cpp-httplib のようなもの。私は以前に古い C++ ネットワーク http リクエストをプレーン化したことがあり、HackerNews と Lobste.rs API を解析して、ここでそれについて書いています。
このガイドの完全なコードは、私の github にあります。
基本設定
Qt Creator を使用して File
を実行します 、 New Project
. emptyQt Quick (QML) アプリケーションを選択し、ウィザードを終了します。 Qt 5.15 を使用していますが、この例は Qt 6.3 でも動作します。
これは main.qml
です ファイル レイアウト、ボタンとテキスト フィールドを含む 2 行:
Column {
spacing: 5
anchors.fill: parent
anchors.margins: 5
Row {
spacing: 5
Button {
text: "Qml HTTP GET"
}
TextField {
id: qmlResult
}
}
Row {
spacing: 5
Button {
text: "C++ HTTP GET "
}
TextField {
id: cppResult
}
}
}
C++ HTTP GET リクエスト
単純な古い C++ HTTP Get は、Qt が提供するいくつかのクラス、つまり QNetworkAccessManager
を使用します。 、 QNetworkRequest
と QNetworkReply
、リクエストの非同期を処理するためのいくつかのシグナルとスロットを含みます。
QObject から派生したクラスを作成し、それを QML Engine に登録することから始めます。 Qtbefore を実行したことがある場合は、これを何度も実行することを知っているでしょう。 qRegister
のどの形式でも /qmlRegister
月の形に依存する必要がありますが、Qt 6 ではその範囲が改善され、cmake を使用してオブジェクトを登録する場所が 1 つだけになりました。
クラスの作成と Qml の登録
NetworkExample
という名前の新しいクラスを作成します 自分でファイルを作成するか、Qt Creator Add New
を使用して、QObject に基づいています。 その場合、新しい C++ クラスを選択し、ベースとして QObject を指定します:
NetworkExample.h
#ifndef NETWORKEXAMPLE_H
#define NETWORKEXAMPLE_H
#include <QObject>
class NetworkExample : public QObject
{
Q_OBJECT
public:
explicit NetworkExample(QObject *parent = nullptr);
signals:
};
#endif // NETWORKEXAMPLE_H
NetworkExample.cpp
#include "NetworkExample.h"
NetworkExample::NetworkExample(QObject *parent)
: QObject{parent}
{
}
ファイルはまだ何もしません。 main.cpp
で 、インスタンスを作成して Qml エンジンに登録し、Qml にインポートできるようにします。
#include "NetworkExample.h"
[...] // below the QGuiApplication line
NetworkExample* networkExample = new NetworkExample();
qmlRegisterSingletonInstance<NetworkExample>("org.raymii.NetworkExample", 1, 0, "NetworkExample", networkExample);
ファイルの下部にある return app.exec()
を変更します その値を保存するだけでなく、終了する前にオブジェクトを破棄します:
auto result = app.exec();
networkExample->deleteLater();
return result;
これは単純な例ですが、この部分を明示的に追加することで、少し衛生的な方法を教えたいと思っています.
main.qml
で 、他の import
の下 行:
import org.raymii.NetworkExample 1.0
ネットワーク リクエスト
最後に、実際のリクエストを実行します。 <QNetworkAccessManager>
を追加します ヘッダーをインクルードに追加し、 QNetworkAccessManager* _manager = nullptr;
を追加します private:
で ヘッダーのセクション。コンストラクタ内 new
それ:
_manager = new QNetworkAccessManager(this);
親オブジェクトを提供しているので、new
結構です。一度親 QObject
が破壊された場合、これも破壊されます。
実際のリクエストを行うメソッドを追加します。ヘッダーで、宣言して Q_INVOKABLE
としてマークします だからQmlはそれを呼び出すことができます:
Q_INVOKABLE void doGetRequest(const QString& url);
関数定義:
void NetworkExample::doGetRequest(const QString& url)
{
setResponse("");
auto _request = QScopedPointer<QNetworkRequest>(new QNetworkRequest());
_request->setUrl(url);
_request->setTransferTimeout(5000);
_request->setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0");
QNetworkReply *reply = _manager->get(*_request);
QObject::connect(reply, &QNetworkReply::finished, this, &NetworkExample::slotFinished);
}
<QNetworkReply>
を含めることを忘れないでください ヘッダー。
最初の部分は Qt スタイルのスマート ポインターなので、QNetworkRequest
を削除する必要はありません。 私たち自身。範囲外になると、破棄されます。最初の行は、Q_PROPERTY
の以前の応答データを消去します。 、後で定義します。
次に、いくつかのパラメータを設定します。最も重要なものは URL です。おまけとして、ユーザー エージェント ヘッダーとリクエスト タイムアウトを 5 秒に設定しました。
QNetworkAccessManager
の使用 リクエストを送信し、finished
に接続します 応答する信号。このガイドをシンプルにするために、errorOccured
には接続していません。 または readyRead
信号ですが、おそらく信号 QNetworkReply
に関するドキュメントを読む必要があります
新しいスロットを追加します (通常の方法、public slots:
行の下) ) forour slotFinished
メソッド:
public slots:
void slotFinished();
内容:
void NetworkExample::slotFinished()
{
QNetworkReply *reply = dynamic_cast<QNetworkReply*>(sender());
if(reply != nullptr) {
setResponse(reply->readAll());
reply->deleteLater();
}
}
signal/slot
ごと 接続には、シグナル QObject::sender()
を送信したオブジェクトへのポインタを返すメソッドがあります . dynamic_cast
で使用しています nullptr ではなく、正しいタイプであることを確認します。 QNetworkReply::readAll()
の使用 、返信全体が利用可能です。 slotFinished
()
の場合 reply
は (シグナル/スロット経由ではなく) 直接呼び出されます。 オブジェクトは nullptr になります。 QObject::sender()
について留意すべき考慮事項がいくつかあります。 元のオブジェクトが破棄されて DirectConnection
になった場合のように 、しかし、この例ではこれで問題なく動作します。
ドキュメントでは、 deleteLater()
を呼び出すように明示的に言及しています ネットワーク上で返信するので、通常の削除の代わりにそれを行います。
メソッドの最後の部分は新しい Q_PROPERTY
です response
という名前 .行 Q_OBJECT
のすぐ下のヘッダーに追加します :
Q_PROPERTY(QString response READ response WRITE setResponse NOTIFY responseChanged)
Qt Creator の最近のバージョンでは、Q_PROPERTY
を右クリックして 一部を選択してRefactor
を選択 、 Generate Missing Q_PROPERTY Members
.それ以外の場合は、このプロパティについて特別なことは何もありません。お使いの Qt Creator バージョンに便利なオプションが表示されない場合は、手動でシグナル/スロットとメンバー変数を追加してください。
Qml で、このプロパティを TextField
にバインドします。 text
プロパティ:
TextField {
id: cppResult
text: NetworkExample.response
}
Button
を作る 定義したばかりの関数を呼び出します:
Button {
text: "C++ HTTP GET "
onClicked: NetworkExample.doGetRequest("http://httpbin.org/ip")
}
この URL は、送信元 IP を含む JSON 応答を返します。
大きな緑色の再生 (実行) ボタンを押してテストします:
簡単でしたよね? CURL*
をいじる必要はありません または curl_easy_setopt()
デフォルトでは非同期です。 QML / JavaScript の部分はさらに簡単で、タイプセーフでないチートのように簡単です。
QML HTTP GET リクエスト
QML 部分は、プロパティ バインディングを備えた単純な古い JavaScript です。 main.qml
で ファイルで、property var
を定義します Window{}
内に応答データを保持します 、 Column
のすぐ上 :
property var response: undefined
新しいプロパティのすぐ下に、リクエストを実行する関数を追加します:
function doGetRequest(url) {
var xmlhttp = new XMLHttpRequest()
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === XMLHttpRequest.DONE
&& xmlhttp.status == 200) {
response = xmlhttp.responseText
}
}
xmlhttp.open("GET", url, true)
xmlhttp.send()
}
このメソッドは、呼び出されると XMLHttpRequest
を実行します 、ステータス コードをチェックするコールバック関数を使用して、リクエストが成功した場合は response
を更新します 財産。応答プロパティを TextField
にバインドします :
TextField {
id: qmlResult
text: response
}
ボタンの onClicked
に新しい関数を追加します :
Button {
text: "Qml HTTP GET"
onClicked: {
response = ""
doGetRequest("http://httpbin.org/ip")
}
}
次に、大きな緑色の再生ボタンを押してテストします:
もちろん、JSON の場合は JSON.parse(xmlhttp.responseText)
を追加できます の場合、QML 内で JSON に直接アクセスできます (text: response.origin
)、またはエラー処理を追加します。
ご覧のとおり、これは単なる JavaScript であるため、すでに非常に単純な C++ の部分よりもさらに簡単です。
async
をテストしたい場合 -ness、具体的には、GUI スレッドをブロックしないようにするには、URL https://httpbin.org/delay/4
を使用します 、応答する前に 4 秒間待機します。ボタンをクリックして、何が起こっているかを確認できるはずです。
この目的のために、C++ と Qml のどちらが一番好きかについての考えを私に送ってください。