C++ 如何在Qt5 for Windows中以编程方式处理交互式CLI
我有以下交互式CLI-C++ 如何在Qt5 for Windows中以编程方式处理交互式CLI,c++,qt,command-line-interface,interactive,qprocess,C++,Qt,Command Line Interface,Interactive,Qprocess,我有以下交互式CLI- c:\TEST> python test.py Running test tool. $help |'exec <testname>' or 'exec !<testnum>' |0 BQ1 |1 BS1 |2 BA1 |3 BP1 $exec !2 |||TEST BA1_ACTIVE $quit c:\TEST>
c:\TEST> python test.py
Running test tool.
$help
|'exec <testname>' or 'exec !<testnum>'
|0 BQ1
|1 BS1
|2 BA1
|3 BP1
$exec !2
|||TEST BA1_ACTIVE
$quit
c:\TEST>
以下只是一个示例代码,test.py应保持原样!我只是想在test.py之外找到一个解决方案
"""---beginning test.py---"""
from cmd import Cmd
class MyPrompt(Cmd):
def do_help(self, args):
if len(args) == 0:
name = " |'exec <testname>' or 'exec !<testnum>'\n |0 BQ1\n |1 BS1\n |2 BA1\n |3 BP1'"
else:
name = args
print ("%s" % name)
def do_exec(self, args):
if (args == "!0"):
print ("|||TEST BQ1_ACTIVE")
elif (args == "!1"):
print ("|||TEST BS1_ACTIVE")
elif (args == "!2"):
print ("|||TEST BA1_ACTIVE")
elif (args == "!3"):
print ("|||TEST BP3_ACTIVE")
else:
print ("invalid input")
def do_quit(self, args):
print ("Quitting.")
raise SystemExit
if __name__ == '__main__':
prompt = MyPrompt()
prompt.prompt = '$ '
prompt.cmdloop('Running test tool.')
"""---end of test.py---"""
“”--“开始测试.py--”
从cmd导入cmd
类MyPrompt(Cmd):
def do_帮助(自我,参数):
如果len(args)==0:
name=“|'exec'或'exec!'\n | 0 BQ1\n | 1 BS1\n | 2 BA1\n | 3 BP1”
其他:
name=args
打印(“%s”%name)
def do_exec(自身,参数):
如果(args==“!0”):
打印(“测试BQ1激活”)
elif(args==“!1”):
打印(“测试BS1激活”)
elif(args==“!2”):
打印(“测试BA1|U激活”)
elif(args==“!3”):
打印(“测试BP3激活”)
其他:
打印(“无效输入”)
def do_退出(自我,参数):
打印(“退出”)
升起系统出口
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
prompt=MyPrompt()
prompt.prompt='$'
prompt.cmdloop('正在运行测试工具')
“---测试结束。py----”
首先避免使用waitForXXX方法,使用Qt的主要优点:信号和插槽
在QProcess
的情况下,必须使用readyReadStandardError
和readyReadStandardOutput
,另一方面,程序不能是“python test.py”
,程序是“python”
,其参数是“test.py”
下面的示例已经在Linux中测试过,但我认为您应该做的更改是设置python可执行文件和.py文件的路径
#include <QCoreApplication>
#include <QProcess>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QProcess process;
process.setProgram("/usr/bin/python");
process.setArguments({"/home/eyllanesc/test.py"});
// commands to execute consecutively.
QList<QByteArray> commands = {"help", "exec !2", "exec !0", "help", "exec !1", "exec !3", "quit"};
QListIterator<QByteArray> itr (commands);
QObject::connect(&process, &QProcess::readyReadStandardError, [&process](){
qDebug()<< process.readAllStandardError();
});
QObject::connect(&process, &QProcess::readyReadStandardOutput, [&process, &itr](){
QString result = process.readAll();
qDebug().noquote()<< "Result:\n" << result;
if(itr.hasNext()){
const QByteArray & command = itr.next();
process.write(command+"\n");
qDebug()<< "command: " << command;
}
else{
// wait for the application to close.
process.waitForFinished(-1);
QCoreApplication::quit();
}
});
process.start();
return a.exec();
}
#包括
#包括
#包括
int main(int argc,char*argv[])
{
qcorea应用程序(argc、argv);
QProcess过程;
process.setProgram(“/usr/bin/python”);
setArguments({/home/eyllansc/test.py});
//连续执行的命令。
QList命令={“help”,“exec!2”,“exec!0”,“help”,“exec!1”,“exec!3”,“quit”};
QListIterator itr(命令);
QObject::connect(&process,&QProcess::readyReadStandardError,[&process](){
qDebug()
所有处理都应该是异步的;没有waitFor
调用
从QProcess
传入的数据可以是任意块。您需要收集所有这些块,并对它们进行分析,以确定何时出现新的输入提示
该过程应以文本模式打开,以便新行翻译为\n
,与平台无关
标准错误转发可由QProcess
处理
Python脚本不应该使用原始输入——它将挂起在Windows上。相反,它应该使用stdin/stdout,并且应该在退出时的处理程序中返回True
,而不是抛出异常
首先,让我们考虑一下对指挥官的过程询问:
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-interactive-50159172
#include <QtWidgets>
#include <algorithm>
#include <initializer_list>
class Commander : public QObject {
Q_OBJECT
QProcess m_process{this};
QByteArrayList m_commands;
QByteArrayList::const_iterator m_cmd = m_commands.cbegin();
QByteArray m_log;
QByteArray m_prompt;
void onStdOut() {
auto const chunk = m_process.readAllStandardOutput();
m_log.append(chunk);
emit hasStdOut(chunk);
if (m_log.endsWith(m_prompt) && m_cmd != m_commands.end()) {
m_process.write(*m_cmd);
m_log.append(*m_cmd);
emit hasStdIn(*m_cmd);
if (m_cmd++ == m_commands.end())
emit commandsDone();
}
}
public:
Commander(QString program, QStringList arguments, QObject * parent = {}) :
QObject(parent) {
connect(&m_process, &QProcess::stateChanged, this, &Commander::stateChanged);
connect(&m_process, &QProcess::readyReadStandardError, this, [this]{
auto const chunk = m_process.readAllStandardError();
m_log.append(chunk);
emit hasStdErr(chunk);
});
connect(&m_process, &QProcess::readyReadStandardOutput, this, &Commander::onStdOut);
connect(&m_process, &QProcess::errorOccurred, this, &Commander::hasError);
m_process.setProgram(std::move(program));
m_process.setArguments(std::move(arguments));
}
void setPrompt(QByteArray prompt) { m_prompt = std::move(prompt); }
void setCommands(std::initializer_list<const char*> commands) {
QByteArrayList l;
l.reserve(int(commands.size()));
for (auto c : commands) l << c;
setCommands(l);
}
void setCommands(QByteArrayList commands) {
Q_ASSERT(isIdle());
m_commands = std::move(commands);
m_cmd = m_commands.begin();
for (auto &cmd : m_commands)
cmd.append('\n');
}
void start() {
Q_ASSERT(isIdle());
m_cmd = m_commands.begin();
m_process.start(QIODevice::ReadWrite | QIODevice::Text);
}
QByteArray log() const { return m_log; }
QProcess::ProcessError error() const { return m_process.error(); }
QProcess::ProcessState state() const { return m_process.state(); }
int exitCode() const { return m_process.exitCode(); }
Q_SIGNAL void stateChanged(QProcess::ProcessState);
bool isIdle() const { return state() == QProcess::NotRunning; }
Q_SIGNAL void hasError(QProcess::ProcessError);
Q_SIGNAL void hasStdIn(const QByteArray &);
Q_SIGNAL void hasStdOut(const QByteArray &);
Q_SIGNAL void hasStdErr(const QByteArray &);
Q_SIGNAL void commandsDone();
~Commander() {
m_process.close(); // kill the process
}
};
我们还需要一个将文本添加到日志视图的函数:
void addText(QPlainTextEdit *view, const QString &text, const QTextCharFormat &modifier, bool newBlock) {
view->mergeCurrentCharFormat(modifier);
if (newBlock)
view->appendPlainText(text);
else
view->textCursor().insertText(text);
}
最后,演示工具:
int main(int argc, char *argv[]) {
QApplication app{argc, argv};
Commander cmdr{"python", {"test.py"}};
cmdr.setPrompt("$ ");
cmdr.setCommands({"help", "exec !2", "exec !0", "help", "exec !1", "exec !3", "quit"});
QWidget w;
QVBoxLayout layout{&w};
QPlainTextEdit logView;
QPushButton start{"Start"};
Logger log{logView.document()};
layout.addWidget(&logView);
layout.addWidget(&start);
logView.setMaximumBlockCount(1000);
logView.setReadOnly(true);
logView.setCurrentCharFormat(QTextCharFormat() << SystemFixedPitchFont);
log.setLogFormat(QTextCharFormat() << Qt::darkGreen);
QObject::connect(&log, &Logger::addText, &logView, [&logView](auto &text, auto &mod, auto block){
addText(&logView, text, mod, block);
});
QObject::connect(&cmdr, &Commander::hasStdOut, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::black); });
QObject::connect(&cmdr, &Commander::hasStdErr, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::red); });
QObject::connect(&cmdr, &Commander::hasStdIn, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::blue); });
QObject::connect(&cmdr, &Commander::stateChanged, &start, [&start](auto state){
qDebug() << state;
start.setEnabled(state == QProcess::NotRunning);
});
QObject::connect(&start, &QPushButton::clicked, &cmdr, &Commander::start);
w.show();
return app.exec();
}
#include "main.moc"
intmain(intargc,char*argv[]){
QApplication app{argc,argv};
指挥官cmdr{“python”{“test.py”};
cmdr.setPrompt($);
cmdr.setCommands({“help”、“exec!2”、“exec!0”、“help”、“exec!1”、“exec!3”、“quit”});
qw;
QVBoxLayout布局{&w};
QPlainTextEdit日志视图;
QPushButton开始{“开始”};
日志记录器日志{logView.document()};
layout.addWidget(&logView);
layout.addWidget(&start);
logView.setMaximumBlockCount(1000);
logView.setReadOnly(true);
logView.setCurrentCharFormat(QTextCharFormat())代码本身工作得很完美。但是我还有两个问题-1)程序失败,将源代码移动到类的函数,然后从main()调用该函数。它很容易复制,并且有办法修复它吗?2)如果每个命令都在等待某些内容,例如,“exec!1”向设备发送一个“命令”,并等待设备的响应。看起来程序将“挂起”或者UI应用程序的屏幕将被冻结,有没有办法从Qt解决这个问题?如果超时发生或进程完成,我们如何捕获第二个问题中的数据?@cnm您要嵌入它的类是一个小部件或QObject?@cnm 1)当然,因为函数返回时所有对象都已消失;您必须e将其转换为类。2)完全不-代码是从事件循环中驱动的,UI将始终负责,但请删除waitFor
!@cnm您不能询问有关虚构代码的问题。“我尝试了一个类…”-当然,你做了,你做错了什么。你希望我们做什么?预测你的错误?如果你有一个新问题,记住具体的代码-请将其作为一个新问题发布。否则,如果你想用一些能够处理USB的自含代码更新你的问题-请这样做。否则你会浪费每个人的时间-y我们的和我们的。该代码导致内存泄漏!我不确定它是否在Qt或Windows中。在单击“开始”按钮五次后,私有字节从8332K增加到9368K,工作集从23460K增加到25128K。如果您已经发现了泄漏,那么您知道如何修复它-请随时向github存储库提交拉取请求。唉,查看一些数字并不能识别泄漏。您引用的数字没有多大意义。可能某个地方有错误。可能没有。唯一可以确保的方法是使用实际的泄漏检测工具。而您没有这样做。请思考每次单击“开始”时代码会做什么:它需要使用更多的内存;日志增长!但是你不知道增长是否完全由日志增长来解释。因此,你不能声称泄漏-还没有。你所说的完全正确。我曾经使用purify,这是一个非常好的工具,不幸的是,我没有了它。我确实看到了私有字节和工作集当我发出N次“咔嗒”的信号时,它会越来越大。我想你们可能会
template <typename T> void forEachLine(const QByteArray &chunk, T &&fun) {
auto start = chunk.begin();
while (start != chunk.end()) {
auto end = std::find(start, chunk.end(), '\n');
auto lineEnds = end != chunk.end();
fun(lineEnds, QByteArray::fromRawData(&*start, end-start));
start = end;
if (lineEnds) start++;
}
}
class Logger : public QObject {
Q_OBJECT
QtMessageHandler previous = {};
QTextCharFormat logFormat;
bool lineStart = true;
static QPointer<Logger> &instance() { static QPointer<Logger> ptr; return ptr; }
public:
explicit Logger(QObject *parent = {}) : QObject(parent) {
Q_ASSERT(!instance());
instance() = this;
previous = qInstallMessageHandler(Logger::logMsg);
}
void operator()(const QByteArray &chunk, const QTextCharFormat &modifier = {}) {
forEachLine(chunk, [this, &modifier](bool ends, const QByteArray &chunk){
auto text = QString::fromLocal8Bit(chunk);
addText(text, modifier, lineStart);
lineStart = ends;
});
}
static void logMsg(QtMsgType, const QMessageLogContext &, const QString &msg) {
(*instance())(msg.toLocal8Bit().append('\n'), instance()->logFormat);
}
Q_SIGNAL void addText(const QString &text, const QTextCharFormat &modifier, bool newBlock);
void setLogFormat(const QTextCharFormat &format) { logFormat = format; }
~Logger() override { if (previous) qInstallMessageHandler(previous); }
};
static struct SystemFixedPitchFont_t {} constexpr SystemFixedPitchFont;
QTextCharFormat operator<<(QTextCharFormat format, const QBrush &brush) {
return format.setForeground(brush), format;
}
QTextCharFormat operator<<(QTextCharFormat format, SystemFixedPitchFont_t) {
return format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), format;
}
void addText(QPlainTextEdit *view, const QString &text, const QTextCharFormat &modifier, bool newBlock) {
view->mergeCurrentCharFormat(modifier);
if (newBlock)
view->appendPlainText(text);
else
view->textCursor().insertText(text);
}
int main(int argc, char *argv[]) {
QApplication app{argc, argv};
Commander cmdr{"python", {"test.py"}};
cmdr.setPrompt("$ ");
cmdr.setCommands({"help", "exec !2", "exec !0", "help", "exec !1", "exec !3", "quit"});
QWidget w;
QVBoxLayout layout{&w};
QPlainTextEdit logView;
QPushButton start{"Start"};
Logger log{logView.document()};
layout.addWidget(&logView);
layout.addWidget(&start);
logView.setMaximumBlockCount(1000);
logView.setReadOnly(true);
logView.setCurrentCharFormat(QTextCharFormat() << SystemFixedPitchFont);
log.setLogFormat(QTextCharFormat() << Qt::darkGreen);
QObject::connect(&log, &Logger::addText, &logView, [&logView](auto &text, auto &mod, auto block){
addText(&logView, text, mod, block);
});
QObject::connect(&cmdr, &Commander::hasStdOut, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::black); });
QObject::connect(&cmdr, &Commander::hasStdErr, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::red); });
QObject::connect(&cmdr, &Commander::hasStdIn, &log, [&log](auto &chunk){ log(chunk, QTextCharFormat() << Qt::blue); });
QObject::connect(&cmdr, &Commander::stateChanged, &start, [&start](auto state){
qDebug() << state;
start.setEnabled(state == QProcess::NotRunning);
});
QObject::connect(&start, &QPushButton::clicked, &cmdr, &Commander::start);
w.show();
return app.exec();
}
#include "main.moc"
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# test.py
from __future__ import print_function
from cmd import Cmd
import time, sys
class MyPrompt(Cmd):
def do_help(self, args):
if len(args) == 0:
name = " |'exec <testname>' or 'exec !<testnum>'\n |0 BQ1\n |1 BS1\n |2 BA1\n |3 BP1"
else:
name = args
print ("%s" % name)
def do_exec(self, args):
if (args == "!0"):
print (" |||TEST BQ1_ACTIVE")
elif (args == "!1"):
print (" |||TEST BS1_ACTIVE")
elif (args == "!2"):
print (" |||TEST BA1_ACTIVE")
elif (args == "!3"):
print (" |||TEST BP3_ACTIVE")
else:
print ("invalid input")
time.sleep(1)
def do_quit(self, args):
print ("Quitting.", file=sys.stderr)
return True
if __name__ == '__main__':
prompt = MyPrompt()
prompt.use_rawinput = False
prompt.prompt = '$ '
prompt.cmdloop('Running test tool.')