t12/lcdrange.h
LCDRange現在有標籤了
class QLabel;
我們先聲明QLabel,因為,我們要用到QLabel的指標,我們之前說過,因為,目前還沒有要實作QLabel,我們目前只有使用QLabel的指標,目前,我們不需要包含qlabel.h這一個頭檔,只先宣告class QLabel,可以加速compiler的速度。
class LCDRange : public QVBox
{
Q_OBJECT
public:
LCDRange( QWidget *parent=0, const char *name=0 );
LCDRange( const char *s, QWidget *parent=0, const char *name=0 );
這裡我們宣告另一個建構函數,與原本不同的是,新增的建構函數多了一個標籤。
const char *text() const;
回傳標籤字串
void setText( const char * );
設置標籤字串
private:
void init();
因為我們有兩個建構子,我們選擇把通常的初始化設定在非公開的init函數中
lcdrange.cpp
LCDRange::LCDRange( QWidget *parent, const char *name ) : QVBox( parent, name )
{
init();
}
這一個建構函數使用最基本的初始化,同時,我們把最基本的初始化設定在init函數中
LCDRange::LCDRange( const char *s, QWidget *parent, const char *name ) : QVBox( parent, name )
{
init();
setText( s );
}
這一個建構函數除了使用最基本的初始化之外,還另外設置了標籤的內容
void LCDRange::init()
{
QLCDNumber *lcd = new QLCDNumber( 2, this, "lcd" );
slider = new QSlider( Horizontal, this, "slider" );
slider->setRange( 0, 99 );
slider->setValue( 0 );
label = new QLabel( " ", this, "label" );
label->setAlignment( AlignCenter );
connect( slider, SIGNAL(valueChanged(int)),lcd, SLOT(display(int)) );
connect( slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) );
setFocusProxy( slider );
}
這裡的init與上一章的建構子的內容是一樣的
除了中間增加了一個設置QLabel的部分,並且讓它顯示的字串與中間對齊;但是,QLabel顯示的字串是"",也就是目前是沒有顯示東西啦
const char *LCDRange::text() const
{
return label->text();
}
這一個函數會回傳一個字串指標
void LCDRange::setText( const char *s )
{
label->setText( s );
}
這一個函數就是在設置label的顯示字串
cannon.h
CannonField現在有兩個新的信號:hit()和missed()。另外它還包含一個目標。
void newTarget();
這一個新的slot function生成一個新的目標
signals:
void hit();
void missed();
當射中目標時,就會觸發hit function
當炮彈到達右邊或下方的邊界時,就會觸發missed function
觸發是用emit這一個巨集
void paintTarget( QPainter * );
這一個非公開函數是在畫射擊目標的
QRect targetRect() const;
這一個函數傳回目標的方塊
QPoint target;
這一個非公開變數是以QPoint在記錄目標的中心點座標
cannon.cpp
#include <qdatetime.h>
因為,我們要隨機產生目標的位置,所以,
我們要設定rand的seed,
若沒有設定,或每一次seed都是一樣的話,那產生出來的結果就會一樣
因此透過datetime把seed設定成目前的時間,則每一次跑rand的結果就會不一樣
#include <stdlib.h>
因為我們需要用到rand()的函數
newTarget();
這一個函數是在替我們在一個隨機的位置上建立一個新的目標
並且在建構子上呼叫此函數,
在建構函數視窗元件還是不可見的,QT保証在不可見的視窗函數呼叫repaint是無害的
void CannonField::newTarget()
{
static bool first_time = TRUE;// 當此函數被呼叫第二次時,此行沒有效果
if ( first_time ) {
first_time = FALSE;
QTime midnight( 0, 0, 0 );
srand( midnight.secsTo(QTime::currentTime()) );
}
QRegion r( targetRect() );
target = QPoint( 200 + rand() % 190, 10 + rand() % 255 );
repaint( r.unite( targetRect() ) ); // targetRect()會依target值來改變出現的位置
}
這裡對static關鍵字說明一下,
當類別中的資料成員使用static時,所有此類別的物件的此一變數都是共用同一個記憶體。
另一方面當函數宣告成static時,方法將成為類別的方法
另外,為了隨機產生位置,若不設定隨機種子的話,每次的結果都會是一樣的,
因此,我們必需設定隨機種子,並且,隨機種子的數值也必需每一次都不一樣,
所以,我們利用了時間函數,使得隨機種子的數值是由子時0點目前到秒數,所以,每一次執行的結果會不同。請參考:C++ - rand
並且設定目標的位置是x=200~389和y=35~289
注意rand()返回一個>=0的隨機整數
if ( shotR.intersects( targetRect() ) ) {
autoShootTimer->stop();
emit hit();
檢查炮彈(shotR)是否與目標(targetRect)是否相交,
若相交,則表示擊中,則我們停止計時器,並且觸發hit()
} else if ( shotR.x() > width() || shotR.y() > height() ) {
autoShootTimer->stop();
emit missed();
若離開下面的邊界或右邊的邊界,則表示沒有射中目標,則停止時間計時,並且觸發missed()
if ( updateR.intersects( targetRect() ) )
paintTarget( &p );
這兩行確認目標是否要被重新繪製
void CannonField::paintTarget( QPainter *p )
{
p->setBrush( red );
p->setPen( black );
p->drawRect( targetRect() );
}
繪出紅色有邊框的方塊
QRect CannonField::targetRect() const
{
QRect r( 0, 0, 20, 10 );
r.moveCenter( QPoint(target.x(),height() - 1 - target.y()) );
return r;
}
一開始先建立一個在(0,0)座標的方塊,在移動到由target()計算出來的目標位置
並且回傳這一個方塊
main.cpp
在main.cpp中沒有新的成員,但是,我們在LCDRange class中有新增一個建構子
設置一個新的標籤
LCDRange *angle = new LCDRange( "ANGLE", this, "angle" );
homework:
創建一個作弊的按鈕,當按下它的時候,讓CannonField畫出炮彈在五秒中的軌跡:
建立一個enum SHOOT {shoot_mode,cheat_mode};
這是原本的connect( shoot, SIGNAL(clicked()), cannonField, SLOT(shoot()) );
這是新增的connect(cheat,SIGNAL(clicked()),cannonField,SLOT(cheat_shoot()));
把shoot()的內容全部都移動到新增的function:move()
void CannonField::shoot()
{
mode = shoot_mode;
move();
}
void CannonField::cheat_shoot()
{
mode = cheat_mode;
move();
}
void CannonField::moveShot()
{
QRegion r( shotRect() );
timerCount++;
QRect shotR = shotRect();
if (mode==cheat_mode && timerCount>20) // when the mode equal cheat_mode , then we determine the timerCount bigger than 10
{
autoShootTimer->stop();
}
else if ( shotR.intersects( targetRect() ) ) {
autoShootTimer->stop();
emit hit();
} else if ( shotR.x() > width() || shotR.y() > height() ) {
autoShootTimer->stop();
emit missed();
} else {
r = r.unite( QRegion( shotR ) );
}
repaint( r );
}
沒有留言:
張貼留言