我正在围绕控制台应用程序构建GUI。我想能够点击一个按钮来运行控制台应用程序,并显示GUI本身内部的控制台输出。我该如何做到这一点呢?
您也可以尝试QProcess。它提供了一个Qt接口来启动外部进程,读取它们的I/O并等待(或不等待)它们完成。
对于您的目的,听起来像是希望进程异步运行,因此代码可能看起来像:
myprocessstarter.h:
#include <QObject>
#include <QProcess>
#include <QDebug>
class MyProcessStarter : public QObject
{
Q_OBJECT
public:
MyProcessStarter() : QObject() {};
void StartProcess();
private slots:
void readStandardOutput();
private:
QProcess *myProcess;
};
main.cpp:
#include "myprocessstarter.h"
void MyProcessStarter::StartProcess()
{
QString program = "dir";
QStringList arguments;
// Add any arguments you want to be passed
myProcess = new QProcess(this);
connect(myProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
myProcess->start(program, arguments);
}
void MyProcessStarter::readStandardOutput()
{
QByteArray processOutput;
processOutput = myProcess->readAllStandardOutput();
qDebug() << "Output was " << QString(processOutput);
}
void main(int argc, char** argv)
{
MyProcessStarter s;
s.StartProcess();
}
我想在我的一个应用程序中做类似的事情。我将标准流(cout)的所有输出重定向到控制台窗口。为了定期读取流内容,我使用计时器循环。
StdRedirector.cpp
#include "StdRedirector.h"
QMutex coutMutex;
void outcallback(const char* ptr, std::streamsize count, void* bufferString)
{
string *b = (string *) bufferString;
string t;
for (int i=0; i < count; i++)
{
if (ptr[i] == 'n')
{
t = t + "n";
} else {
t = t + ptr[i];
}
}
coutMutex.lock();
*b = *b + t;
coutMutex.unlock();
}
void ConsoleWindow::updateTimer(void)
{
coutMutex.lock();
if (bufferString.size() > 0)
{
consoleBox->insertPlainText(QString(bufferString.c_str()));
bufferString.clear();
QScrollBar *sb = consoleBox->verticalScrollBar();
sb->setValue(sb->maximum());
}
coutMutex.unlock();
}
ConsoleWindow::ConsoleWindow(QWidget *parent) : QWidget(parent)
{
consoleBox = new QTextEdit(this);
consoleBox->setReadOnly(true);
stdRedirector = new StdRedirector<>(std::cout, outcallback, &bufferString);
QVBoxLayout *vb = new QVBoxLayout();
vb->addWidget(consoleBox);
vb->setMargin(0);
vb->setSpacing(0);
setLayout(vb);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateTimer()));
timer->start(100);
}
ConsoleWindow::~ConsoleWindow()
{
delete stdRedirector;
}
StdRedirector.h
#ifndef STD_REDIRECTOR
#define STD_REDIRECTOR
#include <QWidget>
#include <QTextEdit>
#include <QString>
#include <QVBoxLayout>
#include <QTimer.h>
#include <QMutex>
#include <QScrollBar>
#include <iostream>
#include <string>
using namespace std;
template<class Elem = char, class Tr = std::char_traits<Elem>>
class StdRedirector : public std::basic_streambuf<Elem, Tr>
{
typedef void (*pfncb) ( const Elem*, std::streamsize _Count, void* pUsrData );
public:
StdRedirector(std::ostream& a_Stream, pfncb a_Cb, void* a_pUsrData) :
m_Stream(a_Stream),
m_pCbFunc(a_Cb),
m_pUserData(a_pUsrData)
{
m_pBuf = m_Stream.rdbuf(this);
}
~StdRedirector()
{
m_Stream.rdbuf(m_pBuf);
}
std::streamsize xsputn(const Elem* _Ptr, std::streamsize _Count)
{
m_pCbFunc(_Ptr, _Count, m_pUserData);
return _Count;
}
typename Tr::int_type overflow(typename Tr::int_type v)
{
Elem ch = Tr::to_char_type(v);
m_pCbFunc(&ch, 1, m_pUserData);
return Tr::not_eof(v);
}
protected:
std::basic_ostream<Elem, Tr>& m_Stream;
std::streambuf* m_pBuf;
pfncb m_pCbFunc;
void* m_pUserData;
};
class ConsoleWindow : public QWidget
{
Q_OBJECT
public:
ConsoleWindow(QWidget *parent = 0);
~ConsoleWindow();
public slots:
void updateTimer(void);
public:
QTextEdit *consoleBox;
StdRedirector<> *stdRedirector;
string bufferString;
};
#endif
StdRedirector类是基于这个论坛帖子的代码:http://www.qtforum.org/article/24554/displaying-std-cout-in-a-text-box.html
看一下popen()
函数,它可能会满足您的需求。
然后您可以将FILE *
传递给QTextStream
并以Qt风格与它一起工作。
我建议,与其在GUI中显示标准输出,不如拥有自己的控制台输出,这实际上意味着您要向用户显示的所有消息都将发送到您自己的输出。
通过这种方式,您可以从控制台获得调试消息等仍然可用的信息,包括连接的潜在错误和任何可能发生的情况,并在GUI应用程序中完全控制控制台输出。当然,这个输出也可以输出到stdout,以便在控制台中可见,但它也允许您附加一个前缀,如WARNING LOG NOTICE NO_THIS_WENT_WRONG或任何您想要作为控制台条目显示给用户的前缀。