应用场景与问题描述:
问题是这样的,我写了很多命令行程序用于处理遥感影像,这种方式很方便,可以通过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(); }
运行效果: