如何使用QT在Linux中读取文件设备

How to read a file device in Linux using Qt?

本文关键字:读取 文件 Linux 何使用 QT      更新时间:2023-10-16

我正在处理一个基于QT5的小GUI,该GUI应显示Linux文件设备的数据流。为此,我选择了操纵杆输入。使用cat /dev/input/js0,可以在终端上看到传入流。

使用C,您可以使用带有阻止读取或处理设备信号的循环读取此设备文件。但是我不用qt。

使用QT与设备文件进行交互的典型方法是什么?


基于@rodrigo的答案,这里有一个新的实现:

joystick.h

#ifndef JOYSTICK_H
#define JOYSTICK_H
#include <QObject>
#include <QFile>
#include <QSocketNotifier>
class Joystick
      : public QObject
{
    Q_OBJECT
    QString fileName = "/dev/input/js0";
    QFile *file;
    QSocketNotifier *notifier;
public:
    explicit Joystick(QObject *parent = nullptr);
    ~Joystick();
signals:
public slots:
    void handle_readNotification(int socket);
};
#endif // JOYSTICK_H

joystick.cpp

#include "joystick.h"
Joystick::Joystick(QObject *parent)
   : QObject(parent)
{
    file = new QFile();
    file->setFileName(fileName);
    if( !file->exists() ){
        qWarning("file does not exist");
        return;
    }
    if( !file->open(QFile::ReadOnly) ){
        qWarning("can not open file");
        return;
    }
    notifier = new QSocketNotifier( file->handle(),
                                    QSocketNotifier::Read,
                                    this);
    connect( notifier,
             &QSocketNotifier::activated,
             this,
             &Joystick::handle_readNotification );
    if( !notifier->isEnabled() ){
        qInfo("enable notifier");
        notifier->setEnabled(true);
    }
    qInfo("Joystick init ready");
}
void
Joystick::handle_readNotification(int /*socket*/)
{
    static quint64 cnt=0;
    qInfo("cnt: %d",cnt++);
    if( !(file->isOpen()) ){
        qWarning("file closed");
        return;
    }
    char buf[16]; /* tested with different sizes */
    if( file->read(buf,sizeof(buf)) ){
        qInfo("read: %s",buf);
    }
//  QByteArray ba = file->readAll();
//  qInfo("Data: %s", ba.data());
}

然后我运行,最后一个排名是cnt: 0。看来readreadAll立即呼叫封锁。如果我评论读取电话,则计数器运行速度很快。这里是一个类似的帖子,这里是错误的?


最终解决方案

感谢Rodrigo!

joystick.h

#ifndef JOYSTICK_H
#define JOYSTICK_H
#include <QObject>
#include <QFile>
#include <QSocketNotifier>
class Joystick
      : public QObject
{
    Q_OBJECT
    QString fileName = "/dev/input/js0";
    QSocketNotifier *notifier;
    int fd;
public:
    explicit Joystick(QObject *parent = nullptr);
    ~Joystick();
signals:
    void buttonPressed(quint8 number, qint16 value);
    void axisMoved(quint8 number, qint16 value);

public slots:
    void handle_readNotification(int socket);
};
#endif // JOYSTICK_H

joystick.cpp

#include "joystick.h"
#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>
Joystick::Joystick(QObject *parent)
   : QObject(parent)
{
    auto file = new QFile();
    file->setFileName(fileName);
    if( !file->exists() ){
        qWarning("file does not exist");
        return;
    }
    fd = open(fileName.toUtf8().data(), O_RDONLY|O_NONBLOCK);
    if( fd==-1 ){
        qWarning("can not open file");
        return;
    }
    notifier = new QSocketNotifier( fd,
                                    QSocketNotifier::Read,
                                    this);
    connect( notifier,
             &QSocketNotifier::activated,
             this,
             &Joystick::handle_readNotification );
}
Joystick::~Joystick()
{
    if( fd>=0 ){
        close(fd);
    }
}
void
Joystick::handle_readNotification(int /*socket*/)
{
    struct js_event buf;
    while( read(fd,&buf,sizeof(buf))>0 ){
        switch (buf.type) {
        case JS_EVENT_BUTTON:
            emit buttonPressed(buf.number, buf.value);
            break;
        case JS_EVENT_AXIS:
            emit axisMoved(buf.number, buf.value);
            break;
        }
    }
}

通常使用工具包的轮询来源解决此问题。如果是QT,这是QSocketNotifier。尽管它的名称(历史事故?(,但它可用于对任何文件描述符进行轮询,而不仅仅是插座。

因此,您只需使用open()打开设备以获取文件描述符,然后在其上使用QSocketNotifier::Read创建QSocketNotifier。当要读取事件时,您将在其上获得activate()信号。

替代解决方案,如果您仍然要使用Qfile而不是低级读/写功能:

auto file = new QFile(fileName);
...
// IMPORTANT: You must set the Unbuffered flag below
if(!file->open(QFile::ReadOnly | QFile::Unbuffered))
{
    // handle error
}
auto fd = file.handle();
auto flags = fcntl(fd, F_GETFL, 0);
if(flags == -1)
{
    // handle error
}
flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if(flags == -1)
{
    // handle error
}
auto notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
...
struct js_event buf;
while(file->read(&buf, sizeof(buf)) > 0)
{
    // process data
}