はじめに
今までの投稿はOpenCVばかりでしたので、JoypadをQtで取得しようと思います。
次に投稿を予定しているOpenCVのEdgeの処理でもJoypadを使用したいと思います。
仕様
1.SubThreadでJoypadの処理を行う
2.MainThreadでJoypadの表示を行う
3.JoypadのAnalog Axisを使用する
使用Joypad
ELECOMさんのJoypadを使用しました。
Axis:3(Analog/Digital切り替えが内2つ)
Button:12
参考先
Joypadの実装に関しては
こちら
を参考にさせていただきました。
JoypadのCalibration
初めてJoypadを使用するときは、Calibrationを実施したほうが良いと思います。
「jstest-gtk」でCalibrationします。(他にCalibrationするやり方があるのでしたら、ご教示していただけると嬉しいです)
インストール
ターミナルから下記を実行します。
sudo apt install -y jstest-gtk
Calibration
「アプリケーションを表示する」から「jstest-gtk」をクリックし、「プロパティ」をクリックします。
ここでAxis(表示はAxes)を見ると、「6」となっております。
左右で1、上下で1 = 2で、3Axis(軸)あるので、6と表示されるようです。
「Calibration」をクリックします。
「StartCaribration」をクリックします。
「OK」をクリックします。
「OK」クリック後、下記画面になりますので「StartCaribration」をクリックしてからJoyPadのすべての軸をすべての方向に傾けたり、すべてのボタンを押したりしてください。
すべての軸を傾けたり、すべてのボタンを押したら、「閉じる」をクリックします。
すべての軸を傾けたり、すべてのボタンを押して問題がないか確認してください。
終了するときは、「閉じる」をクリックしてください。
プロジェクトの新規作成
新規に「joypad」というプロジェクトを作成します。
新規にプロジェクトを作成する方法は、
QtでHello world!
を参照してください。
UI
Joypad名称用Labelを1(geometryの幅を200、背景色はピンクに設定してくさい)
表題用のlabelを18個(Axis:6、Button:12)
表示用のlabelを18個(Axis:6、Button:12)
配置します。
labelの配置方法は、
QtでHello world!
を参照してください。
表題用のLabelの名称を下記の通り設定します。
Axis1(L/R)、Axis1(U/D)、Axis2(L/R)、Axis2(U/D)、Axis3(L/R)、Axis3(U/D)
Button01、Button02、Button03、Button04、Button05、Button06
Button07、Button08、Button09、Button10、Button11、Button12
表示用のlabelのobjectName、text、背景色を下記の通り設定します。
表題用Label | objectName | text | 背景色 |
---|---|---|---|
Axis1(L/R) | label_Axis1_LR | なし | 黄色 |
Axis1(U/D) | label_Axis1_UD | なし | 黄色 |
Axis2(L/R) | label_Axis2_LR | なし | 黄色 |
Axis2(U/D) | label_Axis2_UD | なし | 黄色 |
Axis3(L/R) | label_Axis3_LR | なし | 黄色 |
Axis3(U/D) | label_Axis3_UD | なし | 黄色 |
Button01 | label_Button01 | なし | 水色 |
Button02 | label_Button02 | なし | 水色 |
Button03 | label_Button03 | なし | 水色 |
Button04 | label_Button04 | なし | 水色 |
Button05 | label_Button05 | なし | 水色 |
Button06 | label_Button06 | なし | 水色 |
Button07 | label_Button07 | なし | 水色 |
Button08 | label_Button08 | なし | 水色 |
Button09 | label_Button09 | なし | 水色 |
Button10 | label_Button10 | なし | 水色 |
Button11 | label_Button11 | なし | 水色 |
Button12 | label_Button12 | なし | 水色 |
実装
クラスの追加
実装はJoypadを別スレッドで取得するようにします。
Joypad用のクラスは「getJoypad」とします。
クラスの作成方法は、
QtでOpenCV(Threadによる取得)
を参照してください。
実装詳細
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "getJoypad.h" // 追加
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
// 追加(ここから)
public:
getJoypad *getJoyThread;
private:
int axisNum, buttonNum;
QString joypadName;
std::vector<int> joyAxisStatus;
std::vector<char> joyButtonStatus;
void reSizeAxisButtonMain(int, int);
public slots:
void onValueChangedJoy(void);
};
// 追加(ここまで)
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox> // 追加
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 追加(ここから)
getJoyThread = new getJoypad(this);
if(getJoyThread->initJoypad()){
getJoyThread->getJoypadData(&axisNum, &buttonNum, &joypadName);
getJoyThread->reSizeAxisButton(axisNum, buttonNum);
reSizeAxisButtonMain(axisNum, buttonNum);
ui->label_joypadName->setText(joypadName);
connect(getJoyThread, SIGNAL(valueChangedJoy(void)), this, SLOT(onValueChangedJoy(void)));
getJoyThread->start();
} else {
QMessageBox msgBox(this);
msgBox.setText(tr("Joypadがオープンできませんでした。"));
msgBox.setWindowTitle(tr("エラー"));
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
exit(0);
}
// 追加(ここまで)
}
MainWindow::~MainWindow()
{
getJoyThread->Stop = true; // 追加
delete ui;
}
// 追加(ここから)
void MainWindow::reSizeAxisButtonMain(int axisNum, int buttonNum)
{
joyAxisStatus.resize(axisNum, 0);
joyButtonStatus.resize(buttonNum, 0);
}
void MainWindow::onValueChangedJoy(void)
{
getJoyThread->getJoypadStatus(joyAxisStatus, joyButtonStatus);
ui->label_Axis1_LR->setNum(joyAxisStatus[0]);
ui->label_Axis1_UD->setNum(joyAxisStatus[1]);
ui->label_Axis2_LR->setNum(joyAxisStatus[2]);
ui->label_Axis2_UD->setNum(joyAxisStatus[3]);
ui->label_Axis3_LR->setNum(joyAxisStatus[4]);
ui->label_Axis3_UD->setNum(joyAxisStatus[5]);
ui->label_Button01->setNum(joyButtonStatus[0]);
ui->label_Button02->setNum(joyButtonStatus[1]);
ui->label_Button03->setNum(joyButtonStatus[2]);
ui->label_Button04->setNum(joyButtonStatus[3]);
ui->label_Button05->setNum(joyButtonStatus[4]);
ui->label_Button06->setNum(joyButtonStatus[5]);
ui->label_Button07->setNum(joyButtonStatus[6]);
ui->label_Button08->setNum(joyButtonStatus[7]);
ui->label_Button09->setNum(joyButtonStatus[8]);
ui->label_Button10->setNum(joyButtonStatus[9]);
ui->label_Button11->setNum(joyButtonStatus[10]);
ui->label_Button12->setNum(joyButtonStatus[11]);
}
// 追加(ここまで)
#ifndef GETJOYPAD_H
#define GETJOYPAD_H
#include <QThread>
#include <QObject>
#include <vector> // 追加
#include <linux/joystick.h> // 追加
#define JOYPAD_NAME_SIZE 80 // 追加
class getJoypad : public QThread
{
Q_OBJECT
public:
// getJoypad(); // 削除
getJoypad(QObject *parent = 0, bool b = false); // 修正
// 追加(ここから)
~getJoypad();
bool initJoypad(void);
void getJoypadData(int*, int*, QString*);
void reSizeAxisButton(int, int);
void run(void);
void getJoypadStatus(std::vector<int>&, std::vector<char>&);
bool Stop;
private:
int joyFd, numOfAxis, numOfButtons;
std::vector<int> joyAxis;
std::vector<char> joyButton;
char nameOfJoypad[JOYPAD_NAME_SIZE];
js_event js;
signals:
void valueChangedJoy(void);
// 追加(ここまで)
};
#endif // GETJOYPAD_H
#include <QMutex> // 追加
#include <QTextCodec> // 追加
#include <sys/ioctl.h> // 追加
#include <unistd.h> // 追加
#include <fcntl.h> // 追加
#include "getJoypad.h"
#define JOY_DEV "/dev/input/js0" // 追加
//getJoypad::getJoypad() // 削除
getJoypad::getJoypad(QObject *parent, bool b) : QThread(parent), Stop(b) // 修正
{
}
// 追加(ここから)
getJoypad::~getJoypad()
{
Stop = true;
close(joyFd);
}
bool getJoypad::initJoypad(void)
{
bool ret = true;
numOfAxis = numOfButtons = 0;
memset(nameOfJoypad, 0x00, sizeof(nameOfJoypad));
if((joyFd = open(JOY_DEV, O_RDONLY)) < 0){
ret = false;
} else {
ioctl(joyFd, JSIOCGAXES, &numOfAxis);
ioctl(joyFd, JSIOCGBUTTONS, &numOfButtons);
ioctl(joyFd, JSIOCGNAME(JOYPAD_NAME_SIZE), nameOfJoypad);
fcntl(joyFd, F_SETFL, O_NONBLOCK);
}
return ret;
}
void getJoypad::getJoypadData(int *axisNum, int *buttonNum, QString *joypadName)
{
*axisNum = numOfAxis;
*buttonNum = numOfButtons;
QTextCodec *tc = QTextCodec::codecForLocale();
*joypadName = QString(tc->toUnicode(nameOfJoypad));
}
void getJoypad::reSizeAxisButton(int axisNum, int buttonNum)
{
joyAxis.resize(axisNum, 0);
joyButton.resize(buttonNum, 0);
}
void getJoypad::run(void)
{
QMutex mutex;
while(1){
mutex.lock();
if(this->Stop){
break;
}
mutex.unlock();
read(joyFd, &js, sizeof(js_event));
switch(js.type & ~JS_EVENT_INIT){
case JS_EVENT_AXIS:
if((int)js.number>=(int)joyAxis.size()){
continue;
}
joyAxis[(int)js.number] = js.value;
break;
case JS_EVENT_BUTTON:
if((int)js.number>=(int)joyButton.size()){
continue;
}
joyButton[(int)js.number] = js.value;
break;
}
emit valueChangedJoy();
this->msleep(10);
}
}
void getJoypad::getJoypadStatus(std::vector<int>& axisStatus, std::vector<char>& buttonStatus)
{
for(int loopCount = 0; loopCount < (int)joyAxis.size(); loopCount++){
axisStatus[loopCount] = joyAxis[loopCount];
}
for(int loopCount = 0; loopCount < (int)joyButton.size(); loopCount++){
buttonStatus[loopCount] = joyButton[loopCount];
}
}
build&run
実装が完了しましたら、▶をクリックして、build & run します。
Calibrationが済んでいるのでしたら、下記の表示になると思います。
Axis1を動かしてみる
Axis2を動かしてみる
Buttonを押してみる
Button04を押した状態です。
皆さんも上手く動いたでしょうか?
まとめ
今回のJoypadもSubThreadで取得し、MainThreadでGUIの処理を行いました。
OpenCVのThreadの投稿では、USBカメラの取得をSubThreadで取得し、MainThreadでGUIの処理を行いました。
考えた方(実装の仕方)は同じにしております。
JoypadのCalibrationも一緒に説明しましたので、記事が長くなりました。申し訳ございません。