Merge pull request '添加图马斯特摇杆控制功能框架,按钮未关联吊舱控制键' (#1) from chen into master

Reviewed-on: #1
master
Administrator 4 weeks ago
commit ce83ebfc31

@ -13,9 +13,14 @@ qt_add_executable(VideoClient
videoControl.h videoControl.ui videoControl.cpp
wprogressbar.h wprogressbar.cpp
webmapwidget.h webmapwidget.cpp
joystickcontroller.h joystickcontroller.cpp
directinputjoystick.h directinputjoystick.cpp
)
qt6_add_resources(VideoClient "resources"
PREFIX "/"
FILES "resources/res.qrc"
@ -39,7 +44,11 @@ target_link_libraries(VideoClient
Qt6::Network
Qt6::SerialPort
Qt6::WebEngineWidgets
#Qt6::Gamepad
video
winmm
dinput8
dxguid
# QWindowKit::Widgets
# WidgetFrame
)

@ -145,3 +145,69 @@ void CommandWidget::on_pushButton_16_clicked() {
void CommandWidget::on_pushButton_15_clicked() {
buttonResponse(0xC9);
}
void CommandWidget::onButtonChanged(int button, bool pressed)
{
qDebug() << "Button" << button << (pressed ? "Pressed" : "Released");
// switch (button)
// {
// case 0:
// on_pushButton_clicked();
// break;
// case 1:
// on_pushButton_2_clicked();
// break;
// case 2:
// on_pushButton_3_clicked();
// break;
// case 3:
// on_pushButton_clicked();
// break;
// case 4:
// on_pushButton_2_clicked();
// break;
// case 5:
// on_pushButton_3_clicked();
// break;
// case 6:
// on_pushButton_clicked();
// break;
// case 7:
// on_pushButton_2_clicked();
// break;
// case 8:
// on_pushButton_3_clicked();
// break;
// case 9:
// on_pushButton_clicked();
// break;
// case 10:
// on_pushButton_2_clicked();
// break;
// case 11:
// on_pushButton_3_clicked();
// break;
// case 12:
// on_pushButton_clicked();
// break;
// case 13:
// on_pushButton_2_clicked();
// break;
// case 14:
// on_pushButton_3_clicked();
// break;
// }
}
void CommandWidget::onDirectionChanged(int direction){
direction!=42949672?qDebug() << "direction" << direction:qDebug() << "center";
}

@ -122,6 +122,12 @@ private slots:
void on_pushButton_15_clicked();
public slots:
void onButtonChanged(int button, bool pressed);
void onDirectionChanged(int dirction);
private:
Ui::CommandWidget *ui;
QUdpSocket *m_commandUdpSocket = nullptr;

@ -0,0 +1,164 @@
#include "DirectInputJoystick.h"
#include <QDebug>
DirectInputJoystick::DirectInputJoystick(QObject *parent) : QObject(parent) {
// 确保 COM 库在主线程初始化
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
}
DirectInputJoystick::~DirectInputJoystick() {
cleanup();
CoUninitialize();
}
bool DirectInputJoystick::initialize() {
HRESULT hr = DirectInput8Create(
GetModuleHandle(nullptr),
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(VOID**)&m_pDirectInput,
nullptr
);
if (FAILED(hr)) {
emit errorOccurred("无法创建 DirectInput 对象");
return false;
}
// 枚举所有摇杆设备
hr = m_pDirectInput->EnumDevices(
DI8DEVCLASS_GAMECTRL,
enumJoysticksCallback,
this,
DIEDFL_ATTACHEDONLY
);
return SUCCEEDED(hr);
}
BOOL CALLBACK DirectInputJoystick::enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context) {
DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
return joystick->setupDevice();
}
bool DirectInputJoystick::setupDevice() {
if (!m_pDirectInput) return false;
// 创建设备
HRESULT hr = m_pDirectInput->CreateDevice(
GUID_Joystick,
&m_pJoystick,
nullptr
);
if (FAILED(hr)) {
emit errorOccurred("无法创建摇杆设备");
return false;
}
// 设置数据格式
hr = m_pJoystick->SetDataFormat(&c_dfDIJoystick);
if (FAILED(hr)) {
emit errorOccurred("设置数据格式失败");
return false;
}
// 设置协作级别
hr = m_pJoystick->SetCooperativeLevel(
nullptr, // 无窗口句柄
DISCL_BACKGROUND | DISCL_NONEXCLUSIVE
);
// 枚举轴并设置范围
hr = m_pJoystick->EnumObjects(enumAxesCallback, this, DIDFT_AXIS);
if (FAILED(hr)) {
emit errorOccurred("枚举摇杆轴失败");
return false;
}
// 创建事件对象
m_hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
hr = m_pJoystick->SetEventNotification(m_hEvent);
if (FAILED(hr)) {
emit errorOccurred("设置事件通知失败");
return false;
}
return true;
}
BOOL CALLBACK DirectInputJoystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context) {
DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
DIPROPRANGE diprg;
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYID;
diprg.diph.dwObj = doi->dwType;
diprg.lMin = -1000;
diprg.lMax = 1000;
HRESULT hr = joystick->m_pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph);
return SUCCEEDED(hr);
}
void DirectInputJoystick::startListening() {
if (!m_pJoystick || m_isRunning) return;
m_isRunning = true;
m_workerThread = QThread::create([this]() {
m_pJoystick->Acquire();
while (m_isRunning) {
WaitForSingleObject(m_hEvent, INFINITE);
DIJOYSTATE state;
HRESULT hr = m_pJoystick->GetDeviceState(sizeof(DIJOYSTATE), &state);
if (SUCCEEDED(hr)) {
// 处理轴数据
double x = state.lX / 1000.0;
double y = state.lY / 1000.0;
double z = state.lZ / 1000.0;
emit axisChanged(0, x);
emit axisChanged(1, y);
emit axisChanged(2, z);
// 处理按钮
for (int i = 0; i < 32; ++i) {
if(lastState.rgbButtons[i]!=state.rgbButtons[i])
{
bool pressed = (state.rgbButtons[i] & 0x80) != 0;
emit buttonChanged(i, pressed);
}
}
if(lastState.rgdwPOV[0]!=state.rgdwPOV[0])
{
emit directionChanged(state.rgdwPOV[0]/100);
}
}
lastState = state;
}
});
connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
m_workerThread->start();
}
void DirectInputJoystick::cleanup() {
m_isRunning = false;
if (m_hEvent) {
CloseHandle(m_hEvent);
m_hEvent = nullptr;
}
if (m_pJoystick) {
m_pJoystick->Unacquire();
m_pJoystick->Release();
m_pJoystick = nullptr;
}
if (m_pDirectInput) {
m_pDirectInput->Release();
m_pDirectInput = nullptr;
}
}

@ -0,0 +1,47 @@
#ifndef DIRECTINPUTJOYSTICK_H
#define DIRECTINPUTJOYSTICK_H
#include <QObject>
#include <windows.h>
#include <dinput.h>
#include <QThread>
class DirectInputJoystick : public QObject
{
Q_OBJECT
public:
explicit DirectInputJoystick(QObject *parent = nullptr);
~DirectInputJoystick();
bool initialize();
void startListening();
signals:
// 轴变化信号axis: 0-X, 1-Y, 2-Z; value: -1.0 ~ 1.0
void axisChanged(int axis, double value);
// 按钮状态变化button: 按钮编号从0开始pressed: 是否按下)
void buttonChanged(int button, bool pressed);
// 方向按钮状态变化direction: 按钮编号上下左右pressed: 是否按下)
void directionChanged(int direction);
// 错误信号
void errorOccurred(const QString &message);
private:
bool setupDevice();
void cleanup();
static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context);
static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context);
DIJOYSTATE lastState;
LPDIRECTINPUT8 m_pDirectInput = nullptr;
LPDIRECTINPUTDEVICE8 m_pJoystick = nullptr;
HANDLE m_hEvent = nullptr;
QThread *m_workerThread;
bool m_isRunning = false;
};
#endif // DIRECTINPUTJOYSTICK_H

@ -0,0 +1,106 @@
#include "joystickcontroller.h"
#include <QDebug>
JoystickController::JoystickController(int joystickId, QObject *parent)
: QObject{parent}, m_joystickId(joystickId), m_isConnected(false)
{
// 初始化定时器
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &JoystickController::pollJoystick);
// 检测设备是否存在
UINT numDevs = joyGetNumDevs();
if (numDevs == 0) {
emit errorOccurred("系统未检测到任何摇杆设备");
return;
}
// 获取设备能力
MMRESULT capsResult = joyGetDevCaps(m_joystickId, &m_joyCaps, sizeof(JOYCAPS));
if (capsResult != JOYERR_NOERROR) {
emit errorOccurred(QString("无法获取摇杆设备能力,错误代码: %1").arg(capsResult));
return;
}
m_isConnected = true;
qDebug() << "摇杆设备初始化成功,名称:" << QString::fromWCharArray(m_joyCaps.szPname);
}
JoystickController::~JoystickController()
{
stopPolling();
}
bool JoystickController::isConnected() const
{
return m_isConnected;
}
void JoystickController::startPolling(int intervalMs)
{
if (m_isConnected) {
m_timer->start(intervalMs);
}
}
void JoystickController::stopPolling()
{
m_timer->stop();
}
void JoystickController::pollJoystick()
{
JOYINFOEX joyInfo;
joyInfo.dwSize = sizeof(JOYINFOEX);
joyInfo.dwFlags = JOY_RETURNALL;
MMRESULT result = joyGetPosEx(m_joystickId, &joyInfo);
if (result != JOYERR_NOERROR) {
emit errorOccurred(QString("读取摇杆数据失败,错误代码: %1").arg(result));
m_isConnected = false;
stopPolling();
return;
}
// 处理轴数据
normalizeAxisValues(joyInfo);
// 处理按钮状态
for (int i = 0; i < m_joyCaps.wNumButtons; ++i) {
bool isPressed = (joyInfo.dwButtons & (1 << i)) != 0;
emit buttonChanged(i + 1, isPressed); // 按钮ID从1开始
}
}
void JoystickController::normalizeAxisValues(const JOYINFOEX &joyInfo)
{
// X/Y轴归一化处理
double x = (joyInfo.dwXpos - 32768) / 32768.0;
double y = (joyInfo.dwYpos - 32768) / 32768.0;
emit axisChanged(1, x); // 假设轴1为X轴
emit axisChanged(2, y); // 轴2为Y轴
// 其他轴如Z轴、Rudder等
if (m_joyCaps.wCaps & JOYCAPS_HASZ) {
double z = (joyInfo.dwZpos - 32768) / 32768.0;
emit axisChanged(3, z);
}
}

@ -0,0 +1,41 @@
#ifndef JOYSTICKCONTROLLER_H
#define JOYSTICKCONTROLLER_H
#include <QObject>
#include <QTimer>
#include <windows.h>
#include <mmsystem.h>
class JoystickController : public QObject
{
Q_OBJECT
public:
explicit JoystickController(int joystickId = JOYSTICKID1, QObject *parent = nullptr);
~JoystickController();
bool isConnected() const;
void startPolling(int intervalMs = 50);
void stopPolling();
signals:
// 摇杆轴变化信号(范围:-1.0 ~ 1.0
void axisChanged(int axis, double value);
// 按钮状态变化信号按钮ID, 是否按下)
void buttonChanged(int button, bool pressed);
// 错误信号
void errorOccurred(const QString &message);
private slots:
void pollJoystick();
private:
void normalizeAxisValues(const JOYINFOEX &joyInfo);
QTimer *m_timer;
int m_joystickId;
bool m_isConnected;
JOYCAPS m_joyCaps;
};
#endif // JOYSTICKCONTROLLER_H

@ -32,6 +32,7 @@ MainWindow::MainWindow(QWidget *parent)
qDebug() << "Open file fail " << Qt::endl;
}
// QList<QString> list;
// list.append("rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid");
// list.append("rtsp://182.92.130.23/nmyj/video");
@ -60,6 +61,8 @@ MainWindow::MainWindow(QWidget *parent)
initHideButton();
//initJoyStick();
initDtJoyStick();
setSavedVideoDir();
// ui->stackedWidget->setCurrentIndex(1);
// ui->videoWidget1->play(list.at(0));
@ -123,6 +126,8 @@ void MainWindow::initSignalConnection() {
connect(ui->videoControlWidget, &VideoControl::stopVideoSignal, this,
&MainWindow::closeVideoSlot);
}
void MainWindow::initNotifyMessageConnection() {
@ -141,6 +146,38 @@ void MainWindow::initChangeVideoLayoutConnection() {
});
}
void MainWindow::onAxisChanged(int axis, double value)
{
qDebug() << "Axis" << axis << "Value:" << value;
}
// void MainWindow::initJoyStick()
// {
// m_joystick = new JoystickController(JOYSTICKID1,this);
// connect(m_joystick, &JoystickController::axisChanged, this, &MainWindow::onAxisChanged);
// connect(m_joystick, &JoystickController::buttonChanged, this, &MainWindow::onButtonChanged);
// if (m_joystick->isConnected()) {
// m_joystick->startPolling();
// }
// }
void MainWindow::initDtJoyStick()
{
m_dtjoystick = new DirectInputJoystick(this);
if (m_dtjoystick->initialize()) {
connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, ui->commandWidget, &CommandWidget::onButtonChanged);
connect(m_dtjoystick, &DirectInputJoystick::directionChanged, ui->commandWidget, &CommandWidget::onDirectionChanged);
// connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, this, &MainWindow::onButtonChanged);
m_dtjoystick->startListening();
}
}
void MainWindow::initNotifyManager() {
m_notifyManager = new NotifyManager(this, this);
m_notifyManager->setMaxCount(5);

@ -16,7 +16,9 @@
#include "videoControl.h"
#include "videowidget.h"
#include "webmapwidget.h"
//#include "joystickapi.h"
#include "joystickcontroller.h"
#include "DirectInputJoystick.h"
#ifdef Q_OS_WIN
#include "windows.h"
#include "windowsx.h"
@ -68,6 +70,14 @@ private:
void setSavedVideoDir();
void initHideButton();
// void initJoyStick();
void initDtJoyStick();
private slots:
void onAxisChanged(int axis, double value);
private slots:
void showSettingDlgSlot();
@ -92,5 +102,8 @@ private:
QToolButton *hideCommandWidgetBtn = nullptr;
WebMapWidget *m_mapWidget = nullptr;
// JoystickController *m_joystick = nullptr;
DirectInputJoystick *m_dtjoystick;
};
#endif // MAINWINDOW_H

@ -209,6 +209,7 @@ bool ReadStream::initSocket(QString ip, int port) {
}
}
}
}
bool ReadStream::initSavedRawFile(QString fileDir, QString uavName) {

@ -12,7 +12,6 @@
#include "avpacketqueuemanager.h"
#include "decodestream.h"
#include "ffmpeginclude.h"
#include "pushstream.h"
#include "savestream.h"

Loading…
Cancel
Save