迁移通知

本站内容正在逐步向 https://www.weiran.ink 迁移,更新内容请到新站查找。


通过Qt优雅地调用命令行计算程序并抓取输出显示在窗口上

编写大型工程计算软件时,计算核心与图形操作界面混在一起不是一个好的实践。一则不容易实现跨平台,二则难以在非图形环境下部署和使用,三则这样写容易让二者的代码混在一起提高维护难度,四则图形库往往引入不必要的性能开销和难以预知的输入。所以,分别单独实现计算核心和图形操作界面,然后通过命令行调用、消息输出是一个很合适的方案。

这些天基于Qt搞了一个这样一套方案,同时以此为契机搞清楚了Qt信号(signal)/槽(slot)机制,这里做一个总结。

  • 使用特定的命令行参数启动计算核心
  • 实时抓取计算核心的输出,显示在图形界面上
  • 计算过程中要保持GUI的正常响应

比较容易,Qt已经提供了很好的封装:

computeProcess.setProgram(launchOption.executable);
computeProcess.setArguments(launchOption.arguments);
computeProcess.setWorkingDirectory(launchOption.runDirectory);
 
computeProcess.start();
 
while (!computeProcess.waitForFinished()) {
    QThread::sleep(1);
}
 
emit computingFinished(computeProcess.exitStatus() == QProcess::NormalExit);

launchOption就是个结构体,存放了一些字符串。start是实际启动外部程序。computeProcess.waitForFinished()看名字是等待到程序结束,但实际上默认最长只等三十秒,然后就返回一个false回来。理论上可以传一个参数让他永久等待,不过想想还是用一个循环多次调用它比较好,需要的话,循环里面可以做一些别的事情,比如计数什么的。

最后一句emit ...是发送一个信号,信号中包含了一个数据:程序是否正常退出。

可以对Process对象执行相关的抓取函数来获得。但是单开一个线程反复尝试抓取(我在Java里面就是这样干的!)相当的不优雅。利用Qt的消息机制,可以将他们封装为信号:

// 在一个继承了QObject的类中
 
// 已经声明了:
// public slots:
//    void doCompute();
// 以及
// signals:
//    void newMessageIncoming(QString newMessage, bool isError);
//    void computingFinished(bool isExitedNormally);
 
connect(&computeProcess, &QProcess::readyReadStandardOutput, [this]() {
    emit newMessageIncoming(computeProcess.readAllStandardOutput(), false);
});
connect(&computeProcess, &QProcess::readyReadStandardError, [this]() {
    emit newMessageIncoming(computeProcess.readAllStandardError(), true);
});

等于是,把Process中Qt原生提供的的“有标准输出/错误输出”信号,连接到了两个lambda表达式史上,这两个表达式所执行的内容则是读出所有的新来的信息,并且将他们封装到一个自定义的信号newMessageIncoming中,发射出去。

对于信号的槽的几点认识:

  • 最后更改: 2025/05/10 05:59