I'm working on a small Qt5-based GUI, which shall display a data stream from a Linux file device.
For that I choose a joystick input. With cat /dev/input/js0
it is possible to see the incoming stream on the terminal.
Using C you can read this device file using a loop with a blocking read or handle the device signals. But I don't get this with Qt.
What is the typical approach to interact with device files using Qt?
Based on the answer of @rodrigo here a new implementation:
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());
}
Then I run this, the last out put is cnt: 0
. It seems, that the read
or readAll
call now blocks. If I comment out the read calls the counter runs very fast. here a similiar post That is wrong here?
Thanks to 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;
}
}
}
This problem is usually solved with a polling source for your toolkit. In case of Qt, this is the QSocketNotifier
. Despite its name (an historical accident?), it can be used to poll on any file descriptor, not just sockets.
So you just open the device, with open()
to get the file descriptor, and then create a QSocketNotifier
on it, with type QSocketNotifier::Read
. When there are events to be read you will get the activate()
signal on it.
Alternative solution, if you still want to use QFile instead of low-level read/write functions:
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With