我是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参数),我需要与控制台进行交互的部分。
答案 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"