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月18日 星期二

Advanced Format Driver

在2011年1月後出的硬碟都會支援Advanced Format
原本的硬碟都是採用512 byte為一個chunk,
在Advanced Format是採用4k為一個chunk。

聽說在XP上使用這一個硬碟的話,會有很多問題~
目前可能還是不打算使用這一種新的硬碟~

參考資料:
已經來臨的硬碟格式變動 先進格式
體驗最新 WD 先進格式化技術硬碟!

放棄XP系統 WD硬盤先進格式化使用要點
先進格式化

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 ();
}


最後,透過signslot,當有資料可讀的時候,才去讀資料,終於解決會頓頓的情況了。
以下是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月10日 星期一

Inno Setup - 進階設定

1. 修改安裝過程的圖片

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "Caspar_Calc"
#define MyAppVersion "1.0"
#define MyAppPublisher "Caspar Company, Inc."
#define MyAppURL "http://ccd9527.blogspot.com"
#define MyAppExeName "Calc.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{9436BBE4-5C37-413F-9D7D-92F4B8EB131B}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
WizardImageFile=D:\Release\Inno Setup\install.bmp
WizardSmallImageFile=D:\Release\Inno Setup\install2.bmp

AppVerName={#MyAppName} {#MyAppVersion}
(以下略)

圖片限定使用bmp且大小上限是164x314 pixels
WizardImageFile變數是顯示一開始安裝的第一個畫面~


圖片限定使用bmp且大小上限是55x58 pixels
WizardSmallImageFile的部分是設定安裝右上角的小圖


2. 指定壓縮方式
在[Setup]區段可以選擇壓縮的方式
在說明檔裡面,有以下的值可以使用
zip
zip/1 through zip/9
bzip
bzip/1 through bzip/9
lzma
lzma/fast
lzma/normal
lzma/max
lzma/ultra (review memory requirements below before using)
lzma/ultra64 (review memory requirements below before using)
lzma2
lzma2/fast
lzma2/normal
lzma2/max
lzma2/ultra (review memory requirements below before using)
lzma2/ultra64 (review memory requirements below before using)
none
Default value:
lzma2/max

預設是使用lzma2/max
zip壓縮方式比較快,而且需要記憶體的空間比較小
lzma2是使用7z的演算法,壓縮效率比較好,但是,需要的時間與記憶體空間比較多

3. 為要執行的檔案建立捷徑
在[Icons]的區段加入以下的內容
Name: "{userdesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}";WorkingDir: "{app}"; IconFilename: "D:\Release\Inno Setup\OA6.ico"; Comment: "快捷方式"



4. 安裝完後,顯示ReadMe文件
在[Files]區段裡面ReadMe文件最後的Flags設定isreadme
而且一次可以輸入多個ReadMe檔案
Source: "D:\Release\Inno Setup\ReadMe.txt"; DestDir: "{app}";Flags:isreadme
Source: "D:\Release\Inno Setup\ReadMe_Doc.doc"; DestDir: "{app}";Flags:isreadme



5. 在程式安裝目錄下,建立一個資料夾
在[Files]區段輸入以下內容
Name: "{app}\WorkDir"

這樣在安裝後,會建立WorkDir的資料夾


當然這個文件夾是空白的,一般可用來作為初始工作的目錄或者保存結果的目錄。
另外,上面的這種情況下,如果移除程式,該資料夾會被如何處理呢?
1.如果資料夾是空白的,該資料夾會被刪除。
2.如果資料夾非空,該資料夾會被保留

6. 有選擇性的安裝元件
[Components]
Name: main; Description: "主程式(必選)"; Types:full compact custom; Flags: fixed
Name: help; Description: "說明檔"; Types: full

[Files]
Source: "D:\Release\Calc.exe"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\libgcc_s_dw2-1.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\mingwm10.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\QtCore4.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\QtCored4.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\QtGui4.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\QtGuid4.dll"; DestDir: "{app}"; Flags: ignoreversion; Components:main
Source: "D:\Release\Inno Setup\ReadMe.txt"; DestDir: "{app}";Flags:isreadme; Components:help
Source: "D:\Release\Inno Setup\ReadMe_Doc.doc"; DestDir: "{app}";Flags:isreadme; Components:help
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

在[Components]定義有哪一些Components的類別,
並且可以透過fixed關鍵字,來表示一定要安裝的元件
另外,在[Files]在最後,可以用Components來表示是屬於哪一類Components


參考資料:
Inno Setup入門(一) - 最簡單的安裝腳本
Inno Setup入門(十) -- 操作注冊表

2011年10月9日 星期日

建立安裝檔 - Inno Setup

1. 一開始建立一個空的script

2. 透過設置精靈設定




3. 設定應用程式的名稱、版號、應用程式公開者、相關網址


4. 設定要安裝的目標資料夾,預設是Program Files資料夾
也可以自定安裝的資料夾


5. 選擇主要的執行檔與同時要包進來的相關檔案


6. 把所有讓使用者是否建立一些捷徑


7. 選擇要顯示License的內容,與安裝前和安裝後要給使用者看的內容


8. 選擇在安裝過程中支援的語言


9. 設定Inno Setup產生的安裝檔要放的位置、安裝檔的名稱、安裝檔的icon


10. 是否在腳本裡面使用#define來定義一些變數,這樣在重覆使用一些變數上比較方便


11. 到了最後一步了


12. 根據剛剛的設定,建立了一個完整的Script,下一個階段就可以修改這一個Script來增加一些進階的修改


然後,就會產生安裝檔
D:\Release\Inno Setup\Caspar_Calc_Setup.exe

用Inno Setup+MSI的安靜模式參數來安裝
[Components] 組件
Filename: "{app}\INIT.EXE"; Parameters: "/x" 執行
說明裡有

參考資料:
InstallShield
安裝製作-Inno-Setup-5.2.2-繁體中文化版
Setup Factory
製做綠色軟體的安裝檔
大量安裝Inno Setup的批次檔
一鍵安裝 自動安裝 製作程式 Inno Setup
Inno Setup入門(一) - 最簡單的安裝腳本

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月7日 星期五

內文搜尋軟體 - DocFetcher

一直以來因為記憶力不好而困擾~~
所以透過blog來記錄學習的點點滴滴~~
工作的內容,
可能會有一些法津上的問題,確沒有辦法透過blog來記錄。

最近打算在電腦裡面,仿造一個檔案形式的blog,
會是由pdf、Office檔案所組成的方式。
檔名就像是blog的每一篇文章的標題,
內文就像是blog的內文一樣,隨時可以補充。

原本屬意Google Desktop,
但是,現在Google Desktop似乎不維護,並且不提供下載了~

最後找到一個Open source的軟體
DocFetcher
可以尋找Office文件檔案、PDF檔案、電子郵件內容、網路瀏覽記錄等等資訊
而且跟Google Desktop的差別是,Google Desktop是整台電腦去搜尋~~
而DocFetcher可以指定要搜尋哪一個資料夾內的資料~~
而且,DocFetch也有Portable版本的,非常好用~~


上面的圖分成五個部分:
1: 設定要尋找的檔案類型
2: 已經建立的索引,在空白處可以按右鍵,對某一個資料夾下的資料內容建立索引
3: 有含要尋找Keyword的檔案,直接對檔案點兩下,可以開啟檔案;另外,也可以用拖曳的方式,直接修改FileName, Title...的順序
4: 當上面的欄位有選擇檔案時,會顯示裡面的「文字」內容
5: 輸入要尋找的Keyword

使用後的感想:
實在是太符合我的需求了~
而且,效能也非常好,
建立的索引檔檔案大小也小~

參考資料:
Archivarius 3000
DNKA - remote desktop search tool
Everything 取代 Windows 內建搜尋,超快速檔案檢索工具
DocFetcher 快找文件檔案內文的輕巧桌面搜尋器,支援中文索引
Locate32 v3.1 - 超快速搜檔
Everything常見問題
為檔案加上Tag功能的桌面搜尋列:VistaGlance 1.2
研究生必備!超好用檔案搜尋軟體 Copernic Desktop Search
Ava Find 1.5 - 檔案搜尋工具就是要一個比一快就是了!

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