Should QFileSystemWatcher work with device nodes? - c++

Qt is new to me so I don't know all the idioms yet. I have a custom linux driver which exposes itself as /dev/mydevice. When something interesting happens in hardware, the driver writes some data to that file. I have tested that this works with
xxd -l 16 /dev/mydevice
and can see my data being dumped to the screen when I press a button.
Now I want a simple GUI to show me what's being dumped; QFileSystemWatcher seems like a good candidate since it "monitors the file system for changes to files," but it doesn't fire the fileChanged signal.
I'm guessing QFileSystemWatcher is just looking at the modification time or something like that? Since QFile doesn't implement the readyRead signal, am I down to spawning a new thread and looping on QFile::read()? Or implementing my own QIODevice that does that? What's the best way to achieve my goal?
Here is the toy example.
main.cpp:
#include <QCoreApplication>
#include <QDebug>
#include <QFileSystemWatcher>
#include <QString>
class EventTester : public QObject
{
Q_OBJECT
public:
EventTester(QObject *parent = 0) : QObject(parent)
{
qfsw = new QFileSystemWatcher(this);
if (!qfsw->addPath("/dev/mydevice")) {
qDebug() << "Couldn't add watcher.";
}
connect(qfsw, &QFileSystemWatcher::fileChanged,
this, &EventTester::onEvent);
}
QFileSystemWatcher *qfsw;
public slots:
void onEvent(const QString &path)
{
Q_UNUSED(path);
qDebug() << "We got a special event!";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
EventTester e();
return a.exec();
}
qfswtestcon.pro:
QT += core
QT -= gui
TARGET = qfswtestcon
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp

Related

Handle and trigger events from QML to C++ and viceversa

I'm working with Qt, using the latest version of Qt Creator on windows 8.1. Once I finished my GUI, I tried to communicate some of my QML elements in C ++ and vice versa, i.e. send data from the two sides.
Example I've tried
I had no idea how to do this, then I have forwarded to read the official documentation and examples from this site, but no one works for me.
Code:
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QObject>
#include <QGuiApplication>
#include <QQuickView>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}
But i'm getting an error:
C:\Users\Tomi\qml\main.cpp:20: error: cannot convert 'QQuickItem*' to 'QObject*' in initialization
QObject *item = view.rootObject();
What I want
All I need is that when a button is pressed from QML, certain data to C++ are requested and when they are ready to be sent to QML. Is this possible?, could you show me a simplistic and functional example?
Thanks!
The error is because the compiler isn't aware of what a QQuickItem is. You need to include it:
#include <QQuickItem>
QQuickItem is only forward-declared in QQuickView's header, for example, so you can't rely on it to include QQuickItem for you, and shouldn't anyway.
Also, the comment about moc not working with classes defined in main.cpp is wrong; you just need to include main.moc after your class definition for the QObject stuff to work:
#include "main.moc"
I dislike this myth, because it turns short snippets and examples into three files, when they could be contained in just one, which is much more useful on Stack Overflow, bug trackers, etc. :)

Communication between a server thread and a man-machine interface (MMI)

I need your advice on a program I'm coding right now. Let me first present you what it is.
Design
I'm designing a man-machine interface (MMI). In this MMI, there are two core elements:
The MainWindow:
This MainWindow is the base of everything. Important: Here, I launch in a thread a server that receives the data from a client. This data is very important to the next element.
Supervision
This window contains a QTableWidget, the goal is to show in pseudo-real time the data received on the server in the thread of MainWindow.
The Problem
The server in the thread owned by the MainWindow receive a structure every 10 mS. How can I send these data to the Supervision window if it's open? I was thinking of using shared memory, but not really sure about this, and doesn't really which method I have to use.
Some Solutions
I tried to implement the solution of Sebastian Lange :
An emit in the thread Server
A connect in the MainWindow
A slot in supervision
So now my thread Server emit a signal at every frame received. But how can I do the connect in the MainWindow and how will Supervision receive the struct emit in the signal?
Here's the code of my emit:
MainWindow* MainWindow::m_psMainWindow = nullptr; // C++ 11 nullptr
void MainWindow::emit_signal_TrameRecu(StructureSupervision::T_StructureSupervision* ptr){
emit signal_TrameRecu(ptr);
}
void MainWindow::lancerServeur(std::atomic<bool>& boolServer){
serveur s;
StructureSupervision::T_StructureSupervision* bufferStructureRecu;
while(boolServer){
bufferStructureRecu = s.receiveDataUDP();
if(bufferStructureRecu->SystemData._statutGroundFlight != 0){
m_psMainWindow->emit_signal_TrameRecu( bufferStructureRecu );
}
}
}
Queued Connections
Qt makes cross thread communication easy when you use queued connections.
Your calls to connect should use the Qt::QueuedConnection or Qt::BlockingQueuedConnection connection types.
Custom Types in Slots and Signals
To use custom types (structs) in slots, signals, QVariant and properties you need to declare and register the type to make it available to the Qt dynamic type system.
In your header (.hpp) use Q_DECLARE_METATYPE and in your source (.cpp) use qRegisterMetaType.
Worked Example
server.hpp
#ifndef SERVER_HPP
#define SERVER_HPP
#include <QtCore>
struct customdata
{
int id;
QDateTime tstamp;
};
Q_DECLARE_METATYPE(customdata)
class Server : public QThread
{
Q_OBJECT
public:
Server();
signals:
void sendData(const customdata& d);
protected:
virtual void run();
};
#endif
server.cpp
#include "server.hpp"
static const int customdata_metatype_id =
qRegisterMetaType<customdata>();
Server::Server() : QThread() {}
void Server::run()
{
customdata d;
d.id = 0;
for (int i = 0; i < 10; ++i)
{
d.id++;
d.tstamp = QDateTime::currentDateTime();
emit sendData(d);
sleep(1);
}
}
window.hpp
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include <QtGui>
#include "server.hpp"
class Window : public QWidget
{
Q_OBJECT
public:
Window();
public slots:
void receiveData(const customdata& d);
private:
QListWidget* mList;
};
#endif
window.cpp
#include "window.hpp"
Window::Window() : QWidget(),mList(new QListWidget())
{
resize(400, 300);
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->addWidget(mList);
setLayout(mainLayout);
}
void Window::receiveData(const customdata& d)
{
QString str(QString("%1 %2").arg(d.id).arg(d.tstamp.toString()));
mList->addItem(str);
}
main.cpp
#include <QtGui>
#include "server.hpp"
#include "window.hpp"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Window win;
Server ser;
QObject::connect(
&ser, SIGNAL(sendData(customdata)),
&win, SLOT(receiveData(customdata)),
Qt::QueuedConnection);
win.show();
ser.start();
return app.exec();
}
test.pro
TEMPLATE=app
QT=core gui
HEADERS=server.hpp window.hpp
SOURCES=main.cpp server.cpp window.cpp
I once (in the days of Qt 3.2) implemented this cross thread communication using QApplication::postEvent (now QCoreApplication::postEvent). However nowadays best practise is to use promises and futures to communicate asynchronously between threads. Promises and futures has become a part of recent C++ standard and the concepts are also implemented separately as part of Qt 5 Concurrent framework. See http://qt-project.org/doc/qt-5/qtconcurrent-index.html

QProcess saving to QTextEdit

What I'm trying to do is launch a program within another program using QProcess and then save the output from the launched program into a QTextEdit of the launcher program. Every time I launch this program I want it to add more text to the QTextEdit. Now I get the program to launch but then after the text is supposed to be written it crashes. Here is the code:
#include <QWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QProcess>
#include <QVBoxLayout>
#include <QApplication>
class Widget : public QWidget
{
Q_OBJECT
QTextEdit* text;
public:
Widget() : text(new QTextEdit) {
QPushButton* addBtn = new QPushButton("Add Module");
text->setReadOnly(true);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(addBtn,0);
layout->addWidget(text);
connect(addBtn,SIGNAL(clicked()),SLOT(launchModule()));
}
Q_SLOT void launchModule() {
QString program = "C:/A2Q2-build-desktop/debug/A2Q1.exe";
QProcess *myProcess = new QProcess(this);
connect(myProcess, SIGNAL(finished(int)), SLOT(finished()));
connect(myProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(finished()));
myProcess->start(program);
}
Q_SLOT void finished() {
QProcess *process = qobject_cast<QProcess*>(sender());
QString out = process->readAllStandardOutput(); // will be empty if the process failed to start
text->append(out);
delete process;
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
app.exec();
}
#include "main.moc"
It's crashing because you're deleting the sender object while inside a slot. Instead of delete process, you should
process->deleteLater();
For logging purposes you should be using QPlainTextEdit instead of a QTextEdit. The former is faster. You're prematurely pessimizing by using the latter. Alas, even QPlainTextEdit becomes abysmally slow if you're sending about 100 lines/s (at least on Qt 4.8). If you want a really fast log view, you'll need to use QListWidget, with a caveat, or roll your own.
I have a complete example of how to send to and receive from a process in another answer.
The process is crashing because you're deleting the parent from within the finished slot.
Also, it's probably easier to do something like this:
QObject::connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(getOutput()));
instead of using the finished() slot. But that's more personal preference than anything.

Qt QFileSystemWatcher: signal fileChanged() gets emited only once

I was trying the QFileSystemWatcher out and it somehow doesn't work as expected. Or am I doing something wrong?
I've set the QFileSystemWatcher to watch a single file. When I modify the file for the first time, fileChanged() gets emited, that's OK. But when I modify the file again, fileChanged() doesn't get emited anymore.
Here is the source code:
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
mainwindow.h
#include <QDebug>
#include <QFileSystemWatcher>
#include <QMainWindow>
#include <QString>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private slots:
void directoryChanged(const QString & path);
void fileChanged(const QString & path);
private:
QFileSystemWatcher * watcher;
};
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow()
{
watcher = new QFileSystemWatcher(this);
connect(watcher, SIGNAL(fileChanged(const QString &)), this, SLOT(fileChanged(const QString &)));
connect(watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(directoryChanged(const QString &)));
watcher->addPath("path to directory");
watcher->addPath("path to file");
}
void MainWindow::directoryChanged(const QString & path)
{
qDebug() << path;
}
void MainWindow::fileChanged(const QString & path)
{
qDebug() << path;
}
Thank you for your answers.
Edit 1
I ran this code under Linux.
Edit 2
I actually need to check all MetaPost files in a tree given by some directory, whether they were modified. I will probably stick to my alternative solution, which is to run QTimer every second and manually check all files. The QFileSystemWatcher probably does this in similar fashion internally, but probably more effectively.
Had the same problem just now. Seems like QFileSystemWatcher thinks that the file is deleted even if it's only modified. Well at least on Linux file system. My simple solution was:
if (QFile::exists(path)) {
watcher->addPath(path);
}
Add the above to your handler of fileChanged(). Change the word watcher as necessary.
I had the same problem using Qt5 on Linux. Found out the reason :
Some text editors, like kate, don't modify the contents of a file, but replace the original file with a new file. Replacing a file will delete the old one (IN_DELETE_SELF event), so qt will stop watching the file.
A solution is to also watch the file's directory for creation events.
I can confirm your problem with current Qt5 and Linux.
In addition to the answer given by Peter I solved this problem by adding the following code to the end of the slot-function:
QFileInfo checkFile(path);
while(!checkFile.exists())
std::this_thread::sleep_for(std::chrono::milliseconds(10));
watcher->addPath(path);
Note that if you add the path immediately, the file often does not exist yet, you get a warning and nothing will be added at all and the watcher looses this path. Therefore, you have to wait/sleep until the file is back to life again, then add it.
Also note that in this example I used C++11 and included and for realizing the sleep.

Hide console window when gui program sends commands to a cli program?

In my project, I have made a GUI program that will occasionally send commands to a cli program. I do it like this:
system("folder\\program.exe -d folder\\inputFile.dat folder\\outputPath");
Obviously without those names but you get the idea. This works fine, except when my GUI program sends these commands, a command prompt window opens and does whatever the cli program is supposed to do. It looks very bad and unclean.
Is there any way I could "hide" the cli program window but still have it silently do what it needs to do?
Thanks for your time :)
EDIT: I tried olive's technique which was to use QDesktopServices and QUrl to call the program:
QDesktopServices::openUrl(QUrl("folder\\program.exe -d folder\\inputFile.dat folder\\outputPath"));
The console window isn't showing up, however, the program wasn't called at all. Are there any changes that need to be made to the path when using olive's technique rather than my original system() command?
I cannot determine whether you need a cross platform solution or not. On windows execution using start generally hides the command window.
system("start program.exe -d inputFile.dat outputPath");
I solved this problem like so:
QProcess::execute("start program.exe -d inputFile.dat outputPath");
The problem is, I can only do this once. Everytime I try to call it again, it will not work. The thing that makes this hidden is "start." Taking it out allows the console to be seen, it's just blank.
It seems like I need a way to "end" the program or whatever before running it again. (I say or whatever because I have no clue what/why adding "start" to the path does)
QDesktopServices::openUrl() is usually used if you wish to open a document (eg PDF document, web page) in a viewing or editing program and you're not sure which programs have been installed. This function lets the operating system choose for you from the list of default programs with respect to the file types.
Although you can also use the function to open executable files (eg console programs), an alternative to that would be using QProcess. If you don't need to communicate with the console program or wait for it to complete, you can just launch it in a fire-and-forget fashion using one of the two QProcess::startDetached() static functions
QProcess::startDetached
Sorry for misleading with QDesktopService::URL, later i understood that it wont accept parameter.
So implemented by improving error handling, if process not started/exited badly or waitfor the process to finish the task..I think this is useful
In QProcess, execute is blocking thread, but start is resuming the thread.
Current code is using start() API, but more or less featurewise like execute..
Code is copied from SO and modified little for the current requirements.
> Mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QProcess>
#include <QShortcut>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
cameraControl = new QProcess(this);
}
MainWindow::~MainWindow()
{
delete ui;
cameraControl->close();
delete cameraControl;
}
void MainWindow::on_pushButton_clicked()
{
// connect the camera control finished signal to the slot that will read the exit code and
// place the std output string from the process into a label on the form
connect(cameraControl, SIGNAL(finished(int , QProcess::ExitStatus )),
this, SLOT(on_cameraControlExit(int , QProcess::ExitStatus )));
// Disable the ui button do we don't get double presses
ui->pushButton->setDisabled(true);
// setup the gphoto2 arguments list
QStringList args;
args.append("d:\\text.txt");
// start the camera control
cameraControl->start("notepad",args);
// // wait for the process to finish or 30 seconds whichever comes first
cameraControl->waitForFinished(30000);
}
void MainWindow::on_cameraControlExit(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << cameraControl->errorString();
qDebug() << cameraControl->readAllStandardError();
qDebug() << cameraControl->readAllStandardOutput();
ui->pushButton->setEnabled(true);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QString>
#include <QProcess>
#include <QObject>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void reply2();
private slots:
void on_pushButton_clicked();
void on_cameraControlExit(int exitCode, QProcess::ExitStatus exitStatus);
private:
Ui::MainWindow *ui;
QProcess* cameraControl;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
in your Qt program,there is a .pro file.You can add this line into the file:
config+=console