2月 04, 2009

【翻譯】Qt Tutorial 5 - Building Blocks

@
tutorials/tutorial/t5/main.cpp
tutorials/tutorial/t5/t5.pro

  這個範例展示了如何建立許多元件,並使用 signal 與 slot 來把它們連接在一起,還有如何處理改變大小。



 #include <QApplication>
 #include <QFont>
 #include <QLCDNumber>
 #include <QPushButton>
 #include <QSlider>
 #include <QVBoxLayout>
 #include <QWidget>

 class MyWidget : public QWidget
 {
 public:
     MyWidget(QWidget *parent = 0);
 };

 MyWidget::MyWidget(QWidget *parent)
     : QWidget(parent)
 {
     QPushButton *quit = new QPushButton(tr("Quit"));
     quit->setFont(QFont("Times", 18, QFont::Bold));

     QLCDNumber *lcd = new QLCDNumber(2);
     lcd->setSegmentStyle(QLCDNumber::Filled);

     QSlider *slider = new QSlider(Qt::Horizontal);
     slider->setRange(0, 99);
     slider->setValue(0);

     connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
     connect(slider, SIGNAL(valueChanged(int)),
             lcd, SLOT(display(int)));

     QVBoxLayout *layout = new QVBoxLayout;
     layout->addWidget(quit);
     layout->addWidget(lcd);
     layout->addWidget(slider);
     setLayout(layout);
 }

 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
     MyWidget widget;
     widget.show();
     return app.exec();
 }


Line by Line Walkthrough

 class MyWidget : public QWidget
 {
 public:
     MyWidget(QWidget *parent = 0);
 };

 MyWidget::MyWidget(QWidget *parent)
     : QWidget(parent)
 {
     QPushButton *quit = new QPushButton(tr("Quit"));
     quit->setFont(QFont("Times", 18, QFont::Bold));

     QLCDNumber *lcd = new QLCDNumber(2);
     lcd->setSegmentStyle(QLCDNumber::Filled);

  lcd 是一個 QLCDNumber,一個以類似 LCD 方式顯示數字的元件。在這個例子中被設定成顯示兩位數。我們將 QLCDNumber::segmentStyle 的屬性設定為 QLCDNumber::Filled,使這個 LCD 更容易辨識。

  歷史筆記:QLCDNumber 是第一個始終寫在 Qt 裡的元件,那個時候 QPainter 只支持一個原生的繪圖函式:drawLine()。使用 QLCDNumber 來顯示分數的 Tetrix 範例最初版,也是在大約這個時候被寫出來。

     QSlider *slider = new QSlider(Qt::Horizontal);
     slider->setRange(0, 99);
     slider->setValue(0);

  使用者可以使用 QSlider 元件去調整一定範圍內的一個整數值。這裡我們建立了一個水平式的(horizontal) QSlider,設定它的最小值為 0、最大值為 99,且它的初始值為 0。

     connect(slider, SIGNAL(valueChanged(int)),
             lcd, SLOT(display(int)));

  這裡我們使用了 signal 與 slot 的方式將 slider 的 valueChanged() signal 連接到 LCD 數字的 display() slot。

  無論這個 slider 的值在何時被改變,它都會藉由發出(emit) valueChanged() signal 來傳遞這個新值。因為這個 signal 被連接到 LCD 數字的 display() slot,所以當這個 signal 傳遞時,這個 slot 就會被呼叫。這兩個物件彼此都不知道對方。這就是組件程式設計(component programming)的本質。

  slot 與普通的 C++ 成員函式不同,但遵循著正規的 C++ 存取規則。

     QVBoxLayout *layout = new QVBoxLayout;
     layout->addWidget(quit);
     layout->addWidget(lcd);
     layout->addWidget(slider);
     setLayout(layout);

  MyWidget 現在使用了 QVBoxLayout 來管理其子元件的幾何位置。因此,我們不需要再像第四章一樣,明確指定每一個元件的螢幕座標。此外,使用一個版面配置(layout)確保在視窗改變大小時,其子元件都能跟著改變大小。現在我們使用 QBoxLayout::addWidget()quitlcdslider 加入到版面配置裡。

  QWidget::setLayout() 函式把這個版面配置設置在 MyWidget 中。這使得這個版面配置成為 MyWidget 的一個子元件,所以我們不需要去擔心刪除它的問題;父子關係可以確保它將會跟著 MyWidget 一起被刪除。另外,QWidget::setLayout() 將會自動重新設定版面配置中元件的父元件,使它們成為 MyWidget 的子元件。因此,我們不需要為 quitlcdslider 元件指定 this 為它們的父元件。

  在 Qt 中,元件不是其他元件的子元件(例如 this),就是沒有任何父元件。一個元件可以被加入到一個版面配置中。在這種情況下,版面配置就變成需要負起管理這些元件幾何位置的責任了,不過版面配置可以不必自己去扮演父元件的角色。事實上,QWidget 的建構子需要一個作為其父元件的 QWidget 指標,而且 QLayout 也不是繼承自 QWidget。


Running the Application

  這個 LCD 數字反映了你對滾動軸(slider)所做的一切,而且這個元件也成功的處理了變更大小的動作。注意到當視窗改變大小(因為它可以)時,這個 LCD 數字也會跟著改變大小,不過其他的元件仍維持原樣(不然它們看起來會很奇怪)。


Exercises

  試著改變 LCD 數字,增加更多位數或是改變模式(QLCDNumber::setMode())。你甚至可以增加四個按鈕去設定基數(number base)。

  你也可以改變滾動軸的範圍。

  也許使用 QSpinBox 會比使用滾動軸來的好?

  試著讓應用程式在 LCD 數字溢出(overflow)時退出。


來源:Qt Tutorial 5 - Building Blocks
版本:4.4.3

4 回覆:

匿名 提到...

大大你真厲害,解釋很詳細,我有幾個問題還是不太瞭解,可以麻煩你解釋一下嘛^^ 多謝
1. QLCDNumber *lcd = new QLCDNumber(2);
這個為啥要設成2阿? 我有試過1跟3,1不能用,3卻能,這是為啥?

2.
connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
跟之前的
QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));
有啥差別?

3.請問大大是從哪裡學習的,哈,因為我也有興趣學,麻煩大大了,多謝。

小參 提到...

你好,這篇文章是 Qt 的官方文件,不是我寫的
我只是將它翻譯成中文而已:)

1.
這裡的 QLCDNumber 建構子引數設為 2,是代表這個 LCD 數字為兩位數
http://doc.trolltech.com/4.6/qlcdnumber.html#QLCDNumber-2
設為 1 不能用,大概是因為超出一位數字的顯示範圍吧

2.
其實兩者是一樣的
因為範例自訂的 MyWidget 類別是 QWidget 的子類別,而 QWidget 又是 QObject 的子類別
所以在這個範例中的 MyWidget 內部呼叫 connect() 方法,等同於呼叫 QObject 的 connect() 方法

3.
其實我 Qt 不算學得很深XD
要學的話,其實官方文件都有相當詳細的說明跟範例
個人覺得算是相當不錯,你可以試著看一看:
http://doc.trolltech.com/4.6/index.html

匿名 提到...

嗯嗯 感謝大大的幫忙
在此敬上一鞠躬^^

匿名 提到...

為何connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));要加&符號
跟之前的
QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));卻不用加&

張貼留言