【问题标题】:Qt application hangs on process.start() function. Happens only when function is being called from QML scriptQt 应用程序挂起 process.start() 函数。仅在从 QML 脚本调用函数时发生
【发布时间】:2022-11-11 06:46:57
【问题描述】:

我的 HMI 屏幕上运行着嵌入式 Qt 应用程序。 我正在尝试执行一些命令以在 cmd 中执行。 我只是从 QML 调用这个 c++ 函数。 每次我调用它时,它都会挂在 process.start() 上。 有人对此类问题有任何经验吗?请帮忙。 我已经创建了一个简单的函数来打印日期,无论我执行什么 cmd,它仍然挂在 process.start() 上。

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

我在 Ubuntu 18.04.6LTS 平台上使用 Qt 5.9。

【问题讨论】:

  • 题外话了,但是...为什么要通过 shell sh 而不是简单的 process.start("date", QStringList{} &lt;&lt; "+%F %X")
  • 谢谢@GM我刚刚试过你说的,但它也是一样的。无论我执行什么命令,它每次都挂在 process.start() 上。
  • 哪个操作系统?哪个版本的 Qt?如果您提供完整的绝对路径怎么办?既然您打电话给sh,您是否还有可能有帮助的日志文件?
  • edit您的问题提供minimal reproducible example。您可能还想在调试器下运行您的代码,以找出导致代码(显然)挂起的确切原因。
  • 爱德华,我没有日志文件。不管我叫 sh 与否,这都会发生。所以不要专注于我会说的 sh。例如,如果我像从 cmd 一样执行“ls”。它仍然发生。前任。 process.start("ls");也会挂起。而且我也无法调试它。我的断点根本没有触发。

标签: c++ qt qml qprocess process.start


【解决方案1】:

我做了一些进一步的故障排除,我想我在你的启动命令中发现了一个错误。它应该是:

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

你的错误是你的命令 date 加入了它的参数,而它应该是分开的。

由于您对 QML 感兴趣,因此我模拟了以下 C++ 应用程序,并在其中调用了类似的命令:

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

我还让程序监听并等待onReadyReadStandardOutput 和/或onReadyReadStandardError,然后分别调用process.readAllStandardOutput() 和/或process.readAllStandardError()

为方便起见,我还映射了 OpenMode 标志,以便它们可以在 QML 中使用。

当你运行程序时,会有一个“Go!”页脚中的按钮。当您单击它时,它会运行process.start(),并且输出,无论是常规输出还是错误都会显示在上面的ListView 中。当我运行它时,我看到这样的输出:

#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

和 qml.qrc:

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

【讨论】:

  • 好吧,您所做的确实是一项很棒的调查,我很感谢您抽出宝贵的时间。我发现我的问题是调试模式。如果我创建发布并按下运行按钮(Ctlr + R),那绝对没问题。无需对我的代码进行任何更改。顺便说一句,我认为 date 命令不是问题,因为它对我使用的任何命令都是这样做的。但现在如果我在发布模式下执行 Ctrl + R 就好了。
【解决方案2】:

我发现,问题是调试模式。如果我创建发布并按下运行按钮(Ctlr + R),那么它绝对没问题(不是调试按钮,而是 QtCreator 上的运行按钮)。无需对我的代码进行任何更改。我不知道这会对应用程序产生什么影响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-13
    • 1970-01-01
    相关资源
    最近更新 更多