应用场景与问题描述:
问题是这样的,我写了很多命令行程序用于处理遥感影像,这种方式很方便,可以通过dos或shell脚本来实现批处理。但这也引起一个问题,当我在集成时偷懒,不想做界面,而用户又必须要求有个界面时,我不得不做一个窗口来体现我是有界面的,于是我决定直接调用我的可执行程序或者批处理脚本,但这些命令和脚本不能在终端或dos窗口里执行。这个问题简单的抽象为执行一个"ping localhost"的命令,并且把输出重定向到我的消息窗口里。
我们习惯于先看看别人有没有做过这个事,于是先百度一下,就直接看到这篇博客:点击打开链接,参照这个博客的例子,我做了如下的实验:
头文件:
#ifndef QTWINMSG_H
#define QTWINMSG_H
#include <QtWidgets/QMainWindow>
#include "ui_qtwinmsg.h"
#include "Worker.h"
class QtWinMsg : public QMainWindow
{
Q_OBJECT
public:
QtWinMsg(QWidget *parent = 0);
~QtWinMsg();
public slots:
void onTest();
private:
Ui::QtWinMsgClass ui;
};
#endif // QTWINMSG_H
源文件:
#include "qtwinmsg.h"
#include <QMessageBox>
#include <conio.h>
#include <stdio.h>
#include <windows.h>
QtWinMsg::QtWinMsg(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.lineEdit->setText(tr("ping localhost"));
ui.textEdit->setLineWrapMode(QTextEdit::NoWrap);
connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(onTest()));
}
QtWinMsg::~QtWinMsg()
{
}
void QtWinMsg::onTest()
{
QString qsCmd = ui.lineEdit->text();
QByteArray qbCmd = qsCmd.toLocal8Bit();
char* pszCmd = qbCmd.data();
LPWSTR ppCmd = new TCHAR[100];
LPSTR p = pszCmd;
MultiByteToWideChar(CP_ACP, 0, p, -1, ppCmd, 100);
SECURITY_ATTRIBUTES sa;
HANDLE hRead,hWrite;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hRead,&hWrite,&sa,0))
{
return ;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.hStdError = hWrite;
si.hStdOutput = hWrite;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
//关键步骤,CreateProcess函数参数意义请查阅MSDN
if (!CreateProcess(NULL, ppCmd
,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi))
{
return ;
}
CloseHandle(hWrite);
char buffer[4096] = {0};
DWORD bytesRead;
//ofstream outfile("log.txt");
while (true)
{
if (ReadFile(hRead,buffer,4095,&bytesRead,NULL) == NULL)
break;
//buffer中就是执行的结果,可以保存到文本,也可以直接输出
//printf(buffer);
//outfile << buffer << endl;
//Sleep(1000);
QString qsMsg = QString::fromLocal8Bit(buffer);
ui.textEdit->append(qsMsg);
this->update();
}
//outfile.close();
}
运行效果:
这样可以实现目标,但是在Qt程序里使用了Win32 API,这是有点别扭,另外输出的消息并不是动态的输出,而是在执行完毕后一下子刷出来,即使我们通过QThread,把处理过程放到其它线程里,然后通过异步的事件来刷新消息输出也是实现不了动态消息效果。总体来说,这种方式是不完美的。
我想Qt应该有自己的方式。通过查看Qt的帮助,很快就可以找到做这个事情的类QProcess,QProcess可以同于执行外部程序和命令,并且支持消息重定向和标准输入、输出,有了QProcess类,前面讲到的问题就可以简单的通过下面的方式来实现了:
头文件:
#ifndef TEST2_H
#define TEST2_H
#include <QWidget>
#include <QProcess>
#include "ui_Test2.h"
class Test2 : public QWidget
{
Q_OBJECT
public:
Test2(QWidget *parent = 0);
~Test2();
public slots:
void onTest();
void onOutput();
private:
Ui::Test2 ui;
QProcess *m_Process;
};
#endif // TEST2_H
源文件:
#include "Test2.h"
#include <QTextEdit>
Test2::Test2(QWidget *parent)
: QWidget(parent),m_Process(new QProcess)
{
ui.setupUi(this);
ui.lineEdit->setText(tr("ping localhost"));
ui.textEdit->setLineWrapMode(QTextEdit::NoWrap);
m_Process->setProcessChannelMode(QProcess::MergedChannels);
connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(onTest()));
connect(m_Process,SIGNAL(readyReadStandardOutput()),this,SLOT(onOutput()));
}
Test2::~Test2()
{
m_Process->terminate();
}
void Test2::onTest()
{
QString qsCmd = ui.lineEdit->text();
m_Process->start( qsCmd);
}
void Test2::onOutput()
{
QByteArray qbt = m_Process->readAllStandardOutput();
QString msg = QString::fromLocal8Bit(qbt);
ui.textEdit->append(msg);
ui.textEdit->update();
}
运行效果: