QT中调用外部程序的方法

来源:互联网 发布:自学sas能应聘数据分析 编辑:程序博客网 时间:2024/06/04 17:49

上个月有人写信问我这个问题,当时做了比较详细的回答,把回答内容放上来,也许对其它 人会有些用处的吧。有些地方我的理解可能也不正确,欢迎指正:

问题:

    我做的那个小软件的图形界面是基于QT3.2的,在主界面的命令行编辑框输入命令以后要执行别人已经写好的可执行文件。这些可执行文件执行的时间比较长,在终端上运行时会显示一些运行的信息,最后才显示执行结果。我的界面上有一个文本框,我想把它们在后台执行过程中的信息不断添加到文本框中,相当于实时显示吧,不过要求也不是那么高。

    我说我现在怎么做的吧,我在一个叫做QGUI_CommandWidget类(属于主窗口)中定义了一个命令行编辑框输入命令,定义一个QTextEdit对象用来显示那些执行信息,定义了一个自定义的MyThread类对象用来执行外部程序,在这个线程的run函数里我调用fork,execv函数执行外部程序,把可执行程序的标准输出重定向到管道,然后从管道读那些信息,再把这些信息用QApplication::postEvent()函数传回主线程,由主线程把这些信息append到文本框中。

    我现在的疑问是:
        第一,执行外部程序,用fork、execv函数是不是不行,非要用QProcess不可?为什么呢?论坛上讲的也不是很清楚。具体怎么做呢?在那个QGUI_CommandWidget类创建一个QProcess类对象还是在我MyThread类对象里再创建一个QProcess类对象?
        第二,基于qt3的GUI线程和非GUI线程的通信,应该怎么做?那个外部程序我是不能更改的,它什么时候结束我也不知道。用QProcess的话它的输出信息我要怎么样才能读到然后回显在我的主窗口的文本框中?怎么知道可执行程序结束然后杀死该线程?
        第三,我在《C
++ GUI programming with qt3》中看到:“ QTimer 类以及应用于网络的QFtp, QHttp, QSocket, and QSocketNotifier 类都是基于消息事件循环的,所以也不能用在非GUI线程中。”这是为什么呢?还有我看了别人用QQSocketdevice的例子里都用到了QSocketNotifier。在你的博客说到用Qthread、QQSocketdevice、QWaitCondition可以完成视频采集,你是否也用到QSocketNotifier?


回答:

1)完全可以使用fork,execv函数,其实QProcess类只是对这些底层函数的封装而
已,但是考虑到使用QProcess的话,不需要自己处理程序管道,也不需要自己处理
windows下的情况,可以省去很多时间,因此还是推荐使用QProcess,他们的效果
将是一样的。

2)照你的需求,完全可以不需要使用线程,因为QProcess已经自己处理掉这些事
了,在使用QProcess的start函数执行外部程序后,这个函数不会被阻塞,另外
QProcess也会以事件的方式自动将外部程序传回的信息反馈回来。具体看以下这个
简单的例子:

class enstCdRecord : public QObject
{
  。。。。。。。。。。。。
  QProcess mProcess;
}
;

enstCdRecord::enstCdRecord(QWidget 
*pParent)
{
  mParent 
= pParent;
  connect(
&mProcess, SIGNAL(readyReadStandardError()), this,
  SLOT(ReadProcessOutput())); 
//连接readyReadStandardError事件,这样就可以
    得到程序StdErr中的信息了,同样也可以连接其 readyReadOutput事件。
}


bool enstCdRecord::CreateCd(const QString &pImageFile)
{
  QStringList cmdlist;
  cmdlist.append(
"-v");
  cmdlist.append(
"speed=2");
  cmdlist.append(pImageFile);

  mProcess.start(
"cdrecord.exe", cmdlist);
  
while (! mProcess.waitForFinished(300)) //启动程序后,用循环等待其结
    束,如果对程序何时结束并不关心,以下代码可以不需要。
  
if (mProcess.state() == QProcess::NotRunning) //process failed
    QMessageBox::critical(mParent, SYSTEMNAME, tr("Error when record cd."));
    
return false;
  }

  qApp
->processEvents(); //防止UI死锁,一般情况下,用这种等一小段时间(这
    里是300ms),让UI响应一次的办法,已经足够使用了。
  }

  
if (mProcess.exitCode() != 0//error when run process
    QMessageBox::critical(mParent, SYSTEMNAME, tr("Error when record cd."));
    
return false;
  }

  
return true;
}


void enstCdRecord::ReadProcessOutput()
{
  QMessageBox::critical(mParent, SYSTEMNAME,
  mProcess.readAllStandardError()); 
//将程序的StdErr信息显示出来。
}


外部程序究竟使用StdOut还是使用StdErr来作为运行时状态的输出,各个程序的处
理方式都不一样,甚至可能根本没有输出,这个需要自己试验。

3)一般网络程序中只要在主线程中使用QSocket和QSocketNotifier,就可以完成
数据的发送和接收了,它们不会造成程序界面死锁。在我写的blog的情况下,我的
程序在从网络上接收到数据后,需要做一些计算处理和保存工作,由于数据量很
大,并且程序对性能的要求比较高,因此只能将整个网络数据接收功能放到线程中
了,在那种情况下,QSocket并不适用,而必须使用同步的QSocketDevice。

其实,QT3下提供两种网络访问功能,一类是QFtp, QHttp, QSocket, and
QSocketNotifier等,它们都会在接收到数据后,以事件方式进行通知。另一类是
QSocketDevice,它不会主动发出任何通知事件,而是必须靠外部程序来查询,或
者等待其接收到数据,才能知道何时有数据被收到。

《C
++ GUI programming with qt3》中的那句话没有错,(但是它是针对QT3说的,
在QT4中根本就没有这几个类了,并且QT4中Thread也可以用事件了)。在QT3中,
只有 UI线程在可以使用事件,在其它辅助线程中是无法使用的,因此不能使用靠
事件进行通知的那几个类了。

我的程序中没有使用QSocketNotifier,正如前面说的,这个类只是配合QSocket使
用的,它是考事件进行通知的。我的程序使用的是 QSocketDevice,并且靠其Wait
功能,来等待数据到达。