I need to connect a QProcess to an error handler, but I'm unsure how to pass the error string to the slot. What's below compiles, but doesn't work.
QString MainWindow::RunProcess(QString cstring)
{
QProcess *process = new QProcess(this);
connect(process,SIGNAL(readyReadStandardError()),this,SLOT( adberror(process::ReadAllStandardError() ) ))
process->start(cstring);
}
void MainWindow::adberror(QString errtxt)
{
qDebug() << "error handler";
qDebug() << errtxt;
}
I can induce a a process error, but adberror() never triggers.
When run, in the Application Output pane I see:
QObject::connect: No such slot MainWindow::adberror(process::ReadAllStandardError() )
QObject::connect: (receiver name: 'MainWindow')
edit: this is Qt 5.6. I did a new qmake/clean.
you have two options
1- wait before reading the output
QString MainWindow::RunProcess(QString cstring)
{
QProcess process;
process.start(cstring);
process.waitForFinished();
QString str = process.readAllStandardOutput();
}
2- make you process a member variable and remove your 1st argument from adberror. So,
in RunProcess
connect(process,SIGNAL(readyReadStandardError()),this,SLOT(adberror()))
then in adberror
QString str = process->readAllStandardOutput();
note that in your code you have a problem since your signal and slot args don't to match .. Also, ReadAllStandardError is not going to be ready anyways !
Edit: more code for the 2nd solution
mainwindow.h
class MainWindow
{
private://methods
void adberror();
private://attributes
QProcess* process;
};
mainwindow.cpp
QString MainWindow::RunProcess(QString cstring)
{
process = new QProcess(this);
connect(process,SIGNAL(readyReadStandardError()),this,SLOT(adberror()));
connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
process->start(cstring);
}
void MainWindow::adberror()
{
QString str = process->readAllStandardOutput();
qDebug() << str;
}
To process the readyReadStandardError() signal you should define the slot as:
private slots:
void adberror();
and use it:
connect(process,SIGNAL(readyReadStandardError()),this,SLOT( adberror() ));
i.e. with no arguments. Keep child process as a field of your MainWindow class to read data when it will be available.
Related
I try to read out the output of a command executed in QProcess. I don't get the connection working between QProcess's finished signal and a finished handler.
I tried several different ways of connecting to QProcess finished signal but no luck so far. I know that the process executes because it prints the output in the GUI's console and that the process finishes because it passes the waitForFinished method from sequential API.
Why does my code not hit onFinish or onFinishNoPams?
header:
class Test : public QObject
{
Q_OBJECT
public:
Test(QObject *parent = 0);
Q_INVOKABLE void read();
public slots:
void onFinish(int exitCode , QProcess::ExitStatus exitStatus);
void onFinishNoPams();
private:
QProcess *m_process;
};
cpp:
Test::Test(QObject *parent)
: QObject(parent)
{}
void Test::read(){
m_process=new QProcess(this);
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinishNoPams()));
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinish(int,QProcess::ExitStatus)));
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){
qDebug() << "not reached";
});
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,&Test::onFinish);
QString program = "lsusb";
QStringList arguments;
m_process->execute(program,arguments);
m_process->waitForFinished();
qDebug() << "reached";
return;
}
void Test::onFinish(int exitCode , QProcess::ExitStatus exitStatus){
qDebug() << "not reached";
}
void Test::onFinishNoPams(){
qDebug() << "not reached";
QByteArray out=m_process->readAllStandardOutput();
}
qml:
import test 1.0
Window {
Test{
id:test
Component.onCompleted: test.read()
}
}
QProcess::execute() is a static method that starts the external process and waits for its completion in a blocking way. It has nothing to do with your m_process variable. What you want to use is QProcess::start().
I am trying to put the data passed to a mainwindow.cpp function on the screen with the typical ui->control->setText(message) without success. The same line works if it is in a timer loop or a button function but not from display_that_data function
"MainWindow::display_that_data()" is called from myudp.cpp with the following lines
MainWindow show_tlm;
show_tlm.display_that_data(data_source, buf_copy);
mainwindow.cpp (updated to include emitting a signal for a SIGNAL/SLOT connection)
The SIGNAL/SLOT connection is set up in the constructor with the following line
connect (this, SIGNAL (showdata_signal()), this, SLOT(showdata_slot()));
void MainWindow::display_that_data(QByteArray data_source, QByteArray tlmBuf){
QString msg ;
msg = " in display_that_data";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
//generate a signal which will trigger showdata_slot
emit showdata_signal();
msg = " in display_that_data after emit showdata_signal()";
qDebug() << msg ;
}
void MainWindow::showdata_slot() {
QString msg = "showdata_slot called";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
}
Runtime debug messages show that code is making it to the showdata_slot but it is still not writing to the ui->tlm_vals
" in display_that_data"
"showdata_slot called"
" in display_that_data after emit showdata_signal()"
but.... neither one of the ui->tlm_vals->setText(msg) lines are putting text on the ui
"MainWindow::realtimeDataSlot()" is called by at timer timout signal as follows:
void MainWindow::setupRealtimeDataDemo(QCustomPlot *customPlot) {
// setup a timer that repeatedly calls MainWindow::realtimeDataSlot
connect(&dataTimer, SIGNAL(timeout()), this, SLOT(realtimeDataSlot()));
dataTimer.start(1000);
}
void MainWindow::realtimeDataSlot(){
QString temp = QString("%1").arg(epochTime, 10, 10, QChar('0'));
ui->tlm_vals->setText(temp);
}
And this works perfectly (of course I have to disable it to see if showdata_slot is writing to the ui)
I thought the problem was a needed SIGNAL and SLOT connection to trigger the write to the ui but generating a SIGNAL/SLOT connection (which debug shows as working) still does not write to the ui from the slot function.
For completeness mainwindow.h contains the following
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setupRealtimeDataDemo(QCustomPlot *customPlot);
void display_that_data (QByteArray data_source, QByteArray tlmBuf);
private slots:
void realtimeDataSlot();
void showdata_slot();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
What am I missing/doing wrong?
Somewhere deeply embedded in Qt there is a difference between gui mouse click generated event and code generated event and this somehow caues Qt behavior to vary with the same lines of code. I will repost a more more "root level" question on this topic.
Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.
I want to launch a SCPI command in my device using netcat utility under Ubuntu 10.04 LTS with Qt. My code looks like:
env = "echo TRIG | nc 192.168.1.100 23 -q1";
process1.execute(env);
process1.waitForFinished(1000);
This command does not return any data but simply triggers the data acquisition.
If using terminal with same "echo TRIG | nc 192.168.1.100 23 -q1" command, everything works fine.
From Qt, it does not work. The debug output is "TRIG | nc 10.0.3.250 23 -q1" ... so without an "echo". My device does not receive the TRIG command.
Could you please advise what I'm doing wrong?
Many thanks.
The code below shows a fairly complete, asynchronous implementation of this functionality. It demonstrates how it can be done without launching external processes, and how to leverage C++11 lambdas in Qt 5. For Qt 4, the slots from main() would need to live in their own QObject-derived class.
#include <QtWidgets>
#include <QtNetwork>
class SocketSignaler : public QObject
{
Q_OBJECT
Q_SLOT void stateChanged(QAbstractSocket::SocketState state) {
if (state == QAbstractSocket::UnconnectedState) emit unconnected();
else emit busy();
emit hasState(this->state());
}
public:
explicit SocketSignaler(QAbstractSocket * socket) : QObject(socket) {
connect(socket, &QAbstractSocket::stateChanged, this, &SocketSignaler::stateChanged);
connect(&(const QObject&)QObject(), &QObject::destroyed, this, // defer signal emission
[=]{ emit stateChanged(socket->state()); }, Qt::QueuedConnection);
}
Q_SIGNAL void busy();
Q_SIGNAL void unconnected();
Q_SIGNAL void hasState(const QString &);
QString state() const {
switch (static_cast<QAbstractSocket*>(parent())->state()) {
case QAbstractSocket::UnconnectedState: return "Disconnected";
case QAbstractSocket::HostLookupState: return "Looking up host";
case QAbstractSocket::ConnectingState: return "Connecting";
case QAbstractSocket::ConnectedState: return "Connected";
case QAbstractSocket::ClosingState: return "Closing";
default: return {};
}
}
};
class Ui : public QWidget {
Q_OBJECT
Q_PROPERTY(bool busy WRITE setBusy)
QVBoxLayout m_layout{this};
QFormLayout m_form;
QLineEdit m_target{"192.168.1.100"};
QLineEdit m_message{"TRIG"};
QLabel m_state;
QDialogButtonBox m_box;
QPushButton * const m_send = m_box.addButton("Send", QDialogButtonBox::AcceptRole);
QPushButton * const m_cancel = m_box.addButton(QDialogButtonBox::Cancel);
QMessageBox m_msgBox{this};
public:
Ui() {
m_form.addRow("Target Host", &m_target);
m_form.addRow("Command", &m_message);
m_layout.addLayout(&m_form);
m_layout.addWidget(&m_state);
m_layout.addWidget(&m_box);
m_msgBox.setIcon(QMessageBox::Critical);
connect(m_send, &QPushButton::clicked, this, &Ui::send);
connect(m_cancel, &QPushButton::clicked, this, &Ui::cancel);
}
void setState(const QString & text) { m_state.setText(text); }
QString target() const { return m_target.text(); }
QString message() const { return m_message.text(); }
void showError(const QString & text) {
m_msgBox.setText(text);
m_msgBox.show();
}
void setBusy(bool busy) {
m_send->setEnabled(!busy);
m_cancel->setEnabled(busy);
}
Q_SIGNAL void send();
Q_SIGNAL void cancel();
};
int main(int argc, char *argv[])
{
const int targetPort = 23;
QApplication app{argc, argv};
Ui ui;
ui.show();
QTcpSocket socket;
SocketSignaler socketSig{&socket};
QObject::connect(&socketSig, &SocketSignaler::hasState, &ui, &Ui::setState);
QStateMachine machine;
QState sReady{&machine};
QState sBusy{&machine};
sReady.assignProperty(&ui, "busy", false);
sBusy.assignProperty(&ui, "busy", true);
sReady.addTransition(&socketSig, &SocketSignaler::busy, &sBusy);
sBusy.addTransition(&socketSig, &SocketSignaler::unconnected, &sReady);
QObject::connect(&ui, &Ui::send, [&](){
socket.connectToHost(ui.target(), targetPort);
});
QObject::connect(&ui, &Ui::cancel, [&](){ socket.abort(); });
QObject::connect(&socket,
static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>
(&QAbstractSocket::error), [&]()
{
ui.showError(socket.errorString());
});
QObject::connect(&socket, &QAbstractSocket::connected, [&](){
auto msg = ui.message().toLatin1();
msg.append('\n');
if (socket.write(msg) >= msg.size()) socket.close();
});
QObject::connect(&socket, &QAbstractSocket::bytesWritten, [&](){
if (!socket.bytesToWrite()) socket.close();
});
machine.setInitialState(&sReady);
machine.start();
return app.exec();
}
#include "main.moc"
You can't use the pipe command (|) with QProcess that way.
There are a few ways to tackle this: -
You can call the first command and retrieve its output before processing it either in Qt or with another call to QProcess.
Or, create a script that you call from QProcess and retrieve the output.
Finally, assuming you're using linux / OSX, you can call QProcess with /bin/bash and pass the command to that. For example: -
env = "/bin/bash \"echo TRIG | nc 192.168.1.100 23 -q1\"";
process1.execute(env);
You can probably find an equivalent to /bin/bash for windows, perhaps cmd.exe
I've "Core" object that handles QMainWindow.
Core.h code
class Core : public QObject
{
Q_OBJECT
public:
explicit Core(QObject *parent = 0);
~Core();
void appInit();
int getAuth();
public slots:
void appExit();
private slots:
void appMenuTriggered(QAction *action);
private:
void preInit();
MainWindow *mwnd;
};
Core.cpp code
Core::Core(QObject *parent) : QObject(parent)
{
qDebug() << "Core::Constructor called";
preInit();
}
Core::~Core()
{
delete mwnd;
qDebug() << "Core::Destructor called";
}
int Core::getAuth()
{
LoginDialog *login = new LoginDialog();
int r = login->exec();
delete login;
return r;
}
void Core::appExit() // connected to qapplication aboutToQuit
{
qDebug() << "Core::appExit called";
}
void Core::preInit() // called after getAuth im main.cpp
{
qDebug() << "Core::preInit called";
}
void Core::appMenuTriggered( QAction *action )
{
qDebug() << "action triggered";
}
void Core::appInit()
{
mwnd = new MainWindow();
mwnd->show();
qDebug() << "Core::appInit called";
}
I'm trying to connect mainwindow menubar signal to core slot like this:
connect(mwnd->menuBar(), SIGNAL(triggered()), this, SLOT(appMenuTriggered()));
But it doesn't work. Im new to c++ and Qt. How to connect this?
Or maybe there is better way to handle mainwindow actions to other programm parts.
UPD
Problem solved. Forget to include QMenuBar
You have to give the full function spec in the SIGNAL and SLOT parameters (but without the argument names). Like this:
connect(mwnd->menuBar(),
SIGNAL(triggered(QAction*)),
this,
SLOT(appMenuTriggered(QAction*)));
If you debug such code in Qt Creator, the connect function will write diagnostic error messages to the Application Output pane when it doesn't find a signal or a slot. I suggest that you find these error messages before you fix your problem, so that you know where to look in future. It's very easy to get signals and slots wrong!