Qtで、下図のようにロボットを制御するためのジョイスティックのX, Y軸の位置と、ボタンの押下状態を表示するGUIアプリケーションを作成してみた。
Qtには、ジョイスティックを扱うモジュールが無いため、今回はWindowsマルチメディアのAPI「mmsystem.h」を用いる。
手順を一応メモっておく。
1)まず、Qt_creatorで,「JoystickQT」という名前のDialogベースのプロジェクトを作成する。そして、Group Box、LabelおよびText Browserウィジェットを追加する。ここで、X、Y軸の位置とボタンの押下状態を表示するText Browserのウィジェットには、それぞれ「joyXtext」、「joyYtext」および「joyBTtext」というオブジェクト名を付与する。
2)次に、ジョイスティックから位置を取得して、UIに送信するThreadクラスを定義する
具体的には、プロジェクトウィンドウにて、右クリックで「新しいファイルを追加」を選択し、「C++ Class」ファイルを選択する。そして、クラス名を「JoyThread」に設定し、以下のヘッダー「joythread.h」とメイン「joythread.cpp」を追加する。
【joythread.h】
#ifndef JOYSTICKTHREAD_H #define JOYSTICKTHREAD_H #include <QThread> //for joystick #include <windows.h> #include <mmsystem.h> //inheritance of QThread to get joystick position class JoyThread : public QThread { Q_OBJECT public: JoyThread(); //constructor ~JoyThread(); //destructor void stop(); //to stop thread signals: //signal for sending joystick position void positionSignal(const QString &x, const QString &y, const QString &bt); protected: void run(); //running process private: volatile bool runFlag; //flag for thread running (volatile: surpress optimization of compile) JOYINFOEX joyInfo; //joystick interface int joyId; //joystick id }; #endif // JOYSTICKTHREAD_H
【joythread.cpp】
#include "joythread.h" // constructor for setting joystick id (JOYSTICKID1) and initializing JOYINFOEX JoyThread::JoyThread() { runFlag = true; // running flag ON // set joystick id to JOYSTICKID1 this->joyId = JOYSTICKID1; // initialize JOYINFOEX joyInfo.dwSize = sizeof JOYINFOEX; joyInfo.dwFlags = JOY_RETURNALL; } // method for getting joystick position and sending to UI void JoyThread::run() { while(runFlag) // loop if runFlag on { // get joystick position if (joyGetPosEx(joyId, &joyInfo) == JOYERR_NOERROR){ //convert to QString QString x = ""; QString y = ""; QString bt = ""; x = x.number(joyInfo.dwXpos); y = y.number(joyInfo.dwYpos); bt = bt.number(joyInfo.dwButtons); // emit position signals emit positionSignal(x,y,bt); // sleep 100ms Sleep(100); } } } // method for stopping thred void JoyThread::stop() { runFlag = false; // running flag OFF } // destructor JoyThread::~JoyThread() { runFlag = false; wait(); }
ここで、クラスJoyThreadは、QThreadを継承している。概要は以下のようになる。
- コンストラクタJoyThread()では、runFlagをtrueにし、ジョイスティックのIDを設定、JOYINFOEX変数を初期化。
- run()では、100msごとにjoyGetPosExでジョイスティックのx,y位置とボタンの押下状態を取得。そして、シグナルpositionSignalで、x,y,btを送信。
- stop()と、デストラクタで、runFlagをfalseに設定。
シグナルとスロットに関しては、以下のリンクに詳しく説明されている。
http://densan-labs.net/tech/qt/chapter3.html
http://blog.qt.io/jp/2010/06/17/signals-and-slots-2/
3)プロジェクトファイル「JoystickQT.pro」に、以下のようにライブラリ「winmm.lib」のパスを追加
win32:LIBS += "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/x64/winmm.lib"
4)メインウィンドウクラスのDialogに、JoyThreadのシグナルpositionSignalからx,y,btを受け取り、Text Browserに表示するスロットprintPositionSlotを追加
具体的には、以下のようにヘッダー「Dialog.h」とメイン「Dialog.cpp」を修正する。
【Dialog.h】
#ifndef DIALOG_H #define DIALOG_H #include <QDialog> #include "joythread.h" namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); private slots: //slots for printing joystick position void printPositionSlot(const QString &x,const QString &y,const QString &bt); private: Ui::Dialog *ui; JoyThread myJoyThread; //joystick thread with id 0 }; #endif // DIALOG_H
【Dialog.cpp】
#include "dialog.h" #include "ui_dialog.h" Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); //start joystick thread myJoyThread.start(); //connect JoyThread::positionSignal to Dialog::printJoyPosition connect(&myJoyThread, &JoyThread::positionSignal, this, &Dialog::printPositionSlot); } void Dialog::printPositionSlot(const QString &x, const QString &y, const QString &bt) { ui->joyXtext->setText(x); ui->joyYtext->setText(y); ui->joyBTtext->setText(bt); } Dialog::~Dialog() { delete ui; }
ここで、クラスDialogは、QDialogを継承している。概要は以下のようになる。
- dialog.hにて、JoyThreadオブジェクトのmyJoyThreadを宣言
- コンストラクタにて、JoyThreadを開始し、JoyThreadのシグナルpositionSignalとスロットprintPositionSlotを接続。
- スロットprintPositionSlotにて、x,y,btそれぞれをText BrowserのjoyXtext、joyYtext、joyBTtextに出力。
― シグナルpositionSignalとスロットprintPositionSlotとのQObject::connectにより結びつけられている。
connectの各引数は、以下の通り。
第1引数:シグナルを発行するオブジェクトアドレス(const QObject *sender)
第2引数:シグナル名(const char *signal)
第3引数:スロットで受信するオブジェクトアドレス(const QObject *receiver)
第4引数:スロット名(const char *method)
5)プロジェクトの「ビルド」と「実行」を実行する。
*proファイルの修正が反映されずリンクのエラーがでる場合がある。その場合は、手動でgmakeを実行すると解決する。
本プロジェクト一式は、下記からダウンロードすることができる。
https://drive.google.com/open?id=0B3uB4w2FEJbIMzBHMVJHUXBqV1k
Qtには、ジョイスティックを扱うモジュールが無いので、Windows独自のAPIを用いて対応した。そのため、残念ながら本コードはクロスプラットフォーム対応になっていない(Windowsでしか動かない)。
スマートフォンではジョイスティックを扱うことはまずないので、ジョイスティック需要はあまりないので、今後もQtは対応しなさそうだ。逆に言えば、ジョイスティック操作はWindows、MacおよびLinuxの3つに対応すればいいだけなので、Macroで3つの環境を切り替えるプログラムを自力で書いてもそれほど大変ではないのだろう。