前回の日記に続き、Qt を触っていました。 Qt 5.4 で C++ のコーディングをしていたので忘れないうちにノートしておきます。 今回やったことは:
- C++14 に対応する
- Qt でプラットフォーム特有のコードを書く
- Qt のバージョンを識別する
- QMake でインクルードパスの指定
- QMake に依存するライブラリとして libcurl (cURL) を追加する
- Qt Network を使う
- QTimer::timeout のシグナルを一回だけ呼ばれるようにする
- QString から UTF-8 の std::string に変換する
- Qt で JSON をパーシングする
- Qt のアサーション
- C++ から QML のオブジェクトを取得する
C++14 に対応する
Qt 5.4 または Qt Creator で C++14 に対応するには次のように QMake ファイル (.pro
) に追加します。
CONFIG += c++14
同様に C++11 の場合は:
CONFIG += c++11
ただし、OS X の場合、これだけでは C++14 を使うことができません。 以下の行を QMake ファイルに追加する必要があります。
QMAKE_MACOSX_DEPLOYMENT_TARGET=10.9
QMAKE_MACOSX_DEPLOYMENT_TARGET
は アプリケーションの動作環境を OS X 10.9 以上に指定するビルド設定です。これは Xcode で言えば MACOSX_DEPLOYMENT_TARGET
に相当します。
Note:
Qt に限らず、例えば libc++ を使う場合は OS X 10.7 以上でないと Xcode でビルドできません。今回は C++14 を使うため 10.9 を指定しています。
Qt でプラットフォーム特有のコードを書く
Qt にはプラットフォーム特有の機能にアクセスする手段が用意されています。 プラットフォームごとのコードは次のように書くことができます:
#ifdef Q_WS_WIN
// Windows
#endif
#ifdef Q_WS_MAC
// Mac OS X
#endif
#ifdef Q_WS_X11
// X11
#endif
#ifdef Q_WS_QWS
// QWS
#endif
WS は使用しているウィンドウシステム (Window System) を示しています。
Q_WS_QWS
というのは、組み込み Linux 向けの Qt Window System (QWS) のときに有効になるプリプロセッサです。
ウィンドウシステムではなく OS を識別するには同様に:
#ifdef Q_OS_WIN32
// Windows
#endif
#ifdef Q_OS_MAC
// Mac OS (= Darwin)
#endif
#ifdef Q_OS_LINUX
// Linux
#endif
他にも Q_OS_UNIX
や Q_OS_OPENBSD
, Q_OS_CYGWIN
などが識別可能です。OS に関しては環境によって複数定義されるようです。例えば次のコードは Mac OS X で有効です。
#if defined(Q_OS_UNIX) && defined(Q_OS_MAC) && defined(Q_OS_DARWIN)
qDebug() << "This platform is unix";
#endif
Qt のバージョンを識別する
QT_VERSION
を使って、使用している Qt のバージョンを識別することができます。次の例では Qt のバージョンが 5.4.1 以上かどうかを判断しています。
#if QT_VERSION >= 0x050401
qDebug() << QString::number(QT_VERSION, 16);
#endif
QMake でインクルードパスの指定
Qt が提供するモジュール以外のライブラリを使うことがあります。インクルードパスを通すには QMake ファイルに次のように指定します。
INCLUDEPATH += path/to/rapidjson/include
複数のパスを指定する場合は:
INCLUDEPATH += path/to/rapidjson/include path/to/entityx/include
また空白 (white space) を含む行を指定する場合は次のように指定します:
INCLUDEPATH += "path/to/my headers"
プラットフォームごとにパスを指定することもできます。次の例は、win32 と unix 環境でパスを指定しています。
win32:INCLUDEPATH += C:/path/to/include
unix:INCLUDEPATH += usr/include
QMake に依存するライブラリとして libcurl (cURL) を追加する
QMake ファイル (.pro
) の LIBS
に -lcurl
を追加します。
LIBS += -lcurl
ライブラリパスが通ってない環境も考えられます。例えば Win32 環境などに対応するには次のように記述します。
win32 {
lessThan(QT_MAJOR_VERSION, 5) {
INCLUDEPATH += /path/to/curl
}
} else {
LIBS += -lcurl
}
これで Qt Creator 上で libcurl が使えようになります。試しに次のコードで動作確認できます。
extern "C" {
#include <curl/curl.h>
}
int main(int argc, char *argv[])
{
CURL* curl = curl_easy_init();
curl_easy_cleanup(curl);
return 0;
}
別途 libcurl を追加せずとも、実のところ Qt にはネットワークライブラリとして Qt Network が用意されています。
Qt Network を使う
Qt 5 にはネットワークプログラミングを簡単に扱うことができる Qt Network モジュール が用意されています。
HTTP, TCP, UDP, SSL とよく使われるプロトコルはサポートしているようです。 何が嬉しいかというと、Qt 5 の開発環境さえ入れていればこの Qt Network の機能がどこでも使えることです。 ビルドするときに、あれがない・これがないと悩む必要はありません。Qt SDK へのファイルパスがわかれば、依存関係は QMake が解決してくれます。ポータビリティに優れています。
Qt Network モジュールを使うには QMake に network
を追加します。
QT += network
QTimer::timeout のシグナルを一回だけ呼ばれるようにする
QTimer
クラスを使うと簡単にネットワークのタイムアウト処理などを実装することができます。QTimer::timeout
のシグナルでイベントを受け取ることができます。デフォルトでは QTimer::setInterval
で指定したインターバル毎に何度もこのシグナルは呼び出されます。一度だけ呼び出されるようにするには QTimer::setSingleShot(true)
を指定します。例えば次のようになります。
QTimer* timer = new QTimer(this);
timer->setInterval(std::chrono::milliseconds(100).count());
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]{
qDebug() << "Timeout";
});
timer->start();
QString から UTF-8 の std::string に変換する
QString
を UTF-8 の std::string に変換します。
QString source;
QByteArray byteArray = source.toUtf8();
std::string text = std::string(byteArray.data(), byteArray.size());
Qt で JSON をパーシングする
QJsonDocument を使うと JSON をパーシングしたり、JSON オブジェクトに変換したりすることができます。
QString jsonString;
QJsonParseError parseError;
auto jsonDocument = QJsonDocument::fromJson(jsonString, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Error: invalid Json format";
return;
}
Qt のアサーション
Qt にはデバッグ時の簡易テストとして Q_ASSERT
が用意されています。
Q_ASSERT(std::string("42") == std::to_string(42));
リリースビルドなどにアサートを含めない場合は、QT_NO_DEBUG
を定義します。
C++ から QML のオブジェクトを取得する
C++ から取得したい Qt Quick オブジェクトに QML 上で objectName
プロパティを追加します。
ApplicationWindow {
objectName: "MyWindow"
title: qsTr("himarinkolshizukuesu")
}
C++ から QML オブジェクトを見つけるには QQmlApplicationEngine::rootObjects()
と Object::objectName()
を使います。
次の例では、main.qml
から MyWindow
オブジェクトを取得して、title
プロパティの値を変更しています。
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
for (auto rootObject: engine.rootObjects()) {
if (rootObject->objectName() == "MyWindow") {
rootObject->setProperty("title", "beam");
break;
}
}
オブジェクトが見つからない場合、または子オブジェクトを取ってくる場合は QObject::findChild<T>
で子オブジェクトを探します。
QObject* rootObject = engine.rootObjects().front();
QObject* qmlObject = rootObject->findChild<QObject*>("MyWindow");
例えば次のような関数を定義しておくと、QML のルートのほうにあるオブジェクトの中から任意の名前のオブジェクトを見つけることができます:
auto findFromQml = [](QQmlApplicationEngine & engine, QString const& objectName)-> QObject* {
for (auto rootObject: engine.rootObjects()) {
if (rootObject->objectName() == objectName) {
return rootObject;
}
QObject* qmlObject = rootObject->findChild<QObject*>(objectName);
if (qmlObject != nullptr) {
return qmlObject;
}
}
return nullptr;
};
QObject* mainWindow = findFromQml(engine, "MyWindow");
mainWindow->setProperty("title", "mybeam");
ListView の Model を C++ で記述する
Qt Quick ではビュー (QML) で表示したいデータを モデル (model
) として指定することができます。
QML 上で次のようにモデルを定義します。
ListModel {
id: myModel
ListElement {
name: "himarinko"
color: "red"
}
ListElement {
name: "l"
color: "green"
}
ListElement {
name: "shizukuesu"
color: "blue"
}
}
上のデータを ListView で表示するには次のように QML 上で model
プロパティを指定します。
ListView {
id: myListView
model: myModel
delegate: Item {
Row {
id: row1
spacing: 10
Rectangle {
width: 40
height: 40
color: model.modelData.color
}
Text {
text: model.modelData.name
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
このモデルは、C++ から指定することもできます。
QQmlApplicationEngine engine;
QList<QObject*> dataList;
dataList.append(new MyDataObject("himarinko", "red"));
dataList.append(new MyDataObject("l", "green"));
dataList.append(new MyDataObject("shizukuesu", "blue"));
auto context = engine.rootContext();
context->setContextProperty("myModel", QVariant::fromValue(dataList));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QML での ListElement
にあたるのは、この例では MyDataObject
になります。
定義例を次に示します。
class MyDataObject : public QObject {
Q_OBJECT
public:
explicit MyDataObject(QObject *parent = 0);
~MyDataObject();
MyDataObject(QString const& nameIn, QString const& colorIn)
: name_(nameIn), color_(colorIn)
{}
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
public:
QString const& name() const {
return name_;
}
void setName(QString const& nameIn) {
name_ = nameIn;
}
QString const& color() const {
return color_;
}
void setColor(QString const& colorIn) {
color_ = colorIn;
}
private:
QString name_;
QString color_;
signals:
void nameChanged();
void colorChanged();
public slots:
};
Leave a Reply