這裡透過Qt的QProcess來呼叫外部程式,並且把結果顯示在QTextEdit
main.cpp
#include <QApplication>
#include <QTextEdit>
#include <QProcess>
int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);
QTextEdit *TextEdit = new QTextEdit;
TextEdit->show ();
QProcess *proc = new QProcess;
// proc->startDetached ("ping 168.95.1.1");
proc->start ("ping 168.95.1.1");
if (proc->waitForFinished (-1)) {
TextEdit->append (proc->readAll ());
} else {
TextEdit->append ("Error");
}
return app.exec ();
}
不過,以上的程式有一個缺點,就是只有當外部指令結束之後,才會把結果顯示在QTextEdit裡面,這個時候會造成畫面當掉的感覺~
透過processEvent ()
可以適時的更新Qt的畫面~
但是仍會頓頓的~
main.cpp
#include <QApplication>
#include <QTextEdit>
#include <QProcess>
int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);
QTextEdit *TextEdit = new QTextEdit;
TextEdit->show ();
QProcess *proc = new QProcess (TextEdit);
int x = 0;
// proc->startDetached ("ping 168.95.1.1 -t");
proc->start ("ping 168.95.1.1 -t");
while (proc->state () == QProcess::Running) {
x++;
// while (1) {
if (proc->waitForReadyRead (-1)) {
if (x == 10) {
proc->close ();
break;
}
TextEdit->append (proc->readAll ());
app.processEvents ();
} else {
TextEdit->append ("Error");
}
}
return app.exec ();
}
最後,透過sign與slot,當有資料可讀的時候,才去讀資料,終於解決會頓頓的情況了。
以下是sample code
MyTextEdit.h
#ifndef _MY_TEXT_EDIT_H_
#define _MY_TEXT_EDIT_H_
#include <QTextEdit>
class QProcess;
class MyTextEdit : public QTextEdit {
Q_OBJECT
public:
MyTextEdit (QWidget *parent = 0);
void StartCmd ();
public slots:
void AppendContextFromStandOut ();
void AppendContextFromStandErr ();
void AppendContext ();
private:
QProcess *MyProc;
};
#endif //#ifndef _MY_TEXT_EDIT_H_
MyTextEdit.cpp
#include "MyTextEdit.h"
#include <QProcess>
MyTextEdit::MyTextEdit (QWidget *parent) : QTextEdit (parent) {
// do nothing
}
void MyTextEdit::AppendContextFromStandOut () {
this->append (MyProc->readAllStandardOutput ());
}
void MyTextEdit::AppendContextFromStandErr () {
this->append (MyProc->readAllStandardError ());
}
void MyTextEdit::AppendContext () {
this->append (MyProc->readAll ());
}
void MyTextEdit::StartCmd () {
MyProc = new QProcess (this);
// connect (MyProc, SIGNAL(readyReadStandardOutput ()), this, SLOT(AppendContextFromStandOut ()));
// connect (MyProc, SIGNAL(readyReadStandardError ()), this, SLOT(AppendContextFromStandErr ()));
connect (MyProc, SIGNAL(readyRead ()), this, SLOT(AppendContext ()));
MyProc->start ("ping 168.95.1.1 -t");
}
main.cpp
#include <QApplication>
#include "MyTextEdit.h"
int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);
MyTextEdit *TextEdit = new MyTextEdit;
TextEdit->show ();
TextEdit->StartCmd ();
return app.exec ();
}
最後輸出如下
原版的如下:
耶,怎麼多出那麼多空白行呢?
為了去了解,輸出的內容是什麼,使用一個很好用的函式QByteArray::toHex ()
有以下三點發現
1. Qt還是使用Unix換行的方式,只有0x0a("\n");而在Windows下的換行為0x0a("\r"),0x0d("\n")。因此,必需要移除掉多餘的0x0d
2. 我們由QProcess讀取出來的output本身就有換行符號(0x0a),而每做一個QTextEdit::append ()前,也會跳到新的一行,因此,QTextEdit::append ()多的這一個換行就變成是多餘的了~所以,必需用QTextEdit::insertPlainText ()來取代
3. 因為,QTextEdit::insertPlainText ()是由目前鼠標的位置直接加入字串。所以必需另外,把QTextEdit設定為disable,避免讓使用著移動滑鼠來改變鼠標的位置。
只需要修改MyTextEdit.cpp,程式碼如下:
#include "MyTextEdit.h"
#include <QProcess>
#include <QChar>
MyTextEdit::MyTextEdit (QWidget *parent) : QTextEdit (parent) {
this->setEnabled (false);
}
void MyTextEdit::AppendContextFromStandOut () {
this->append (MyProc->readAllStandardOutput ());
}
void MyTextEdit::AppendContextFromStandErr () {
this->append (MyProc->readAllStandardError ());
}
void MyTextEdit::AppendContext () {
QByteArray TempByteArray(MyProc->readAll ());
QString TempString(TempByteArray);
TempString.remove (QChar(0x0d));
// this->append (TempByteArray.toHex ());
// this->append (TempString);
this->insertPlainText (TempString);
}
void MyTextEdit::StartCmd () {
MyProc = new QProcess (this);
// connect (MyProc, SIGNAL(readyReadStandardOutput ()), this, SLOT(AppendContextFromStandOut ()));
// connect (MyProc, SIGNAL(readyReadStandardError ()), this, SLOT(AppendContextFromStandErr ()));
connect (MyProc, SIGNAL(readyRead ()), this, SLOT(AppendContext ()));
MyProc->start ("ping 168.95.1.1");
}
結果如下:
但是,直接把0x0d("\r")這字元移除掉,看起來不是很好的辦法,理論上應該是把"\r\n"直接取代成"\n"比較合理
修改MyTextEdit.cpp結果如下:
#include "MyTextEdit.h"
#include <QProcess>
#include <QChar>
MyTextEdit::MyTextEdit (QWidget *parent) : QTextEdit (parent) {
this->setEnabled (false);
}
void MyTextEdit::AppendContextFromStandOut () {
this->append (MyProc->readAllStandardOutput ());
}
void MyTextEdit::AppendContextFromStandErr () {
this->append (MyProc->readAllStandardError ());
}
void MyTextEdit::AppendContext () {
QByteArray TempByteArray(MyProc->readAll ());
QByteArray WinNewLine ("\r\n"), UnixNewLine ("\n");
TempByteArray.replace (WinNewLine, UnixNewLine);
QString TempString(TempByteArray);
// this->append (TempByteArray.toHex ());
// this->append (TempString);
this->insertPlainText (TempString);
}
void MyTextEdit::StartCmd () {
MyProc = new QProcess (this);
// connect (MyProc, SIGNAL(readyReadStandardOutput ()), this, SLOT(AppendContextFromStandOut ()));
// connect (MyProc, SIGNAL(readyReadStandardError ()), this, SLOT(AppendContextFromStandErr ()));
connect (MyProc, SIGNAL(readyRead ()), this, SLOT(AppendContext ()));
MyProc->start ("ping 168.95.1.1");
}
在繼續補足其它功能的時候,意外發現,我已經把視窗關掉了,為什麼我執行的外部指令還在執行哩~
原因就是,我在main.cpp裡面是宣告一個MyTextEdit的指標變數,並且指到我建立的記憶體空間(new QTextEdit),當主程式關掉之後,MyTextEdit的指標變數會release給系統(因為,存活的範圍只有在main的{}裡面),但是,MyTextEdit建立的記憶體空間就不會release給系統,除非delete掉這一個空間。
要解決一個方法,就是在main.cpp裡面,宣告一個物件而不是宣告一個指標來指到一個要求的記憶體位址。
main.cpp
#include <QApplication>
#include "MyTextEdit.h"
int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);
MyTextEdit TextEdit;
TextEdit.show ();
TextEdit.StartCmd ();
return app.exec ();
}
MyTextEdit.h
#ifndef _MY_TEXT_EDIT_H_
#define _MY_TEXT_EDIT_H_
#include <QTextEdit>
class QProcess;
class MyTextEdit : public QTextEdit {
Q_OBJECT
public:
MyTextEdit (QWidget *parent = 0);
void StartCmd ();
bool IsScrollDown ();
public slots:
void AppendContext ();
void MoveScrollDown ();
private:
QProcess *MyProc;
};
#endif //#ifndef _MY_TEXT_EDIT_H_
MyTextEdit.cpp
#include "MyTextEdit.h"
#include <QProcess>
#include <QChar>
#include <QScrollBar>
#include <QDebug>
MyTextEdit::MyTextEdit (QWidget *parent) : QTextEdit (parent) {
MyProc = NULL;
setReadOnly (true);
// setTextInteractionFlags (textInteractionFlags() & Qt::NoTextInteraction);
// setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
}
void MyTextEdit::AppendContext () {
bool KeepDownScroll = false;
int OrgScrollBarValue = 0;
//
// Get the output string
//
QByteArray AppendByteArray(MyProc->readAll ());
//
// Get the original ScrollBar value
//
OrgScrollBarValue = verticalScrollBar ()->value ();
QByteArray WinNewLine ("\r\n"), UnixNewLine ("\n");
//
// Replace "\r\n" by "\n"
//
AppendByteArray.replace (WinNewLine, UnixNewLine);
QString AppendString(AppendByteArray);
// this->append (AppendByteArray.toHex ());
if (IsScrollDown ()) {
if (!(textCursor ().hasSelection () || textCursor ().hasComplexSelection())) {
//
// When user select some range or the scroll is already under the buttom
// set the scroll under the buttom
//
KeepDownScroll = true;
}
}
//
// In order to append string in the end of text
// have to get the end cursor of the text
// After append the string, return the cursor to the original status
//
QTextCursor EndCursor, OriginalCursor;
EndCursor = OriginalCursor = textCursor ();
EndCursor.movePosition (QTextCursor::End);
setTextCursor (EndCursor);
//
// Append the string in the end of text
//
insertPlainText (AppendString);
//
// return the cursor to the original status
//
setTextCursor (OriginalCursor);
//
// Set the scrollbar value be the original value
//
QScrollBar *ScrollBar = verticalScrollBar ();
ScrollBar->setValue (OrgScrollBarValue);
//
// When user select some range or the scroll is already under the buttom
// set the scroll under the buttom
//
if (KeepDownScroll) {
MoveScrollDown ();
}
}
void MyTextEdit::StartCmd () {
MyProc = new QProcess (this);
connect (MyProc, SIGNAL(readyRead ()), this, SLOT(AppendContext ()));
MyProc->start ("ping 168.95.1.1 -t");
}
void MyTextEdit::MoveScrollDown () {
// Method 1
// QTextCursor c = textCursor ();
// c.movePosition (QTextCursor::End);
// setTextCursor (c);
// Method 2
QScrollBar *ScrollBar = verticalScrollBar ();
ScrollBar->setValue (ScrollBar->maximum ());
}
bool MyTextEdit::IsScrollDown () {
QScrollBar *ScrollBar = verticalScrollBar ();
if (ScrollBar->value () == ScrollBar->maximum ()) {
return true;
} else {
return false;
}
}
結果如下:
1. 當原本右邊的ScrollBar原本就在底部的話,就算文字內容增加,ScrollBar仍會保持在底部
2. 當使用者有圈選內容的話,就算文字內容增加,ScrollBar會保持在原來的位置,若原本的ScrollBar是在底部的話,會因為文字內容增加,而讓ScrollBar離開底部
3. 當使用者移動ScrollBar離開底部時,當文字內容增加,ScrollBar仍會保持在原來的位置。但當使用者把ScrollBar拉回底部時,當文字內容增加,ScrollBar會依然保持在底部。
參考資料:
QT下实现对linux 的Shell 调用 的几种方法 QProcess AND QThread
QTextEdit Class Reference
QProcess Class Reference
QProcess运行外部程序
怎么处理QT的UI界面假死的情况
QT程序 避免假死、无反应现象
QT中使用QProcess启用外部程序||exe
关于窗口调用外部程序及接收返回内容的调试
QT下解决换行符、回车符与Windows不一致的问题
解决QProcess對象調用execute執行cmd命令不支持中文和空格的問題
VCard
Qt - QTextEdit自動捲到底
QT中使用QProcess启用外部程序
沒有留言:
張貼留言