【Qt】ウィジェットのスクリーンショット
メインウィンドウをスクリーンショットする
メインウィンドウのスクリーンショットを撮る場合、 QPixmap::grabWidget を利用する。
// メインウィンドウのスクリーンショット pixmap = QPixmap::grabWidget( this ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
ただし、この場合ウィンドウフレームは含まれない。 これはウィンドウフレームがQtの処理範囲外のためである。
そのため、ウィンドウフレームを含める場合は、 より大きい範囲、スクリーンのスクリーンショットを撮ってトリミングをする。
QDesktopWidget dw; QWidget * screen=dw.screen( dw.screenNumber(this) ); QRect rect = geometry(); WId wid = screen->winId(); // タイトルバーの高さを取得 QStyleOptionTitleBar options; options.initFrom(this); int tbh = this->style()->pixelMetric( QStyle::PM_TitleBarHeight, &options, this ) - 4; // QPixmap::grabWindow(WId,int,int,int,int) の実行に必要なアトリビュートを設定 setAttribute( Qt::WA_TranslucentBackground ); // ウィンドウフレーム描画の数値を設定 // 以下の6変数はWin7の実測値のため、実行環境の影響を受ける const int fw = 8; ///< frame width const int fh = 5; ///< frame height const int cx = rect.x() - fw; ///< caption x const int cy = rect.y() - (tbh + fh - 3); ///< caption y const int cw = rect.width() + fw * 2; ///< caption width const int ch = rect.height() + (tbh + fh * 2); ///< caption height pixmap = QPixmap::grabWindow( wid, cx, cy, cw, ch ); // スクリーン外の非表示部分も描画するため、 // ウィジェット部分を再描画する。 QPixmap pix_wdt = QPixmap::grabWidget( this ); QPainter painter( &pixmap ); painter.drawPixmap( fw, (tbh + fh - 3), pix_wdt ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
コメントに記載したようにリテラル整数は実測値のため、 環境によっては意図する結果が得られない場合がある。
MDIのサブウィンドウをスクリーンショットする
MDIサブウィンドウの場合、ウィンドウフレームもQtの処理範囲に含まれる。 そのため、QMdiSubWindow を QPixmap::grebWidget することでフレーム付きスクショが撮れる。
// ウィンドウフレームを含むスクリーンショット QMdiSubWindow * activeWindow = mdiArea->activeSubWindow(); if ( activeWindow == NULL) { return false; } Q_ASSERT( activeWindow ); pixmap = QPixmap::grabWidget( activeWindow ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
ウィンドウフレームなしのスクリーンショットの場合、 QMdiSubWindow の widget を QPixmap::grebWidget することで得られる。
// ウィンドウフレームを含まないスクリーンショット QMdiSubWindow * activeWindow = mdiArea->activeSubWindow(); if ( activeWindow == NULL) { return false; } Q_ASSERT( activeWindow ); pixmap = QPixmap::grabWidget( activeWindow->widget() ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
参考資料
- c++ - Qt grabWindow coordinates shifted from GetCursorPos and GetWindowRect - Stack Overflow
- c++ - Qt grabWindow coordinates shifted from GetCursorPos and GetWindowRect - Stack Overflow
- How to get window handle (HWND) of core application window on Win32 for QSpaceNavigator | Qt Forum
- Converting from Qt's WId to Windows HWND?
- QWidget Class | Qt Widgets 5.5
【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 については公式配布物から最新版をインストールでよいです。 当該環境ではインストーラによる導入をしています。
- git : https://git-scm.com/
- node : https://nodejs.org/
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つです。
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はテストプロジェクト向けパッケージ
- QTest::addColumn
() メソッド - QFETCH マクロ
標準組み込み型および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 導入リンク集
ブックマークが溜まってきたので、内容をまとめる前に一度リンクを記事にしました。 まとまりはないので、たどり着いてしまった人は自分の目で確認してくだし。。
- http://doc.qt.io/qt-4.8/modelview.html
- http://www.thedazzlersinc.com/source/2012/06/04/qt-qtableview-example-short-and-quick/
- http://www.bogotobogo.com/Qt/Qt5_QTableView_QItemDelegate_ModelView_MVC.php
- http://stackoverflow.com/questions/21409457/a-checkbox-only-column-in-qtableview
- http://forum.qt.io/topic/25740/checkbox-in-qtableview
- http://stackoverflow.com/questions/578371/hiding-row-labels
- http://stackoverflow.com/questions/3861296/how-to-select-row-in-qtableview
- http://doc.qt.io/qt-4.8/qt.html#CheckState-enum
- http://doc.qt.io/qt-4.8/stylesheet-examples.html
- http://qt.developpez.com/doc/4.6/qcheckbox/
- http://forum.qt.io/topic/4716/qtableview-checkbox-delegate-alignmnent
- https://social.msdn.microsoft.com/Forums/silverlight/en-US/95657146-8e5b-42f2-a547-c61b6b17a148/how-to-put-text-content-on-left-side-of-checkbox
- http://doc.qt.io/qt-4.8/qt.html#LayoutDirection-enum
- http://forum.qt.io/topic/24011/qtreeview-hide-that-1-category
- http://www.qtcentre.org/threads/1060-qt4-xp-QTreeView-w-o-header
- http://www.java2s.com/Code/Cpp/Qt/QTreeViewdemoandQStandardItem.htm
- http://doc.qt.io/qt-4.8/modelview.html#3-1-treeview
- http://www.qtcentre.org/threads/38120-QTreeWidget-How-to-display-the-root-item
- http://doc.qt.io/qt-4.8/qtreeview.html#rootIsDecorated-prop
- http://forum.qt.io/topic/16605/solved-qt-how-to-get-parent-of-qtreewidgetitem
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" とすること。などが記載されてます)。
以上