实时获取QProcess的响应

时间:2019-01-25 15:44:24

标签: c++ qt c++11 qt5

我是C ++编程的新手,所以我需要逻辑方面的帮助(代码会很棒)。我想让QTextEdit像小部件一样充当终端。 我正在尝试使用QProcess来实现这一点,

void QPConsole::command(QString cmd){
    QProcess* process = new QProcess();
    connect(process, &QProcess::readyReadStandardOutput, [=](){ print(process->readAllStandardOutput()); });
    connect(process, &QProcess::readyReadStandardError, [=](){ print(process->readAllStandardError()); });
    connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[=](int exitCode, QProcess::ExitStatus exitStatus){ prepareCommandLine(); return; });

    process->setProgram("/bin/bash");
    process->start();
    process->write(cmd.toStdString().c_str());
//    process->write("\n\r");

    process->closeWriteChannel();
}

下一个问题是: 如果我不关闭写通道(process-> closeWriteChannel),那我就会陷入无限循环。如果我确实关闭它,那我就不记得状态了(kinda进行了新的会话),例如,如果我执行“ pwd”,我会得到结果,但是如果我执行下一个“ cd ..”然后是“ pwd”,结果将与“ pwd”的第一个输出

所以,我的问题是,是否有可能实现某种实时输出+使用QProcess记住以前的状态(例如在实际终端中),或者我必须以其他方式执行。在python中,我是通过管道的子进程调用来实现的,但是我不知道什么是c ++的均衡器。

一个代码示例会很棒。我有QTextEdit部分(正在向函数发送QString参数),我需要与控制台进行交互的部分。

1 个答案:

答案 0 :(得分:1)

这个想法很简单:保留一个QProcess的实例并向其写入命令,然后跟随\n

这是一个完整的示例,带有一个QMainWindow和一个Bash类,将其放在main.cpp中:

#include <QtCore>
#include <QtGui>
#include <QtWidgets>

class Bash : public QObject
{
    Q_OBJECT
public:
    explicit Bash(QObject *parent = nullptr) : QObject(parent)
    {

    }

    ~Bash()
    {
        closePrivate();
    }

signals:
    void readyRead(QString);

public slots:
    bool open(int timeout = -1)
    {
        if (m_bash_process)
            return false;

        m_timeout = timeout;

        return openPrivate();
    }

    void close()
    {
        closePrivate();
    }

    bool write(const QString &cmd)
    {
        return writePrivate(cmd);
    }

    void SIGINT()
    {
        SIGINTPrivate();
    }

private:
    //Functions
    bool openPrivate()
    {
        if (m_bash_process)
            return false;

        m_bash_process = new QProcess();

        m_bash_process->setProgram("/bin/bash");

        //Merge stdout and stderr in stdout channel
        m_bash_process->setProcessChannelMode(QProcess::MergedChannels);

        connect(m_bash_process, &QProcess::readyRead, this, &Bash::readyReadPrivate);
        connect(m_bash_process, QOverload<int>::of(&QProcess::finished), this, &Bash::closePrivate);

        m_bash_process->start();

        bool started = m_bash_process->waitForStarted(m_timeout);

        if (!started)
            m_bash_process->deleteLater();

        return started;
    }

    void closePrivate()
    {
        if (!m_bash_process)
            return;

        m_bash_process->closeWriteChannel();
        m_bash_process->waitForFinished(m_timeout);
        m_bash_process->terminate();
        m_bash_process->deleteLater();
    }

    void readyReadPrivate()
    {
        if (!m_bash_process)
            return;

        while (m_bash_process->bytesAvailable() > 0)
        {
            QString str = QLatin1String(m_bash_process->readAll());
            emit readyRead(str);
        }
    }

    bool writePrivate(const QString &cmd)
    {
        if (!m_bash_process)
            return false;

        if (runningPrivate())
            return false;

        m_bash_process->write(cmd.toLatin1());
        m_bash_process->write("\n");

        return m_bash_process->waitForBytesWritten(m_timeout);
    }

    void SIGINTPrivate()
    {
        if (!m_bash_process)
            return;

        QProcess::startDetached("pkill", QStringList() << "-SIGINT" << "-P" << QString::number(m_bash_process->processId()));
    }

    bool runningPrivate()
    {
        if (!m_bash_process)
            return false;

        QProcess process;
        process.setProgram("pgrep");
        process.setArguments(QStringList() << "-P" << QString::number(m_bash_process->processId()));
        process.setProcessChannelMode(QProcess::MergedChannels);

        process.start();

        process.waitForFinished(m_timeout);

        QString pids = QLatin1String(process.readAll());

        QStringList pid_list = pids.split(QRegularExpression("\n"), QString::SkipEmptyParts);

        return (pid_list.size() > 0);
    }

    //Variables
    QPointer<QProcess> m_bash_process;
    int m_timeout;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        connect(&m_bash, &Bash::readyRead, this, &MainWindow::readyRead);

        QWidget *central_widget = new QWidget(this);

        QVBoxLayout *layout = new QVBoxLayout(central_widget);
        layout->addWidget(&m_message_board);
        layout->addWidget(&m_cmd_edit);
        layout->addWidget(&m_sigint_btn);

        m_message_board.setReadOnly(true);

        m_cmd_edit.setEnabled(false);

        m_sigint_btn.setText("Send SIGINT(CTRL+C)");

        connect(&m_cmd_edit, &QLineEdit::returnPressed, this, &MainWindow::writeToBash);

        connect(&m_sigint_btn, &QPushButton::clicked, this, &MainWindow::SIGINT);

        setCentralWidget(central_widget);

        resize(640, 480);

        QTimer::singleShot(0, this, &MainWindow::startBash);
    }

    ~MainWindow()
    {
        m_bash.close(); //Not mandatory, called by destructor
    }

private slots:
    void startBash()
    {
        //Open and give to each operation a maximum of 1 second to complete, use -1 to unlimited
        if (m_bash.open(1000))
        {
            m_cmd_edit.setEnabled(true);
            m_cmd_edit.setFocus();
        }
        else
        {
            QMessageBox::critical(this, "Error", "Failed to open bash");
        }
    }

    void writeToBash()
    {
        QString cmd = m_cmd_edit.text();

        m_cmd_edit.clear();

        if (!m_bash.write(cmd))
        {
            QMessageBox::critical(this, "Error", "Failed to write to bash");
        }
    }

    void readyRead(const QString &str)
    {
        m_message_board.appendPlainText(str);
    }

    void SIGINT()
    {
        m_bash.SIGINT();
    }

private:
    Bash m_bash;
    QPlainTextEdit m_message_board;
    QLineEdit m_cmd_edit;
    QPushButton m_sigint_btn;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

#include "main.moc"