LoginSignup
0
0

More than 1 year has passed since last update.

QtでJoypad取得

Last updated at Posted at 2022-12-20

はじめに

今までの投稿は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するやり方があるのでしたら、ご教示していただけると嬉しいです)

インストール

ターミナルから下記を実行します。

install command.
sudo apt install -y jstest-gtk

Calibration

「アプリケーションを表示する」から「jstest-gtk」をクリックし、「プロパティ」をクリックします。
ここでAxis(表示はAxes)を見ると、「6」となっております。
左右で1、上下で1 = 2で、3Axis(軸)あるので、6と表示されるようです。
Screenshot from 2022-12-19 10-34-46.png
「Calibration」をクリックします。
Screenshot from 2022-12-19 10-39-55.png
「StartCaribration」をクリックします。
Screenshot from 2022-12-19 10-41-29.png
「OK」をクリックします。
Screenshot from 2022-12-19 10-42-29.png
「OK」クリック後、下記画面になりますので「StartCaribration」をクリックしてからJoyPadのすべての軸をすべての方向に傾けたり、すべてのボタンを押したりしてください。
すべての軸を傾けたり、すべてのボタンを押したら、「閉じる」をクリックします。
Screenshot from 2022-12-19 10-44-49.png
すべての軸を傾けたり、すべてのボタンを押して問題がないか確認してください。
終了するときは、「閉じる」をクリックしてください。
Screenshot from 2022-12-19 10-48-33.png

プロジェクトの新規作成

新規に「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 なし 水色

上記を設定すると、下記のようになると思います。
Screenshot from 2022-12-20 10-31-06.png

実装

クラスの追加

実装はJoypadを別スレッドで取得するようにします。
Joypad用のクラスは「getJoypad」とします。
クラスの作成方法は、
QtでOpenCV(Threadによる取得)
を参照してください。

実装詳細

mainwindow.h
#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
mainwindow.cpp
#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]);
}
// 追加(ここまで)
getJoypad.h
#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
getJoypad.cpp
#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が済んでいるのでしたら、下記の表示になると思います。
Screenshot from 2022-12-20 10-40-26.png

Axis1を動かしてみる

Axis1を左に倒した状態です。
Screenshot from 2022-12-20 10-43-57.png

Axis2を動かしてみる

Axis2を下に倒した状態です。
Screenshot from 2022-12-20 10-45-34.png

Buttonを押してみる

Button04を押した状態です。
Screenshot from 2022-12-20 10-46-35.png
皆さんも上手く動いたでしょうか?

まとめ

今回のJoypadもSubThreadで取得し、MainThreadでGUIの処理を行いました。
OpenCVのThreadの投稿では、USBカメラの取得をSubThreadで取得し、MainThreadでGUIの処理を行いました。
考えた方(実装の仕方)は同じにしております。
JoypadのCalibrationも一緒に説明しましたので、記事が長くなりました。申し訳ございません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0