Home > Software design >  Qt application hangs on process.start() function. Happens only when function is being called from QM
Qt application hangs on process.start() function. Happens only when function is being called from QM

Time:11-11

I have Embedded Qt applicaiton runing on my HMI screen. I am trying to execute some commands to execute in cmd. I am calling this c function simply from QML. Everytime I call it it hangs on process.start(). Do anyone have any experience for such issue? please help. I have ceated a simple function to print out date and it still hangs at process.start() regardless what cmd I execute.

cmd.sprintf("date  %%F' '%%X");
qDebug() << "cmd: " << cmd;
process.start("sh", QStringList()<<"-c"<<cmd);
process.waitForFinished(1000);
dtval = process.readAllStandardOutput();
process.close();

I am using Qt 5.9 on Ubuntu 18.04.6LTS platform.

CodePudding user response:

I did some further troubleshooting, and I think I found a mistake in your start command. It should be:

    process.start("/bin/sh", QStringList()<<"-c"<<"date"<<" %F  %X");

Your mistake was your command date was joined to its arguments whereas it should have been separate.

Since you were interested in QML, I mocked up the following C application where I invoked a similar command:

    process.start("/bin/sh", ["-c", "date", " %F  %X"], Process.ReadOnly);

I also made the program listen and wait for either onReadyReadStandardOutput and/or onReadyReadStandardError before calling process.readAllStandardOutput() and/or process.readAllStandardError() respectively.

For convenience, I also mapped the OpenMode flags so that they can be used in QML.

When you run the program there's a "Go!" button in the footer. When you click on it, it runs the process.start(), and the output, be it either regular output or error gets displayed in the ListView above. When I run it, I see an output like this:

qt-process-app

#qt-process-app.pro
QT  = quick

CONFIG  = c  11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES  = QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES  = \
        Process.cpp \
        main.cpp

RESOURCES  = qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS  = target

HEADERS  = \
    Process.h
//main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "Process.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
   QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    qmlRegisterType<Process>("qt.process.app", 1, 0, "Process");
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
    return app.exec();
}
//Process.h
#ifndef __Process__
#define __Process__
#include <QProcess>
#include <QIODevice>
#include <QObject>
class Process : public QObject
{
    Q_OBJECT
public:
    enum OpenModeFlag {
        NotOpen = QIODevice::NotOpen,
        ReadOnly = QIODevice::ReadOnly,
        WriteOnly = QIODevice::WriteOnly,
        ReadWrite = QIODevice::ReadWrite,
        Append = QIODevice::Append,
        Truncate = QIODevice::Truncate,
        Text = QIODevice::Text,
        Unbuffered = QIODevice::Unbuffered,
        NewOnly = QIODevice::NewOnly,
        ExistingOnly = QIODevice::ExistingOnly
    };
    Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)
signals:
    void readyReadStandardError();
    void readyReadStandardOutput();
public:
    Process(QObject* parent = nullptr);
    virtual ~Process();
    Q_INVOKABLE void start(const QString& program, const QStringList& arguments = QStringList(), OpenMode mode = ReadWrite);
    Q_INVOKABLE QByteArray readAllStandardError();
    Q_INVOKABLE QByteArray readAllStandardOutput();
protected:
    QProcess* m_Process;
    void newProcess();
    void deleteProcess();
};
#endif
//Process.cpp
#include "Process.h"
Process::Process(QObject* parent) :
    QObject(parent),
    m_Process(nullptr)
{
}

Process::~Process()
{
    deleteProcess();
}

void Process::newProcess()
{
    if (m_Process) return;
    m_Process = new QProcess();
    connect(m_Process, &QProcess::readyReadStandardError, this, &Process::readyReadStandardError);
    connect(m_Process, &QProcess::readyReadStandardOutput, this, &Process::readyReadStandardOutput);
}

void Process::deleteProcess()
{
    if (!m_Process) return;
    disconnect(m_Process, &QProcess::readyReadStandardError, this, &Process::readyReadStandardError);
    disconnect(m_Process, &QProcess::readyReadStandardOutput, this, &Process::readyReadStandardOutput);
    delete m_Process;
    m_Process = nullptr;
}

void Process::start(const QString& program, const QStringList& arguments, OpenMode mode)
{
    if (!m_Process) newProcess();
    m_Process->start(program, arguments, static_cast<QIODevice::OpenMode>((static_cast<int>(mode))));
}

QByteArray Process::readAllStandardError()
{
    return m_Process ? m_Process->readAllStandardError() : QByteArray();
}

QByteArray Process::readAllStandardOutput()
{
    return m_Process ? m_Process->readAllStandardOutput() : QByteArray();
}
//main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import qt.process.app 1.0

ApplicationWindow {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Page {
        anchors.fill: parent
        ListView {
            id: listView
            anchors.fill: parent
            clip: true
            model: ListModel {
                id: _console
                function appendMsg(msg, col) {
                    append({msg, col});
                    listView.currentIndex = count - 1;
                }
                function log(...params) {
                    console.log(...params);
                    appendMsg(params.join(" "), "black");
                }
                function error(...params) {
                    console.error(...params);
                    appendMsg(params.join(" "), "red");
                }
            }
            ScrollBar.vertical: ScrollBar {
                width: 20
                policy: ScrollBar.AlwaysOn
            }
            delegate: Frame {
                width: ListView.view.width - 20
                background: Rectangle {
                    color: (index & 1) ? "#eee" : "#ccc"
                }
                Text {
                    width: parent.width
                    text: msg
                    color: col
                }
            }
        }
        footer: Frame {
            Button {
                text: qsTr("Go!")
                onClicked: go()
            }
        }
    }

    Process {
        id: process
        onReadyReadStandardError: {
            let data = readAllStandardError();
            _console.error(data);
        }
        onReadyReadStandardOutput: {
            let data = readAllStandardOutput();
            _console.log(data);
        }
    }

    function go() {
        process.start("/bin/sh", ["-c", "date", " %F  %X"], Process.ReadOnly);
    }
}

qtquickcontrols2.conf:

[Controls]
Style=Material

[Material]
Theme=Light

And qml.qrc:

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>qtquickcontrols2.conf</file>
    </qresource>
</RCC>

CodePudding user response:

What I found on my side is, the issue was debug mode. If I create release and press the run button (Ctlr R) then it is absolutely fine (Not the debug button but Run button on QtCreator). Without any changes to my code. I have no idea what that would make the difference on application though.

  • Related