カンテラの光の下で

dNaga392's memorandom

【Qt】Sleep()の実装例

QtによるSleepメソッド実装方法を記します(ほぼ参考資料の転載です)。

Sleep は指定時間の間、処理の実行を中断するメソッドです。 実装により次の2種類が存在しています。

  • 秒指定
  • ミリ秒指定

前者には C++標準ライブラリ のSleepメソッドが、 後者には Windowライブラリ のSleepメソッドがあります。

本記事ではこの2種類を対象に実装例を示します。

Sleep(秒指定)

#include <QtCore/QThread>

void Sleep( unsigned long secs )
{
    QThread::sleep( secs );
}

Sleep(ミリ秒指定)

#include <QtCore/QEventLoop>
#include <QtCore/QTimer>

void Sleep( int msec )
{
    QEventLoop loop;
    QTimer::singleshot( msec, &loop, SLOT( quit() ) );
    loop.exec();
}

参考資料

以上

Slack への Hubot 導入はじめ

導入でつまずいたので、記事で残します。

環境とインストールしておくツール

git は必須ではないですが、git bashから構築及び実行をしているため挙げました。 git と node.js については公式配布物から最新版をインストールでよいです。 当該環境ではインストーラによる導入をしています。

Hubot を召喚しよう

Hubot プロジェクトの作成には、公式ドキュメントが大きな力になります。

https://hubot.github.com/docs/

召喚例として導入時の内容を以下に記述します。 環境を整えたら、まずは git bash を起動して node および npm が使えることを確認します。

$ node -v
v5.2.0
$ npm -v
3.5.2

npm のバージョンが確認できたら、Hubotのジェネレータをインストールします。

$ npm install -g yo generator-hubot

ここまでできたら、いよいよ召喚の議を行います。 召喚先のプロジェクトフォルダを作成し、Hubotをインストールします。

$ mkdir myhubot
$ cd myhubot
$ yo hubot

これでhubotが降臨します。 降臨中に Owner, Bot name, Description, Bot adapter の4つを聞かれるので回答します。

前者3つは自由回答でかまいませんが、adapter は slack と回答しましょう。 そうすることで slack 向けの初期設定がされます。

降臨後はコマンドで ./bin/hubot を実行し、対話を試みます。 対話では Bot name で決めた名前でよびかけます。 以下は Bot name を myhubot とした場合の対話例です。

$ bin/hubot
myhubot ping
myhubot> PONG

myhubot が PONG を返したので対話成功です。 インストールできたと言えます。

各コマンド実行の際に yeoman が不足しているなどの警告があれば適時インストールしてください。 自分の場合、(タイミングは忘れましたが)yeoman-doctor が欲しいと言われたので npm でインストールしました。

$ npm -g install yeoman-doctor

Hubot を Slackに呼ぼう

Slack に呼ぶ方法は以下を参考にしました。

Slack の Costom IntegrationページでHubotを召還する準備をします。 準備は2つです。

  1. Hubotを降ろすBotアカウントを追加
  2. API Token を控える

Botアカウントを追加できたら、Slackでの対話をしましょう。 以下を実行して、Hubotを起こします。 xxxxxxxxxxxxxxx には、先ほどのAPI Token を置換して実行ください。

$ HUBOT_SLACK_TOKEN=xxxxxxxxxxxxxxx bin/hubot --adapter slack

無事実行できたら、Hubotを降ろしたアカウント宛に ping します。 PONG をリプライされれば対話成功です。

導入はじめの手続きは以上です。

今は script ディレクトリに 対話例のスクリプト example.coffee があるので、いろいろと試して遊んでこの後を考えてます。 また、実際に運用する場合はローカルではなく heroku などを使うことになるかと思います(が、まだ試していません)。

参考

【QtTest】 テストプロジェクトでテストデータにコンテナクラス(QList、QMap、...)を使う

Qtはテストプロジェクト向けパッケージでテストデータを受け渡す方法として、 次の方法が提供されている。

標準組み込み型およびQtライブラリ提供クラスの多くは、 addColumn メソッドで登録したテストデータを、QFETCH マクロで受け取ることができる。

// テストデータ登録メソッド
void TestMyClass::function_data()
{
QTest::addColumn< int >( "number" );
QTest::addColumn< QString >( "text" );

// newRow...
}

// テスト実行メソッド
void TestMyClass::function()
{
QFETCH(int, number);
QFETCH(QString, text);

// QCOMPARE...
}

同様の記述でコンテナクラスをテストデータとする場合、 コンパイルエラーを起こすため、テストデータとして利用できない。

// コンパイルエラーを起こす記述
// テストデータ登録メソッド
void TestMyClass::function_data()
{
QTest::addColumn< QMap< QString, QString > >( "map_data" );

// ...
}

// テスト実行メソッド
void TestMyClass::function()
{
QFETCH(QMap< QString, QString >, map_data);

// ...
}

そのため、コンテナクラスをテストデータとする場合、 次の2つを利用する。

  • typedef文
  • Q_DECLARE_METATYPE マクロ
// 非コンテナのシノニムを定義
typedef QMap< QString, QString > QStringMap;
// QMetaType に登録
Q_DECLEARE_METATYPE( QStringMap );
// テストデータ登録メソッド
void TestMyClass::function_data()
{
QTest::addColumn< QStringMap >( "map_data" );

// ...
}

// テスト実行メソッド
void TestMyClass::function()
{
QFETCH(QStringMap, map_data);

// ...
}

参考資料

【Qt】View-QStandardItemModel 導入リンク集

ブックマークが溜まってきたので、内容をまとめる前に一度リンクを記事にしました。 まとまりはないので、たどり着いてしまった人は自分の目で確認してくだし。。

QtDesigner の custom widget plugin を作る

参考

%%QTDIR%%/examples/designer/worldtimeclockplugin

構成

ファイル 例(worldtimeclockplugin)
ウィジェットクラスファイル worldtimeclock.h worldtimeclock.cpp
プラグインクラスファイル worldtimeclockplugin.h worldtimeclockplugin.cpp
プロジェクトファイル worldtimeclockplugin.pro

カスタムウィジェットプラグインを作る場合、この3種類のファイルが必要となる。

既にカスタムウィジェットを作成している場合、 プラグインクラスファイルとプロジェクトファイルを用意することで、 カスタムウィジェットプラグインのプロジェクトとなる。

追加

release ビルドを実行。
以下の2ファイルが生成される(***:project name)。

  • lib***.a
  • ***.dll

これを以下に配置すると追加プラグインとして利用できる。

%%QTDIR%%/plugins\designer

【QLibrary】DLL内の関数を呼び出す

今回は関数の定義されたDLLの作成と、DLLの関数の呼び出しについて。

QtライブラリのDLL読み込みクラスは、QLibraryとQPluginLoaderがありますが、
今回は QLibrary を使用して読み込みます。

やりたいことはだいたい以下の感じ。

// アプリケーションと同じフォルダに SharedLib.dll があると仮定する。
QLibrary myLib("SharedLib"); // 読み込み

// 関数シンボルの定義。定義のフォーマットは以下のとおり。
//   typedef 戻り値の型 (*定義型)( 入力値の型1, 入力値の型2, ... );
// 今回、読み込む関数は int addNumber( int, int ) なので次のようになる。
typedef int (*MyPrototype)(int,int);

// 関数シンボルのアドレスを取得する(失敗の場合は0)。
MyPrototype myFunction = (MyPrototype) myLib.resolve("addNumbers");

// 呼び出し関数を実行。
int result = myFunction(3,2);
qDebug() << result; // "5"

// アンロード。loadを呼び出してない場合、resolveでloadされているため。
myLib.unload();

実際には、これにアドレスの取得失敗した場合の処理が追加されることになります。

参考というかほぼ以下のページのコピペなんで、リンク先を見て実行すればおおよそ理解できると思います。

Call Symbol From Shared Object File (DLL) with QLibrary

ただし、当該記事は2つ注意することがあります(というか自分がハマったことです)。

まず、main.cpp の20行目で 結果値に 2 が加算されています。 そのため、デバック出力では 7 が表示されます。

次に、記事のソースでは main.cpp の最後のreturnが QCoreApplication クラスの実行になっているため、コンソールから実行した場合、アプリを終了する手立てがありません(厳密にはCtrl+Cなどいくつか方法がありますが)。
そこで、コンソールから実行する場合は、最後のreturn 文を return 0; としておくとよいです。

この他、覚えておきたいこととして1点。

ライブラリのソースを見ると、Q_DECL_EXPORT は Win 環境の場合、__declspec(dllexport) が定義されています。 そのため、 Sharedlib.cpp の __declspec(dllexport) は Q_DECL_EXPORT で置換しても問題なさそうです。

c++ - Delayed DLL load in QT - Stack Overflow

あとは量も多くないので、ドキュメントを一読することをオススメします(呼び出す関数は extern "C" とすること。などが記載されてます)。

以上

【QScrolArea】中身のサイズに応じてスクロールバーを表示する

QScrollArea はスクロールバーを表示するウィジェットです。

スクロールバーは内包するウィジェットのサイズを受けて設定されます。
他のツールでも同様ですが、意味のあるスクロールバーを表示するにはコツがいります。

もし QScrollArea を見て、「これで手軽にスクロールバー表示できるぜ!ヒャッホーイ!」
と特に設定せずウィジェットやアイテムのサイズを動的に変化した場合、
意図したスクロールバーの表示はされません。
(アイテムが潰れた表示になるかもしれません。)

この問題に対処するには、まず QScrollArea に対して以下の設定をします。

   widgetResizable:true

加えて、QScrollArea直下の内包ウィジェット(ContentWidget)のレイアウトに
以下の設定をします。

   layoutSizeConstraint:SetMinimumSize

1つ目の設定で、ウィジェットのサイズ変更をQScrollAreaに容認させ、
2つ目の設定で、ContentWidgetのサイズをそれの持つアイテムによって変化するものとします。

これにより、QScrollAreaは中身のサイズの変化したことを受け取り、
スクロールバーが表示されるようになります。

参考