Qt的QProcess类用来启动一个外部程序并与其进行通信。
启动进程的方法:
1.首先创建Qt Gui工程。
工程名称为“myProcess”,其他选项保持默认即可。
2.然后设计界面,拖Push Button部件。
在设计模式往界面上拖入一个Push Button部件,修改其显示文本为“启动一个进程”。
3.修改函数。
在按钮上点击鼠标右键,转到其clicked()信号对应的槽(函数),更改如下:
void MainWindow::on_pushButton_clicked()
{
myProcess.start(“notepad.exe”);//QProcess类的实例myProcess调用的start函数。
}
4.进入mainwindow.h文件添加代码。
先添加头文件包含:#include
5.运行程序。
当单击界面上的按钮时就会弹出一个记事本程序。
这里我们使用QProcess对象运行了Windows系统下的记事本程序(即notepad.exe程序),因为该程序在系统目录中,所以这里不需要指定其路径。大家也可以运行其他任何的程序,只需要指定其具体路径即可。我们看到,可以使用start()来启动一个程序,有时启动一个程序时需要指定启动参数,这种情况在命令行启动程序时是很常见的,下面来看一个例子,还在前面的例子的基础上进行更改。
1.在mainwindow.h文件中添加代码。
添加私有槽:
private slots:
void showResult();
2.在mainwindow.cpp文件中添加代码。
(1)先添加头文件包含:#include
connect(&myProcess, SIGNAL(readyRead()), this, SLOT(showResult()));
(2)然后添加showResult()槽的定义:
void MainWindow::showResult()
{
qDebug() << “showResult: ” << endl
<< QString(myProcess.readAll());
}
(3)最后将前面按钮的单击信号对应的槽更改为:
void MainWindow::on_pushButton_clicked()
{
QString program = “cmd.exe”;
QStringList arguments;
arguments << “/c dir&pause”;
myProcess.start(program, arguments);
}
这里在启动Windows下的命令行提示符程序cmd.exe时为其提供了命令作为参数,这样可以直接执行该命令。当命令执行完以后可以执行showResult()槽来显示运行的结果。这里为了可以显示结果中的中文字符,使用了QString()进行编码转换。这需要在mian()函数中添加代码。
3. 为了确保可以显示输出的中文字符,在main.cpp文件中添加代码。
先添加头文件包含#include
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
4.运行程序。
按下界面上的按钮,会在Qt Creator中的应用程序输出栏中输出命令的执行结果。
对于Qt中进程进一步的使用可以参考QProcess类的帮助文档。在Qt中还提供了多种进程间通信的方法,大家可以在Qt帮助中查看Inter-Process Communication in Qt关键字对应的文档。
二、线程
Qt提供了对线程的支持,这包括一组与平台无关的线程类,一个线程安全的发送事件的方式,以及跨线程的信号-槽的关联。这些使得可以很容易的开发可移植的多线程Qt应用程序,可以充分利用多处理器的机器。多线程编程也可以有效的解决在不冻结一个应用程序的用户界面的情况下执行一个耗时的操作的问题。关于线程的内容,大家可以在Qt帮助中参考Thread Support in Qt关键字。
(一)启动一个线程
一个QThread代表了一个在应用程序中可以独立控制的线程,它与进程中的其他线程分享数据,但是是独立执行的。相对于一般的程序都是从main()函数开始执行,QThread从run()函数开始执行。默认的,run()通过调用exec()来开启事件循环。要创建一个线程,需要子类化QThread并且重新实现run()函数。
每一个线程可以有自己的事件循环,可以通过调用exec()函数来启动事件循环,可以通过调用exit()或者quit()来停止事件循环。在一个线程中拥有一个事件循环,可以使它能够关联其他线程中的信号到本线程的槽上,这使用了队列关联机制,就是在使用connect()函数进行信号和槽的关联时,将Qt::ConnectionType类型的参数指定为Qt::QueuedConnection。拥有事件循环还可以使该线程能过使用需要事件循环的类,比如QTimer和QTcpSocket类等。注意,在线程中是无法使用任何的部件类的。
下面来看一个在图形界面程序中启动一个线程的例子,在界面上有两个按钮,一个用于开启一个线程,一个用于关闭该线程。
1.创建项目。
新建Qt Gui应用,名称为“myThread”,类名为“Dialog”,基类选择QDialog。
2.设计界面。
完成项目创建后进入设计模式,向界面中放入两个Push Button按钮,将第一个按钮的显示文本更改为“启动线程”,将其objectName属性更改为startButton;将第二个按钮的显示文本更改为“终止线程”,将其objectName属性更改为stopButton,将其enabled属性取消选中。
3.添加自定义线程类。
向项目中添加新的C++类,类名设置为“MyThread”,基类设置为“QThread”,类型信息选择“继承自QObject”。完成后进入mythread.h文件,先添加一个公有函数声明:
void stop();
然后再添加一个函数声明和一个变量的定义:
protected:
void run();
private:
volatile bool stopped;
这里stopped变量使用了volatile关键字,这样可以使它在任何时候都保持最新的值,从而可以避免在多个线程中访问它时出错。然后进入mythread.cpp文件中,先添加头文件#include
stopped = false;
这里将stopped变量初始化为false。下面添加run()函数的定义:
void MyThread::run()
{
qreal i = 0;
while (!stopped)
qDebug() << QString(“in MyThread: %1″).arg(i++);
stopped = false;
}
这里一直判断stopped变量的值,只要它为false,那么就一直打印字符串。下面添加stop()函数的定义:
void MyThread::stop()
{
stopped = true;
}
在stop()函数中将stopped变量设置为了true,这样便可以结束run()函数中的循环,从而从run()函数中退出,这样整个线程也就结束了。这里使用了stopped变量来实现了进程的终止,并没有使用危险的terminate()函数。
4.在Dialog类中使用自定义的线程。
先到dialog.h文件中,添加头文件包含:
#include “mythread.h”
然后添加私有对象的定义:
MyThread thread;
在启动线程时调用了start()函数,然后设置了两个按钮的状态。在终止线程时,先使用isRunning()来判断线程是否在运行,如果是,则调用stop()函数来终止线程,并且更改两个按钮的状态。现在运行程序,按下“启动线程”按钮,查看应用程序输出栏的输出,然后再按下“终止线程”按钮,可以看到已经停止输出了。
(二)线程同步
Qt中的QMutex、QReadWriteLock、QSemaphore和QWaitCondition类提供了同步线程的方法。虽然使用线程的思想是多个线程可以尽可能的并发执行,但是总有一些时刻,一些线程必须停止来等待其他线程。例如,如果两个线程尝试同时访问相同的全局变量,结果通常是不确定的。QMutex提供了一个互斥锁(mutex);QReadWriteLock即读-写锁;QSemaphore即信号量;QWaitCondition即条件变量。
最后要注意的是,使用线程是很容易出现问题的,比如无法在主线程以外的线程中使用GUI类的问题(可以简单的通过这样的方式来解决:将一些非常耗时的操作放在一个单独的工作线程中来进行,等该工作线程完成后将结果返回给主线程,最后由主线程将结果显示到屏幕上)。大家应该谨慎的使用线程。
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。