顯示具有 QT 標籤的文章。 顯示所有文章
顯示具有 QT 標籤的文章。 顯示所有文章

2011年10月23日 星期日

Qt - 依QLineEdit的內容自動調整整個視窗的大小

在Qt - 選擇資料夾或特定的檔案的例子中,當使用者選擇檔案或資料夾後,會將其完整路徑放到QLineEdit裡面~
但是,若路徑過長,並不會調整整個視窗的大小,讓使用都可以完整的看到其路徑。

若要調整其視窗大小,必需要調整Layout的相關關係~~

在Qt裡面,若有受到Layout的控制的話,裡面的元件的大小會受到以下兩個因素影響
1. Qt Layout的限制
QLayout::SetDefaultConstraint
最小尺吋設定為minimumSize ()
QLayout::SetFixedSize
視窗為sizeHint (),且不能被改變大小。
QLayout::SetMinimumSize
視窗被設定為minimumSize ()且不能被變的更小
QLayout::SetMaximumSize
視窗被設定為maximumSize ()且不能被變的更大
QLayout::SetMinAndMaxSize
最小尺吋為minimumSize ()、最大尺吋是maximumSize ()
QLayout::SetNoConstraint
不受到Layout的控制
2. 子視窗本身也受到自己的size policies及其最大/最小值的影響
QSizePolicy::Fixed
sizeHint () - sizeHint ()
QSizePolicy::Minimum
sizeHint () - 無限大
QSizePolicy::Maximum
0 - sizeHint ()
QSizePolicy::Preferred
0 - 無限大
QSizePolicy::Expanding
0 - 無限大
QSizePolicy::MinimumExpanding
sizeHint () - 無限大
QSizePolicy::Ignored
0 - 無限大

以下就是由Qt - 選擇資料夾或特定的檔案改的結果
main.cpp

#include <QApplication>
#include "MyWidget.h"

int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);

MyWidget myWidget;

myWidget.show ();

return app.exec ();
}

MyWidget.h
#ifndef _MY_WIDGET_H_
#define _MY_WIDGET_H_

#include <QWidget>

class QLabel;
class QPushButton;
class QLineEdit;

class MyWidget : public QWidget {
Q_OBJECT

public:
MyWidget (QWidget *parent = 0);

private slots:
void browse ();
void ChangeSelectFile (bool);
void AdjustMainWindowSize (const QString &);

private:
QLabel *myLabel;
QPushButton *myPushButton;
QLineEdit *myLineEdit;
bool SelectFile;
};

#endif //#ifndef _MY_WIDGET_H_

MyWidget.cpp
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QGroupBox>
#include <QRadioButton>
#include <QSizePolicy>

#include "MyWidget.h"

MyWidget::MyWidget (QWidget *parent) : QWidget (parent) {
//
// init
//

myLabel = NULL;
myLineEdit = NULL;
myPushButton = NULL;

//
// Create Label component
//

myLabel = new QLabel (tr ("Target Name"), this);

//
// Create LineEdit component
//

myLineEdit = new QLineEdit (this);

//
// Set the width of QLineEdit from sizeHint () to max
//

myLineEdit->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);

//
// Create GroupBox and RadioButton component
//

QGroupBox *box = new QGroupBox (tr ("Select Type"));
QRadioButton *radio1 = new QRadioButton (tr ("File"));
QRadioButton *radio2 = new QRadioButton (tr ("Directory"));

//
// Set the default checked RadioButton for SelectFile
//

radio1->setChecked (true);
SelectFile = true;

//
// Set RadioButton into QHBoxLayout
// And set RadioButton into same GroupBox
//

QVBoxLayout *VLayout = new QVBoxLayout;
VLayout->addWidget (radio1);
VLayout->addWidget (radio2);
box->setLayout (VLayout);

//
// Create QPushButton component
//

myPushButton = new QPushButton (tr ("&Browse..."), this);

//
// Add all the components into QHBoxLayout
//

QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget (myLabel);
layout->addWidget (myLineEdit);
layout->addWidget (box);
layout->addWidget (myPushButton);

//
// Set main window to the sizeHint () and can not be changed
//

layout->setSizeConstraint (QLayout::SetFixedSize);
// layout->setSizeConstraint (QLayout::SetMinimumSize);

//
// Set the main Layout for MyWidget
//

this->setLayout (layout);

//
// Set the SIGNAL and SLOT for this component
//

connect (myPushButton, SIGNAL (clicked ()), SLOT (browse ()));
connect (radio1, SIGNAL (toggled (bool)), this, SLOT (ChangeSelectFile (bool)));

//
// When the content of QLineEdit changed, try to resize the Main Window
//

connect (myLineEdit, SIGNAL (textChanged (const QString &)), this, SLOT (AdjustMainWindowSize (const QString &)));
}

void MyWidget::browse () {

QString TargetItem;

QFileDialog myFileDialog (this);
if (SelectFile) {
TargetItem = myFileDialog.getOpenFileName (this, tr ("Open File"), QDir::currentPath (), tr ("All C++ files (*.cpp *.h)"));
} else {
TargetItem = myFileDialog.getExistingDirectory (this, tr ("Select Folder"), QDir::currentPath ());
}
if (TargetItem.isEmpty ()) {
// myLineEdit->setText (myFileDialog.directory ().absolutePath ());
// myLineEdit->clear ();
// do nothing
} else {
myLineEdit->setText (TargetItem);
}
}

void MyWidget::ChangeSelectFile (bool checked) {
SelectFile = checked;
}

void MyWidget::AdjustMainWindowSize (const QString & text) {
QFont myFont;
QFontMetrics fm (myFont);

//
// Calculate the width pixel by the number of word
//

myLineEdit->setMinimumWidth (fm.width (myLineEdit->text ()));

//
// Readjust the main window size by new MinimumWidth
//

// resize (sizeHint ());
}


這裡有一些部分要特別注意~
myLineEdit->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);

設定QLineEdit自身的尺寸策略(size policies)
寬度是sizeHint () ~ 無窮大
高度是限制在sizeHint ()

layout->setSizeConstraint (QLayout::SetFixedSize);

設定主視窗的Layout的長寬為sizeHint (),且不能被改變~

myLineEdit->setMinimumWidth (fm.width (myLineEdit->text ()));

這裡是根據QLineEdit所輸入的內容來決定QLineEdit的最小寬度是多少,
另外,要注意的是,用text ().size ()是回傳有幾個字元,但是,setMinimumWidth是要輸入要幾個pixel,因此,必需由目前使用的字型來決定幾個字元需要多少個pixel。

本程式會把使用者輸入的字串長度轉成pixel,並且設定為QLineEdit的最小長度。
當最小長度大於原本的sizeHint的時候,sizeHint就會被重新設定為MinimumWidth的寬度。
而layout則是根據sizeHint來調整MyWidget的小大~

因此,QLineEdit的寬度會保持在
QLineEdit::sizeHint() ~ fm.width (myLineEdit->text ())

在還沒有輸入任何資料前的樣子


在選擇完檔案或資料夾時,主視窗會自動變大~


在經過這個sample code後,若某一個元件是有被Layout來管理的話,要調整他的大小,沒有辦法直接設定他的大小,必需
1. 設定Layout的policy
2. 設定Widget本身的policy,其最大、最小的寬度
那麼Qt layout manager就可以跟據這一些資料來調整Widget最適合的大小。

參考資料:
QFontMetrics Class Reference
Measuring text width in Qt
QBoxLayout與QWidget之間的關係

2011年10月21日 星期五

Qt - 選擇資料夾或特定的檔案

目前打算做一個小程式,可以讓使用選擇特定的檔案或資料夾

main.cpp

#include <QApplication>
#include "MyWidget.h"

int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);

MyWidget myWidget;

myWidget.show ();

return app.exec ();
}


MyWidget.h
#ifndef _MY_WIDGET_H_
#define _MY_WIDGET_H_

#include <QWidget>

class QLabel;
class QPushButton;
class QLineEdit;

class MyWidget : public QWidget {
Q_OBJECT

public:
MyWidget (QWidget *parent = 0);

private slots:
void browse ();

private:
QLabel *myLabel;
QPushButton *myPushButton;
QLineEdit *myLineEdit;
};

#endif //#ifndef _MY_WIDGET_H_


MyWidget.cpp
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QFileDialog>

#include "MyWidget.h"

MyWidget::MyWidget (QWidget *parent) : QWidget (parent) {
// do nothing
myLabel = NULL;
myLineEdit = NULL;
myPushButton = NULL;

myLabel = new QLabel (tr ("Target Name"), this);

myLineEdit = new QLineEdit (this);

myPushButton = new QPushButton (tr ("&Browse..."), this);

connect (myPushButton, SIGNAL (clicked ()), SLOT (browse ()));

QHBoxLayout *layout = new QHBoxLayout (this);
layout->addWidget (myLabel);
layout->addWidget (myLineEdit);
layout->addWidget (myPushButton);

this->setLayout (layout);
}

void MyWidget::browse () {
// QString directory = QFileDialog::getExistingDirectory (this, tr ("Find Files"), QDir::currentPath ());

QFileDialog myFileDialog (this);
QString itemPath = myFileDialog.getOpenFileName (this, tr ("Open File"), QDir::currentPath (), tr ("All C++ files (*.cpp *.h)"));
if (itemPath.isEmpty ()) {
myLineEdit->setText (myFileDialog.directory ().absolutePath ());
} else {
myLineEdit->setText (itemPath);
}
}

結果如下圖所示


本來打算只利用QFileDialog::getOpenFileName ()就可以選擇檔案和資料夾~
就是當使用者沒有選擇檔案的時候,同時按下「開啟」,這樣就會選擇目前視窗的路徑~
但是,沒有選擇檔案的時候,按開啟並不會回傳值。所以,只好讓使用都可以選擇目前要選擇檔案還是資料夾。
sample code如下:
main.cpp
#include <QApplication>
#include "MyWidget.h"

int main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);

MyWidget myWidget;

myWidget.show ();

return app.exec ();
}

MyWidget.h
#ifndef _MY_WIDGET_H_
#define _MY_WIDGET_H_

#include <QWidget>

class QLabel;
class QPushButton;
class QLineEdit;

class MyWidget : public QWidget {
Q_OBJECT

public:
MyWidget (QWidget *parent = 0);

private slots:
void browse ();
void ChangeSelectFile (bool);

private:
QLabel *myLabel;
QPushButton *myPushButton;
QLineEdit *myLineEdit;
bool SelectFile;
};

#endif //#ifndef _MY_WIDGET_H_

MyWidget.cpp
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QGroupBox>
#include <QRadioButton>

#include "MyWidget.h"

MyWidget::MyWidget (QWidget *parent) : QWidget (parent) {

//
// init
//

myLabel = NULL;
myLineEdit = NULL;
myPushButton = NULL;

//
// Create Label component
//

myLabel = new QLabel (tr ("Target Name"), this);

//
// Create LineEdit component
//

myLineEdit = new QLineEdit (this);

//
// Create GroupBox and RadioButton component
//

QGroupBox *box = new QGroupBox (tr ("Select Type"));
QRadioButton *radio1 = new QRadioButton (tr ("File"));
QRadioButton *radio2 = new QRadioButton (tr ("Directory"));

//
// Set the default checked RadioButton for SelectFile
//

radio1->setChecked (true);
SelectFile = true;

//
// Set RadioButton into QHBoxLayout
// And set RadioButton into same GroupBox
//

QVBoxLayout *VLayout = new QVBoxLayout;
VLayout->addWidget (radio1);
VLayout->addWidget (radio2);
box->setLayout (VLayout);

//
// Create QPushButton component
//

myPushButton = new QPushButton (tr ("&Browse..."), this);

//
// Add all the components into QHBoxLayout
//

QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget (myLabel);
layout->addWidget (myLineEdit);
layout->addWidget (box);
layout->addWidget (myPushButton);

//
// Set the main Layout for MyWidget
//

this->setLayout (layout);

//
// Set the SIGNAL and SLOT for this component
//

connect (myPushButton, SIGNAL (clicked ()), SLOT (browse ()));
connect (radio1, SIGNAL (toggled (bool)), this, SLOT (ChangeSelectFile (bool)));
}

void MyWidget::browse () {

QString TargetItem;

QFileDialog myFileDialog (this);
if (SelectFile) {
TargetItem = myFileDialog.getOpenFileName (this, tr ("Open File"), QDir::currentPath (), tr ("All C++ files (*.cpp *.h)"));
} else {
TargetItem = myFileDialog.getExistingDirectory (this, tr ("Select Folder"), QDir::currentPath ());
}
if (TargetItem.isEmpty ()) {
// myLineEdit->setText (myFileDialog.directory ().absolutePath ());
// myLineEdit->clear ();
} else {
myLineEdit->setText (TargetItem);
}
}

void MyWidget::ChangeSelectFile (bool checked) {
SelectFile = checked;
}


設定為選擇File:

當按Browse後,會出現以下的視窗,並且選擇一個限定的副檔名的檔案

當按下開啟後,會把選擇的檔案的路徑放在QLineEdit component裡面


另外,若選擇Directory的話,並且選擇Browse,會跳出以下的視窗


當按下確定後,就會把資料夾的路徑放在QLineEdit裡面,如下圖所示


參考資料:
QFileDialog Class Reference
Widgets and Layouts
Qt 第二章 创建对话框--子类化QDialog
QRadioButton Class Reference
QAbstractButton Class Reference
QLineEdit Class Reference

2011年10月13日 星期四

在Qt下呼叫外部指令

這裡透過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启用外部程序

2011年10月12日 星期三

設定Qt的執行檔的圖示

先建立一個ICO format bitmap file
Calc.ico

再建立一個Calc.qrc檔~內容為

IDI_ICON1               ICON    DISCARDABLE     "Calc.ico"


並且在pro檔裡面加入以下的內容
RC_FILE  += Calc.qrc


透過qmake重新建立Makefile

重新編譯後,產生的執行檔就有圖示了。


在執行檔的左上角的小圖示也會跟著變動~


參考資料:
Setting the Application Icon
The Qt Resource System
如何更改執行檔的圖案

2011年10月9日 星期日

QTreeWidget簡單例子

QTreeWidget是蠻符合可以讓使用客制化選擇的元件~~
裡面可以包含icon的設定,
子Item的設定
勾選的選項
當有需要的時候,Scroll會自動出現

#include <QApplication>
#include <QTreeWidget>

int
main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);

//
// Create a TreeWidget for store TreeWidgetItem
//

QTreeWidget *TreeWidget = new QTreeWidget;

//
// Set the title for QTreeWidget
//

QStringList ColumnTitle;
ColumnTitle.append ("Name");
ColumnTitle.append ("Comment");
TreeWidget->setHeaderLabels (ColumnTitle);

//
// Add the first item in the root of QTreeWidget
//

QStringList fileColumn;
fileColumn.append ("File Name");
fileColumn.append ("Comment for File Name");
QTreeWidgetItem *ParentItem = new QTreeWidgetItem (fileColumn);
ParentItem->setCheckState (0, Qt::PartiallyChecked);
TreeWidget->addTopLevelItem (ParentItem);

//
// Add the sub item of ParentItem QTreeWidget
//

QStringList ChildFileColumn;
ChildFileColumn.append ("Child Name");
ChildFileColumn.append ("Commet for Child Name");
QTreeWidgetItem *ChildItem = new QTreeWidgetItem (ChildFileColumn);
ChildItem->setCheckState (0, Qt::Checked);
ParentItem->addChild (ChildItem);

//
// Add the second sub item of ParentItem QTreeWidget
//

QStringList ChildFileColumn2;
ChildFileColumn2.append ("Child Name2");
ChildFileColumn2.append ("Comment for Child Name2");
QTreeWidgetItem *ChildItem2 = new QTreeWidgetItem (ChildFileColumn2);
ChildItem2->setCheckState (0, Qt::Unchecked);
ParentItem->addChild (ChildItem2);

//
// Set another root item into the QTreeWidget
//

QStringList AnotherfileColumn;
AnotherfileColumn.append ("Another Name");
AnotherfileColumn.append ("Comment for Another Name");
QTreeWidgetItem *AnotherItem = new QTreeWidgetItem (AnotherfileColumn);
AnotherItem->setCheckState (0, Qt::Unchecked);
TreeWidget->addTopLevelItem (AnotherItem);

TreeWidget->show ();

return app.exec ();
}


內容小於顯示區域,則不會出現ScrollBar


若內容大於要顯示的區域,則會出現ScrollBar


參考資料:
Qt4 Gossip: QTreeWidget 與 QTreeWidgetItem
QTreeWidget Class Reference
QTreeWidgetItem Class Reference

2011年10月2日 星期日

使用QScrollArea來控制元件

當Widget不夠大來顯示裡面的元件時,通常會使用Scroll Bar(捲軸),來調整我們要看到裡面元件的部分。

Qt已經有一個類別,它已經整合了Scroll bar,當裡面的元件超過外面可以顯示的大小時,會自動產生Scroll bar來讓使用者使用。

這裡要注意的是,QScrollArea裡面只能設定一個Main Widget(setWidget)
那如果要設定多個Widget的話呢?
1. 自定一個class是繼承QWidget,裡面就放置多個你要的Widget
2. 設定一個Widget,裡面有一個Layout去管理多個Widget
有空再各寫一個sample code

若要使用Layout必需要注意以下的文章「透過setGeometry設定相對於父類別的位置」

以下就是QScrollArea類別最簡單的例子:
main.cpp

#include <QApplication>
#include <QPushButton>
#include <QScrollArea>

int
main (
int argc,
char *argv[]
)
{
QApplication app (argc, argv);

//
// Create a QScrollArea for the main Widget
//

QScrollArea *ScrollArea = new QScrollArea;
ScrollArea->setWindowTitle ("QScrollArea");

//
// Create a QPushButton
//

QPushButton *Button = new QPushButton ("Hello World", ScrollArea);
Button->resize (350, 350);

//
// Set the PushButton for the main widget in the ScrollArea
//

ScrollArea->setWidget (Button);
ScrollArea->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
ScrollArea->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
ScrollArea->resize (200, 200);

ScrollArea->show ();

return app.exec ();
}



若QScrollBar裡面的Widget被Layout控制的話,則ScrollBar可能永遠不會出現~
因為是因為,裡面的Widget可能會被縮到最小,導致ScrollBar的Area仍然夠用,
當然就不會出現ScrollBar

參考資料:
Qt4 Gossip: QScrollArea
QScrollArea Class Reference

2011年9月30日 星期五

透過setGeometry設定相對於父類別的位置

在Qt提供了許多版面配置管理器 - QLayout, QGridLayout, QBoxLayout, QStackedLayout, QHBoxLayout, QVBoxLayout

這裡有一個使用Layout要注意的事情
QSizePolicy
This property holds the default layout behavior of the widget.

If there is a QLayout that manages this widget's children, the size policy specified by that layout is used. If there is no such QLayout, the result of this function is used.
意思應該是說,若已經有一個Layout在管理這一個Widget的話,那麼這一個Widget的大小就是根據這一個Layout的設定來調整。
若沒有Layout來管理這一個Widget的話,Widget才可以透過程式員自行控制。



這一些可能是我還不太會用~當視窗大小版變時,元件也同時會變大,位置變的非常不漂亮。

因此,我必需學會如何自定位置。

在QWidget可以使用setGeometry,可以設定在父類別的座標系統中,其座標位置應該在哪裡。

以下就是一個在一個Widget裡面的QPushButton設定其相對於父類別的相對位置和其大小。

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLabel>

int
main (
int argc,
char *argv[]
)
{
//
// app is this program's QApplication.
// Here it is created and processes some of the command-line arguments (such as -display under X Window)
//

QApplication app (argc, argv);

//
// Create a main widget
//

QWidget *Widget = new QWidget;
Widget->setWindowTitle ("MainWidget");
Widget->resize (250, 500);

//
// Create a PushButton and the Label is "Hello World", it's parent is Widget
//

QPushButton *button = new QPushButton ("Hello World", Widget);

//
// Set the geometry of the widget relative to its parent
// This example is (20, 0) relative to it's parent
// button's size is width: 100 heigh: 50
//

// button->setGeometry (20, 0, button->sizeHint().rwidth(), button->sizeHint().rheight());
button->setGeometry (20, 0, 100, 50);

Widget->show ();

return app.exec ();
}




參考資料:

2011年9月25日 星期日

透過Qt的QRegExp翻轉檔案的開關

這次是透過QRegExp來翻轉Config.txt的特定開關,並且不改到其它的內容。
Config.txt定義在透過QRegExp分析檔案的開關

main.cpp

#include <QFile>
#include <QDebug>
#include <QTextStream>
#include <QStringList>

int main(int argc, char *argv[])
{
QFile FlagFile ("Config.txt");
QFile CopyFlagFlag ("CopyConfig.txt");
QStringList ConfigStringList;

//
// Test the Config.txt exists or not
//

if (!FlagFile.exists ()) {
qDebug () << "The flag file does not exist!!";
return -1;
}

//
// Try to open Config.txt
//

if (!FlagFile.open (QIODevice::ReadOnly)) {
qDebug () << "Can not oepn FlagFile!!";
return -1;
}

//
// Set QTextStream for Config.txt
//

QTextStream out (&FlagFile);

//
// Insert all the context into ConfigStringList
//

while (!out.atEnd ()) {
ConfigStringList << out.readLine();
}

// for (int i = 0; i < ConfigStringList.size (); i++) {
// qDebug () << ConfigStringList.at (i);
// }

//
// Try to create CopyFlagFlag.txt
//

CopyFlagFlag.open (QIODevice::WriteOnly | QIODevice::Text);

//
// Set QTextStream for CopyConfig.txt
//

QTextStream in (&CopyFlagFlag);

//
// Set the regular expressions for Flag format
//

QRegExp YesNoReg ("^(\\w+\\s*=\\s*)(YES|NO)(\\s*)$");

for (int i = 0; i < ConfigStringList.size (); i++) {

if (YesNoReg.indexIn (ConfigStringList.at (i)) != -1) {
//
// When the line match the regular expressions
//


if (YesNoReg.cap (2) == QString ("YES")) {
//qDebug () << YesNoReg.cap (1) << QString ("NO") << YesNoReg.cap (3);

//
// Invert YES to NO
//

in << YesNoReg.cap (1) << QString ("NO") << YesNoReg.cap (3);

} else {
//qDebug () << YesNoReg.cap (1) << QString ("YES") << YesNoReg.cap (3);

//
// Invert NO to YES
//

in << YesNoReg.cap (1) << QString ("YES") << YesNoReg.cap (3);

}
} else {

//
// When the line does not match the regular expressions
//


//qDebug () << ConfigStringList.at (i);
in << ConfigStringList.at (i);
}

if (i < ConfigStringList.size ()) {
in << endl;
}

}

FlagFile.close ();
CopyFlagFlag.close ();

return 0;
}


如此一開,只會改到開關,其它有幾個空白,有幾個tab完全不會動到。

參考資料:
透過QRegExp分析檔案的開關
QDataStream Class Reference

2011年9月24日 星期六

如何透過Qt讀取Unicode的檔案

今天試了一下,如何讀取Unicode的檔案,看起來,Qt都已經把它做好了,跟讀取ANSI的檔案方式沒有什麼差別。

這裡寫了一個例子,
Info.uni是一個Unicode(UTF-16)的檔案,執行這一個檔案會,
會多出一個CopyInfo.uni(UTF-16)的檔案與Info.txt(ANSI)

另外,因為,在Qt中,讀取UTF-16與ANSI的方式是一樣的,所以,
當你把Info.uni的內容改成ANSI的話,同樣程式可以work,
並且一樣會產生出CopyInfo.uni(UTF-16)的檔案與Info.txt(ANSI)

範例程式碼如下:
main.cpp

#include <QFile>
#include <QDebug>
#include <QTextStream>
#include <QStringList>

int main(int argc, char *argv[])
{
QFile UnicodeFile("Info.uni");
QFile CopyUnicodeFile("CopyInfo.uni");
QFile ANSIFile ("Info.txt");
QStringList ContainStringList;

//
// Try to open Unicode file
//

if (!UnicodeFile.open(QIODevice::ReadOnly)) {
qDebug () << "Open UnicodeFile error!!";
return -1;
}

//
// Create a QTextStream for UnicodeFile
//

QTextStream UnicodeOut (&UnicodeFile);

//
// Insert all the contain into ContainStringList
//

while (!UnicodeOut.atEnd()) {
//qDebug() << UnicodeOut.readLine();
ContainStringList << UnicodeOut.readLine();
}

UnicodeFile.close();

//
// Try to create a unicode file
//

if (!CopyUnicodeFile.open(QIODevice::WriteOnly)) {
qDebug() << "Open CopyUnicodeFile error!!";
return -1;
}

QDataStream CopyUnicodeData(&CopyUnicodeFile);

//
// Create Unicode header (FFFE) for CopyInfo.uni
//

CopyUnicodeData << (quint8) 0xFF << (quint8) 0xFE;
CopyUnicodeFile.close();

if (!CopyUnicodeFile.open(QIODevice::Append | QIODevice::Text)) {
qDebug() << "Open CopyUnicodeFile error!!";
return -1;
}

QTextStream CopyUnicodeOut (&CopyUnicodeFile);
CopyUnicodeOut.setCodec("UTF-16");

//
// Insert all the contain from ContainStringList into CopyInfo.uni (Unicode decode)
//

for (int i = 0; i < ContainStringList.size();) {
CopyUnicodeOut << ContainStringList.at(i++);
if (i < ContainStringList.size()) {
CopyUnicodeOut << endl;
}
}

CopyUnicodeFile.close();

//
// Try to create a ANSI file
//

if (!ANSIFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Open ANSIFile error!!!";
return -1;
}

//
// Try to create a ANSI file
//

QTextStream ANSIOut (&ANSIFile);

//
// Insert all the contain from ContainStringList into Info.txt (ANSI decode)
//

for (int i = 0; i < ContainStringList.size();) {
ANSIOut << ContainStringList.at(i++);
if (i < ContainStringList.size()) {
ANSIOut << endl;
}
}

ANSIFile.close();

return 0;
}


參考資料:
QString Class Reference
建立一個Unicode檔案的簡單程式

2011年9月23日 星期五

透過QRegExp分析檔案的開關

今天寫了一個Qt的簡單的程式,可以用來分析一個檔案,並且把特定的位置的資料讀取出來,並且方便以後透過這一些讀取出來的設定,來做適當的動作。

假設目前有一個檔案內容如下
Config.txt

#
# This is mark
#

# Show Log or not
ShowLog = YES

# Print the User Name
PrintMyName = NO

# Show my age or not
ShowMyAge = YES # Comment for this item


程式碼如下:
main.cpp
#include <QtCore/QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QRegExp>
#include <QStringList>

int main (int argc, char *argv[])
{
// QCoreApplication a (argc, argv);

QFile File ("Config.txt");

QString TempString;

QStringList TempStringList;

//
// Try to open file
//

if (!File.open (QIODevice::ReadOnly)) {
qDebug () << "Can Not open File!!";
return -1;
}

QTextStream out (&File);

//
// Insert all the string into TempStringList except the string start with '#' char and null line
//

QRegExp MarkReg ("^[^#].*$");
while (!out.atEnd ()) {
TempString = out.readLine ();

if (TempString.isEmpty ()) {
//
// If the line is null line, judge the next line
//

continue;
}

if (MarkReg.indexIn (TempString) != -1) {
// qDebug () << TempString;
TempStringList << TempString;
}
}

//
// Print the string in the TempStringList
//

for (int i = 0; i < TempStringList.size (); i++) {
// qDebug () << TempStringList.at (i);
}

//
// Print the string with YES flag in the end of the line
//

QRegExp YesReg ("^(\\w+)\\s*=\\s*(YES)\\s*$");
for (int i = 0;i < TempStringList.size(); i++) {
if (YesReg.indexIn (TempStringList.at (i)) != -1) {
// qDebug () << TempStringList.at (i);
// qDebug () << YesReg.cap (1);
}
}

//
// Print the string with NO flag in the end of the line
//

QRegExp NoReg ("^(\\w+)\\s*=\\s*(NO)\\s*$");
for (int i = 0; i < TempStringList.size (); i++) {
if (NoReg.indexIn (TempStringList.at (i)) != -1) {
// qDebug () << TempStringList.at (i);
// qDebug () << NoReg.cap (1);
}
}

//
// Print the string with YES or NO flag in the end of the line
//

QRegExp YesNoReg ("^(\\w+)\\s*=\\s*(YES|NO)(\\s*|\\s*#.*)$");
for (int i = 0; i < TempStringList.size (); i++) {
if (YesNoReg.indexIn(TempStringList.at (i)) != -1) {
// qDebug () << TempStringList.at (i);
qDebug () << YesNoReg.cap (1) << "is" << YesNoReg.cap (2);
}
}

// return a.exec ();
return 0;
}


參考資料:
QStringList Class Reference
QTextStream Class Reference
QRegExp Class Reference

2011年9月19日 星期一

建立一個Unicode檔案的簡單程式

最近可能要開發一個對Unicode(UTF-16)處理的工具~

今天在Qt上先試了一下如何建立一個Unicode的文件程式。

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QtCore>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QFile file("binary.txt");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
//
// Create Unicode header (FFFE)
//

out << (qint8) 0xFF << (qint8) 0xFE;
file.close();

//QFile file("test.txt");

if (file.open (QIODevice::Append | QIODevice::Text)) {

QTextStream out1(&file);
out1.setCodec ("UTF-16");
out1 << "Hello World!!";
file.close();
}

return a.exec();
}


參考資料:
Qt4 Gossip: QTextStream
QDataStream Class Reference
QTextStream Class Reference

2011年9月2日 星期五

用Qt Creator寫一個簡單的計算機

之前有寫過一篇文章[Qtcreator - Qt的IDE],記錄Qt Creator的初體驗~
不過,當時也只是build完Qt的demo example後,覺得用Qt可以寫成這一些程式,覺得好厲害~
當時仍然以linux的環境為主,所以,初次嘗試就到此為止。

現在的主要環境改到Windows了(但是,我還是沒有放棄linux,有時候還是會玩一下)
要寫一些視窗介面的tool,因為,我用VC IDE寫程式的經驗幾乎是0,所以,我打算還是用Qt在windows下寫程式了。

2011年7月26日 星期二

如何build出不需要另外建置環境的QT exe檔

目標是如何在透過QT寫出執行檔,並且直接在另一台沒有QT的library環境上執行

測試過程:
下載程式碼:

1. 先讓qmake去掃整個程式檔案架構 (產生hello.pro)
%> qmake -project

2. 再用qmake依hello.pro產生Makefile
%> qmake

3. 開始編譯
%> mingw32-make release

若直接在原本的command windows執行檔案,會成功,但是,若是用點滑鼠點兩下的方式,會失敗,並且出現出現找不到mingwm10.dll的錯誤。
原因是,目前使用的是動態連結lib的方式,所以,在執行時,會去%PATH%的目錄中尋找相對應的lib。因為,我們若是用QT的command windows,因為,%PATH%都已經設定好了,所以,可以找到相對應的lib。
若我們直接用滑鼠點兩下的方式,吃的是系統預設的PATH,所以,會出現找不到lib的錯誤訊息。
有兩個解決方式
1. 把需要的lib放在系統的%PATH%路徑中
2. 編譯出靜態連結執行檔(執行檔會變的比較大)

Q: 出現找不到mingwm10.dll錯誤
A1: 在Makefile裡面把-mthreads刪除
A2: 直接在hello.pro加入以下內容 (參考qmake.conf相對應的Flag)
QMAKE_CXXFLAGS_EXCEPTIONS_ON -= -mthreads
QMAKE_LFLAGS_EXCEPTIONS_ON -= -mthreads
A3: 直接在win32-g++\qmake.conf 移除掉所有的"-mthreads".

Q: 出現找不到libgcc_s_dw2-1.dll錯誤
A1: 在Makefile裡面的FLAGS加入-static

Q: 出現找不到QtCore4.dll錯誤
A1: ...
A2: 直接把mingwm10.dll, libgcc_s_dw2-1.dll, qtcore4.dll, qtgui4.dll放到與執行檔同一個目錄

參考資料:
用Qt Creator開發Windows的簡單計算機的程式
記錄-如何建立MSVS的Qt編譯環境?
[Mobile]Nokia的開發工具-Qt framework
[Qt密技]執行Qt程式時找不到相關dll檔該怎麼辦?
Removing mingwm10.dll dependency
QT static 出現找不到libgcc_s_dw2-1.dll
windows下 Qt 静态编译
Qt 程序在 windows 下的发布
Deploying an Application on Windows

2009年6月8日 星期一

QMap

QMap實在是超好用的啦~
因為,目前在寫sctp的網路應用程式,所以,我必需記錄每一個ip現在的情況,所以對應的呢,應該是我給他一個ip,系統會給我一堆這一個ip的相關資訊。

這個關係就是我給他index,而他給我資料。
QMap的另一個重點是它回傳的是指標。
而QPtrList是由共同的index來處理所有使用這一個QPtrList的元素。
所以當有多個thread要共同使用這一個QPtrList就會出現問題。

而QMap有
find()這一個函式,而且回傳的是Iterator,應該說是指標的一種,因此,可以在不同的函式中使用,而不會互相干擾。

這裡有一個例子,來說明如何使用QMap
QMap有兩個組成元素,一個是key,一個是data
我們都是由key去尋找data
main.cpp

#include <qstring.h>
#include <qmap.h>
#include <qstring.h>
#include <qapplication.h>

class Employee
{
public:
Employee(): sn(0) {}
Employee( const QString& forename, const QString& surname, int salary ) : fn(forename), sn(surname), sal(salary)
{ }

QString forename() const { return fn; }
QString surname() const { return sn; }
int salary() const { return sal; }
void setSalary( int salary ) { sal = salary; }

private:
QString fn;
QString sn;
int sal;
};

int main(int argc, char **argv)
{
QApplication app( argc, argv );

typedef QMap<QString, Employee> EmployeeMap;
EmployeeMap map;

// 新增相對應的元素, 同時設定key與data
map["JD001"] = Employee("John", "Doe", 50000);
map["JD002"] = Employee("Jane", "Williams", 80000);
map["TJ001"] = Employee("Tom", "Jones", 60000);

// 先建立data,再建立key
Employee sasha( "Sasha", "Hind", 50000 );
map["SH001"] = sasha;
sasha.setSalary( 40000 );

EmployeeMap::Iterator it;
for ( it = map.begin(); it != map.end(); ++it ) {
printf( "%s: %s, %s earns %d\n",it.key().latin1(),it.data().surname().latin1(),it.data().forename().latin1(),it.data().salary() );
}

// 透過find去尋找相對應的值
it = map.find("JD001");
printf( "%s: %s, %s earns %d\n",it.key().latin1(),it.data().surname().latin1(),it.data().forename().latin1(),it.data().salary() );

// 透過find去尋找, 若找不到會回傳值會等於map.end()
it = map.find("Hello");
if (it == map.end())
{
qWarning("do not find the element");
}
return 0;
}

2009年5月19日 星期二

如何使用Qt designer

耶,目前還是主要以Qt3在寫程式,所以,先用Qt3的designer來說明吧~
在開發Qt視窗程式時,我們可以使用Qt Designer的所視即所得環境,協助我們進行視窗外觀的設計,並透過指令產生對應的*.h與*.cpp檔案,這邊介紹Qt Designer的一些簡單的使用方法。

首先開啟Qt Designer,執行選單中的[File/New]指令(或按Ctrl+N)新增要使用的視窗類型,如下圖所示:


接下來的畫面與Visual Basic很像,我們在左邊的Toolbox選擇元件,在中間的表單區域以拖拉的方式配置元件,在右邊的Property Editor設定相關屬性,假設我們的元件如下配置:


接下來我們進行簡單的Signals - Slots連結,執行選單中的[Tools/Connect Signal/Slots]指令(或按F3),然後選擇發出Signal的元件,接著會出現以下視窗,在這邊我們簡單的設定按下按鈕後關閉視窗:


您可以執行選單中的[Preview/Preview Form]指令(或按Crtl+T)來預覽配置成果,接下來我們執行[File/Save]指令(或按Ctrl+S),將之儲存為form1.ui檔,這個檔案一堆描述元件屬性的標籤所組成,我們可以使用下面的指令來產生*.h檔:
$ uic -o form1.h form1.ui

然後我們使用下面的指令產生*.cpp檔:
$ uic -i form1.h -o form1.cpp form1.ui

您可以直接開啟這兩個檔案來看看它們的內容,並根據實際需求再作些修改,下面是我們產生的*.h檔內容:

/****************************************************************************
** Form interface generated from reading ui file 'form1.ui'
**
** Created: 二 5月 19 21:37:24 2009
**
** WARNING! All changes made in this file will be lost!
****************************************************************************/


#ifndef FORM1_H
#define FORM1_H

#include <qvariant.h>
#include <qdialog.h>

class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QSpacerItem;
class QLabel;
class QPushButton;

class Form1 : public QDialog
{
Q_OBJECT

public:
Form1( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 );
~Form1();

QLabel* textLabel1;
QPushButton* pushButton1;

protected:

protected slots:
virtual void languageChange();

};

#endif // FORM1_H



下面是產生的*.cpp內容:
/****************************************************************************
** Form implementation generated from reading ui file 'form1.ui'
**
** Created: 二 5月 19 21:38:40 2009
**
** WARNING! All changes made in this file will be lost!
****************************************************************************/


#include "form1.h"

#include <qvariant.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qwhatsthis.h>

/*
* Constructs a Form1 as a child of 'parent', with the
* name 'name' and widget flags set to 'f'.
*
* The dialog will by default be modeless, unless you set 'modal' to
* TRUE to construct a modal dialog.
*/

Form1::Form1( QWidget* parent, const char* name, bool modal, WFlags fl )
: QDialog( parent, name, modal, fl )
{
if ( !name )
setName( "Form1" );

textLabel1 = new QLabel( this, "textLabel1" );
textLabel1->setGeometry( QRect( 210, 30, 150, 65 ) );

pushButton1 = new QPushButton( this, "pushButton1" );
pushButton1->setGeometry( QRect( 250, 140, 82, 27 ) );
languageChange();
resize( QSize(582, 307).expandedTo(minimumSizeHint()) );
clearWState( WState_Polished );

// signals and slots connections
connect( pushButton1, SIGNAL( clicked() ), this, SLOT( close() ) );
}

/*
* Destroys the object and frees any allocated resources
*/

Form1::~Form1()
{
// no need to delete child widgets, Qt does it all for us
}

/*
* Sets the strings of the subwidgets using the current
* language.
*/

void Form1::languageChange()
{
setCaption( tr( "Form1" ) );
textLabel1->setText( tr( "<h1>Qt Designer</h1>" ) );
pushButton1->setText( tr( "Click me" ) );
}


我們使用下面這個main.cpp程式來測試成果:
#include "form1.h"
#include <qapplication.h>

int main(int argc, char **argv) {
QApplication app(argc, argv);

Form1 *fm = new Form1();
app.setMainWidget(fm);
fm->show();

return app.exec();
}


$ qmake-qt3 -project
$ qmake-qt3
$ make



參考資料:
使用Qt Designer設計視窗

2009年5月13日 星期三

QStringList

這一個可以存放QString的容器類別~

#include <qapplication.h>
#include <qstringlist.h>
#include <iostream.h>
using namespace std;

int main(int argc,char **argv)
{
QStringList fonts;
// 加入字串有以下三種用法
fonts.append("Times");
fonts += "Courier";
fonts += "Courier New";
fonts << "Helvetina [Cronyx]" << "Helevtica [Adobe]";

for (QStringList::Iterator it = fonts.begin();it != fonts.end();++it)
{
cout << *it << ":";
}
cout << endl;

return 0;
}


參考資料:
QStringList Class Reference

2009年5月12日 星期二

QBoxLayout與QWidget之間的關係

因為Qt是在設計GUI介面的函式庫,因此在排版的時候,常常會牽扯到QBoxLayout與QWidget之間的關係。
我整理了以下幾種情況
若
QBoxLayout下面要包一個QWidget則使用
qwidget->addWidget(qboxlayout)

若QBoxLayout(qboxlayout1)下面要包一個QBoxLayout(qboxlayout2)
QBoxLayout *qboxlayout2 = new QBoxLayout(qboxlayout1);

若QWidget下面要包一個QBoxlayout則使用
QBoxLayout *qboxayout = new QBoxLayout(qwidget);

參考資料:
QBoxLayout Class Reference

2009年5月1日 星期五

qmake教學

在寫Unix程式時,需要透過Makefile使得在改寫程式碼時,要重新編譯會比較容易。
但是,好的Makefile確是非常難寫。
是的,Qt提供了qmake可以產生Makefile,而基本上,我們只要學會如何寫*.pro檔就可以很容易產生Makefile啦~

一個項目文件是用來告訴qmake關於為這個應用程序建立makefile所需要的細節。例如,一個*.cpp和*.h的列表、任何應用程序特定配置、例如一個必需要連接的函式庫、或者一個額外的包含路徑,都應該放到項目文件中。

「#」註釋

你可以為項目文件添加註釋。註釋由「#」符號開始,一直到這一行的結束。

「TEMPLATE」模板

模板變數告訴qmake為這個應用程序生成哪種makefile。下面是可供使用的選擇:
app - 建立一個應用程序的makefile。這是預設值,所以如果TEMPLATE沒有被指定,這個將被使用。
lib - 建立一個函式庫的makefile。
vcapp - 建立一個應用程序的Visual Studio項目文件。
vclib - 建立一個函式庫的Visual Studio項目文件。
subdirs - 這是一個特殊的模板,它可以建立一個能夠進入特定目錄並且為一個項目文件生成makefile並且為它調用make的makefile。

「app」模板

「app」模板告訴qmake為建立一個應用程序生成一個makefile。當使用這個模板時,下面這些qmake系統變量是被承認的。你應該在你的.pro文件中使用它們來為你的應用程序指定特定信息。

HEADERS - 應用程序中的所有*.h的列表。
SOURCES - 應用程序中的所有*.cpp的列表。
FORMS - 應用程序中的所有.ui文件(由Qt設計器生成)的列表。
LEXSOURCES - 應用程序中的所有lex源文件的列表。
YACCSOURCES - 應用程序中的所有yacc源文件的列表。
TARGET - 可執行應用程序的名稱。默認值為項目文件的名稱。(如果需要擴展名,會被自動加上。)
DESTDIR - 放置可執行程序目標的目錄。
DEFINES - 應用程序所需的額外的預處理程序定義的列表。(就好像gcc中的 -D)
INCLUDEPATH - 應用程序所需的額外的包含路徑的列表。
DEPENDPATH - 應用程序所依賴的搜索路徑。
VPATH - 尋找補充文件的搜索路徑。
DEF_FILE - 只有Windows需要:應用程序所要連接的.def文件。
RC_FILE - 只有Windows需要:應用程序的資源文件。
RES_FILE - 只有Windows需要:應用程序所要連接的資源文件。

你只需要使用那些你已經有值的系統變量,例如,如果你不需要任何額外的INCLUDEPATH,那麼你就不需要指定它,qmake會為所需的提供默認值。例如,一個實例項目文件也許就像這樣:

TEMPLATE = app
DESTDIR = c:\helloapp
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
DEFINES += QT_DLL
CONFIG += qt warn_on release

如果條目是單值的,比如template或者目的目錄,我們是用「=」,但如果是多值條目,我們使用「+=」來為這個類型添加現有的條目。使用「=」會用新值替換原有的值,例如,如果我們寫了DEFINES=QT_DLL,其它所有的定義都將被刪除。

「lib」模板

「lib」模板告訴qmake為建立一個庫而生成makefile。當使用這個模板時,除了「app」模板中提到系統變量,還有一個VERSION是被支持的。你需要在為庫指定特定信息的.pro文件中使用它們。
VERSION - 目標庫的版本號,比如,2.3.1。

「subdirs」模板

「subdirs」模板告訴qmake生成一個makefile,它可以進入到特定子目錄並為這個目錄中的項目文件生成makefile並且為它調用make。

在這個模板中只有一個系統變量SUBDIRS可以被識別。這個變量中包含了所要處理的含有項目文件的子目錄的列表。這個項目文件的名稱是和子目錄同名的,這樣qmake就可以發現它。例如,如果子目裡是「myapp」,那麼在這個目錄中的項目文件應該被叫做myapp.pro。

CONFIG變量

配置變量指定了編譯器所要使用的選項和所需要被連接的函式庫。配置變量中可以添加任何東西,但只有下面這些選項可以被qmake識別。

下面這些選項控制著使用哪些編譯器標誌:
release - 應用程序將以release模式連編。如果「debug」被指定,它將被忽略。
debug - 應用程序將以debug模式連編。
warn_on - 編譯器會輸出儘可能多的警告信息。如果「warn_off」被指定,它將被忽略。
warn_off - 編譯器會輸出儘可能少的警告信息。

下面這些選項定義了所要連編的庫/應用程序的類型:
qt - 應用程序是一個Qt應用程序,並且Qt庫將會被連接。
thread - 應用程序是一個多線程應用程序。
x11 - 應用程序是一個X11應用程序或庫。
windows - 只用於「app」模板:應用程序是一個Windows下的窗口應用程序。
console - 只用於「app」模板:應用程序是一個Windows下的控制台應用程序。
dll - 只用於「lib」模板:庫是一個共享庫(dll)。
staticlib - 只用於「lib」模板:庫是一個靜態庫。
plugin - 只用於「lib」模板:庫是一個插件,這將會使dll選項生效。

例如,如果你的應用程序使用Qt庫,並且你想把它連編為一個可調試的多線程的應用程序,你的項目文件應該會有下面這行:

CONFIG += qt thread debug

注意,你必須使用「+=」,不要使用「=」,否則qmake就不能正確使用連編Qt的設置了,比如沒法獲得所編譯的Qt庫的類型了。

如果要使用Qt的網路以及多線程函式庫,可以指定
QT += network

在非 Qt 程序中使用 qmake
事實上,qmake非常好用,我們甚至想在非Qt程序中使用它。這也很容易。只要加入
CONFIG -= qt
那麼qmake就不會添加任何和Qt函式庫相關的header file以及lib之類的進來了。

參考資料:
qmake概念
qmake Command Reference

2009年4月28日 星期二

透過qt寫入與讀取xml檔案

必需要注意的是,在每一個tag均不可以有空白存在,若有空白存在,雖然寫入不會出現問題,但是,在讀取時會出現錯誤,而xml檔案的好處就是可以儲存使用者的設定檔

寫入的範例程式
main.cpp

#include <qapplication.h>
#include <qdom.h>
#include <qfile.h>

int main(int argc,char **argv)
{
// 不顯示GUI
// renyang - 若使用這一行的話, 則不在X11也是可以執行,不會連到X服務器
QApplication app(argc,argv,false);

// 在程式中建立一個叫"IhuConfigXML"的xml檔
QDomDocument doc("IhuConfigXML");
// 建立一個在doc中的成員
QDomElement root = doc.createElement("renyang");
// 把root設定為doc目前的最後一個child
doc.appendChild(root);

// 建立一個在doc中的成員
QDomElement general = doc.createElement("general");

// 設定屬性
general.setAttribute("myName","fangrenyang");
general.setAttribute("height","168cm");
general.setAttribute("weight","70kg");

QDomElement family = doc.createElement("family");

family.setAttribute("grandemother",true);
family.setAttribute("father",true);
family.setAttribute("mother",true);
family.setAttribute("old_brother",true);
family.setAttribute("little_brother",true);

QDomElement education = doc.createElement("education");

education.setAttribute("primary_school",true);
education.setAttribute("secondary_school",true);
education.setAttribute("high_school",true);
education.setAttribute("university",true);
education.setAttribute("graduate_school",true);

// 設定general, family, education均為root的child
// 架構圖為
// doc--root--general
// --family
// --education
root.appendChild(general);
root.appendChild(family);
root.appendChild(education);

// 開啟檔案
QFile confFile("confFile.xml");
// 限定開啟檔案的權限
if (confFile.open(IO_WriteOnly))
{
// 建立一個QTextStream, 提供了基本的讀寫的功能
QTextStream ts(&confFile);
ts << doc.toString();
qDebug("create xml success!!");
confFile.close();
}
else
{
qDebug(QString("can't write config file %1 (%2)").arg("confFile.xml").arg(confFile.errorString()));
}

return 0;
}


讀取的範例程式
main.cpp
#include <qapplication.h>
#include <qfile.h>
#include <qdom.h>
#include <qstring.h>

int main(int argc,char **argv)
{
QApplication app(argc,argv,false);

// 用來記錄是否讀取xml檔案成功
bool ret = false;

QDomDocument doc("IhuConfigXML");

QString fileName("confFile.xml");

QFile confFile(fileName);

if (confFile.open(IO_ReadOnly))
{
// This function reads the XML document from the IO device dev
// 若xml內的tag有空白的話,那麼此時會出現錯誤
if (doc.setContent(&confFile))
{
// 設定文件標頭為一開始的最上層tag
QDomElement root = doc.documentElement();
if (root.tagName() == "renyang")
{
// 取得第一個child tage
QDomNode n = root.firstChild();
while (!n.isNull())
{
QDomElement e = n.toElement();
if (!e.isNull())
{
QString tagName = e.tagName();
if (tagName == "general")
{
qDebug(QString("myName: %1").arg(e.attribute("myName",QString(""))));
}
else if (tagName == "family")
{
// 這裡會找不到grandemother, 因為實際檔案中grandmother多打了一個e, 找不到就會回傳後面的預設值
qDebug(QString("grandmother: %1").arg(e.attribute("grandmother","99")));
}
else if (tagName == "education")
{
qDebug(QString("primary_school: %1").arg(e.attribute("primary_school","99")));
}
else
{
// do nothing
}
}
// 下一個tag
n = n.nextSibling();
}
ret = true;
}
else
{
qWarning(QString("Error: %1 is not a valid config file").arg("confFile.xml"));
}
confFile.close();
}
else
{
qWarning(QString("Error: error occurred while parsing %1").arg("confFile.xml"));
}
}
else
{
qWarning(QString("Error: can't read config file %1 (%2)").arg("confFile.xml").arg(confFile.errorString()));
}
return ret;
}

2009年4月20日 星期一

Qt - QSettings類別

這一個像是在記錄應用程式設定值的類別,常常有應用程式在第一次執行程式之後,會要求使用者設定一些參數值,而這一個類別像是用來記錄這一些參數值。

QString QSettings::readEntry(const QString & key, const QString & def = QString::null, bool * ok = 0)

透過key讀取特定的值,回傳一個QString,或是一個default value,若找不到此值對應的value,則以default value為相對應此key的值,並且回傳回來。若不能被讀取且ok又不是一個null pointer則會回傳TRUE,否則為FALSE

bool QSettings::writeEntry ( const QString & key, const QString & value )
把value的值寫入相對應的key中,若失敗的話,會回傳false,若成功的話,會回傳true

QStringList QSettings::entryList ( const QString & key ) const
列出所有目錄下的成員,例:
/MyCompany/MyApplication/background color
/MyCompany/MyApplication/foreground color
/MyCompany/MyApplication/geometry/x
/MyCompany/MyApplication/geometry/y
/MyCompany/MyApplication/geometry/width
/MyCompany/MyApplication/geometry/height
若key為"/MyCompany/MyApplication"
則
/MyCompany/MyApplication/background color
/MyCompany/MyApplication/foreground color
會被收集起來,而子目錄下的並不會被收集起來,換句話說,只會收集當前目錄下的成員。

main.cpp

#include <qsettings.h>
#include <qapplication.h>
#include <qstringlist.h>

int main(int argc,char **argv)
{
QApplication app(argc,argv);

bool gui=false;
bool ret=false;
int width = 88;
QString bgColor;
QStringList keys;
QValueList <QSettings>::iterator it; // 用來放QSettings的容器

QSettings settings;
// settings.insertSearchPath(QSettings::Windows,"MyCompany");
// No search path needed for Unix; see notes further on.
// Use default values if the keys don't exist

// 可以把後面的數值寫到settings的這一個key項目中
// 1. 設定第一個項目"/MyApplication/geometry/width"

settings.writeEntry("/MyApplication/geometry/width",width);

// 讀取相對應的key,且讀出來的是字串型態
// 2. 因之前沒有設定過,所以目前是設定第二個項目"/MyApplication/background color"
// 字串的部分一直轉換!!失敗!!,所以在search時,也不會找到這一個key

bgColor = settings.readEntry("/MyApplication/background color","white",&ret);
if (!ret)
qDebug("1. transfer error");

// 若沒有找到則以後面的default value設定"MyApplication/geometry/width"為相對應的值,並回傳回來
// 以下的例子,若之前沒有設定"MyApplication/geometry/width"的相對應的值
// 則"MyApplication/geoetry/width"會被設定為640

width = settings.readNumEntry("/MyApplication/geometry/width",640,&ret);

qDebug("%d",settings.readNumEntry("/MyApplication/geometry/width",55,&ret));

keys = settings.entryList("/MyApplication/geometry");

qDebug("%d",keys.count());

return gui?app.exec():0;
}