V tomto príklade rozšírime LCDRange tak, aby obsahoval textový popis. Zároveň poskytneme niečo, do čoho sa dá strieľať.
LCDRange má teraz textový popis.
class QLabel;
Deklarujeme meno QLabel, pretože chceme použiť smerník naň v definícii triedy.
class LCDRange : public QWidget { Q_OBJECT public: LCDRange( QWidget *parent=0, const char *name=0 ); LCDRange( const char *s, QWidget *parent=0, const char *name=0 );
Pridali sme nový konštruktor, ktorý nastaví text popisu okrem rodiča a mena.
const char *text() const;
Táto funkcia vracia text popisu.
void setText( const char * );
Tento slot nastaví text popisu.
private: void init();
Pretože teraz máme dva konštruktory, rozhodli sme sa presunúť všeobecnú inicializáciu do privátnej funkcie init().
QLabel *label;
Máme tiež novú privátnu premennú - QLabel. Je to jeden zo štandardných Qt widgetov a môže zobrazovať text alebo pixmapu s alebo bez rámu.
#include <qlabel.h>
Tu vkladáme definíciu triedy QLabel.
LCDRange::LCDRange( QWidget *parent, const char *name ) : QWidget( parent, name ) { init(); }
Tento konštruktor volá funkciu init(), ktorá obsahuje všeobecný inicializačný kód.
LCDRange::LCDRange( const char *s, QWidget *parent, const char *name ) : QWidget( parent, name ) { init(); setText( s ); }
Tento konštruktor najprv volá init(), potom nastaví text popisu.
void LCDRange::init() { lcd = new QLCDNumber( 2, this, "lcd" ); lcd->move( 0, 0 ); sBar = new QScrollBar( 0, 99, // range 1, 10, // line/page steps 0, // inital value QScrollBar::Horizontal, // orientation this, "scrollbar" ); label = new QLabel( this, "label" ); label->setAlignment( AlignCenter ); connect( sBar, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) ); connect( sBar, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) ); }
Nastavenie lcd
a sBar
je rovnaké ako v predošlej
kapitole. Potom vytvoríme QLabel a prikážeme mu zarovnať obsah do stredu
(vertikálne aj horizontálne). Príkazy connect() sú tiež prevzaté z predošlej
kapitoly.
const char *LCDRange::text() const { return label->text(); }
Táto funkcia vracia text popisu.
void LCDRange::setText( const char *s ) { label->setText( s ); }
Táto funkcia nastaví text popisu.
void LCDRange::resizeEvent( QResizeEvent * ) { lcd->resize( width(), height() - 41 - 5 ); sBar->setGeometry( 0, lcd->height() + 5, width(), 16 ); label->setGeometry( 0, lcd->height() + 16 + 5, width(), 20 ); }
Táto obsluha udalosti je podobná na tú v predošlej kapitole, až na to, že musíme urobiť miesto pre popis. Číslo 41 v príkaze resize lcd je výška rolovacej lišty žplus výška nadpisu plus medzera medzi nimi (16+20+5).
CannonField má teraz dva nové signály: hit() a missed(). Okrem toho obsahuje terč.
void newTarget();
Tento slot vytvorí terč na novej pozícii.
signals: void hit(); void missed();
Signál hit() je emitovaný keď strela zasiahne terč. Signál missed() je emitovaný keď sa strela dostane za pravý alebo pod dolný okraj widgetu (t.j. ak nezasiahne terč).
void paintTarget( QPainter * );
Táto privátna funkcia vykreslí terč.
QRect targetRect() const;
Táto privátna funkcia vráti obdĺžnik obklopujúci terč.
QPoint target;
Privátna premenná obsahujúca stredný bod terča.
#include <qdatetime.h>
Vložíme definície tried QDate, QTime a QDateTime.
#include <stdlib.h>
Vložíme knižnicu stdlib, pretože potrebujeme funkciu rand().
newTarget();
Tento riadok sme pridali do konštruktora. Vytvorí "náhodnú" pozíciu pre terč. V skutočnosti funkcia newTarget() skúsi nakresliť terč. Pretože sme v konštruktore, widget CannonField je neviditeľný. Qt garantuje, že sa nestane žiadna škoda, ak kreslíme na skrytý widget.
void CannonField::newTarget() { static bool first_time = TRUE; if ( first_time ) { first_time = FALSE; QTime midnight( 0, 0, 0 ); srand( midnight.secsTo(QTime::currentTime()) ); } erase( targetRect() ); target = QPoint( 200 + rand() % 190, 10 + rand() % 255 ); repaint( targetRect() ); }
Táto privátna funkcia vytvorí stredný bod terča na novej "náhodnej" pozícii.
Používame funkciu rand() na získanie náhodných celých čísel. Funkcia rand() obvykle vracia rovnaké série čísel pri každom spustení programu. To spôsobí, že sa terč bude objavovať vždy na tých istých miestach. Aby sme tomu zabránili, musíme generátor náhodných čísel pred prvým použitím funkcie rand() inicializovať. Inicializačné číslo však musí byť tiež náhodné, aby sme predišli zhodným sériám náhodných čísel. Riešením je použiť ako inicializačné číslo počet sekúnd od polnoci.
Najprv si vytvoríme statickú boolovskú premennú. Pri takýchto (statických) premenných je zaručené, že ich hodnota sa medzi jednotlivými volaniami funkcie nezmení.
Test if
teda uspeje len pri prvom volaní funkcie,
pretože potom už nastavíme premennú first_time
na FALSE
vo vnútri bloku if
.
Potom vytvoríme objekt midnight
triedy QTime,
ktorý reprezentuje čas 00:00:00. Potom zistíme počet sekúnd od polnoci do teraz
a použijeme ho ako inicializačné číslo pre generátor náhodných čísel.
Pre viac informácií si pozrite si dokumentáciu k QDate,
QTime a QDateTime.
Nakoniec vypočítame stredný bod terča. Umiestnime ho do obdĺžnika (x=200,y=35,width=190,height=255), (t.j. možné hodnoty hodnôt x a y sú x = 200..390 a y = 35..290) v systéme súradníc, kde y=0 umiestnime na dolný okraj widgetu a necháme ho rásť smerom zdola nahor. Súradnica x je normálna, s x=0 na ľavom okraji a rastom hodnoty zľava doprava.
Pokusmi sme zistili, že toto je obdĺžnik, v ktorom je terč vždy v dosahu strely.
Všimnite si, že rand() vracia náhodné celé číslo >= 0.
void CannonField::timerEvent( QTimerEvent * ) { erase( shotRect() ); timerCount++; QRect shotR = shotRect();
Táto časť časovača sa od minulej kapitoly nezmenila.
if ( shotR.intersects( targetRect() ) ) { stopShooting(); emit hit(); return; }
Tento príkaz if
kontroluje, či sa obdĺžnik strely
neprekrýva s obdĺžnikom terča. Ak áno, strela trafila terč (aúú!).
Zastavíme streľbu, vyšleme signál hit(), ktorý oznámi okoliu, že
terč bol zničený a ukončíme funkciu.
Všimnite si, že hoci by sme mohli ihneď vytvoriť nový terč, neurobíme to, pretože CannonField je komponent. Rozhodnutie o vytvorení nového terča necháme na požívateľovi komponentu.
if ( shotR.x() > width() || shotR.y() > height() ) { stopShooting(); emit missed(); return; }
Tento príkaz if
je rovnaký ako v predošlej kapitole,
okrem vyslania signálu missed(), ktorý oznámi okoliu, že sme netrafili.
repaint( shotR, FALSE ); }
Bez zmien.
Zmeny v CannonField::paintEvent():
if ( updateR.intersects( targetRect() ) ) paintTarget( &p );
Tieto dva riadky boli do paintEvent() pridané na vykreslenie terča, ak je to potrebné.
void CannonField::paintTarget( QPainter *p ) { p->setBrush( red ); p->setPen( black ); p->drawRect( targetRect() ); }
Táto privátna funkcia nakreslí terč, červený obdĺžnik s čiernym okrajom.
QRect CannonField::targetRect() const { QRect r( 0, 0, 20, 10 ); r.moveCenter( QPoint(target.x(),height() - 1 - target.y()) ); return r; }
Táto privátna funkcia vracia obdĺžnik ohraničujúci terč. Spomente si (z newTarget()),
že bod target
používa y súradnicu na dolnom okraji widgetu. Pred volaním
moveCenter() počítame bod v súradniciach widgetu.
Dôvod, prečo používame toto mapovanie súradníc, je fixovanie vzdialenosti medzi terčom a dolným okrajom widgetu. Pamätajte, že veľkosť widgetu môže byť kedykoľvek zmenená užívateľom alebo programom.
Trieda MyWidget nemá žiadnych nových členov, iba sme trošku zmenili konštruktor, aby sme nastavili textové popisy objektov LCDRange.
angle = new LCDRange( "ANGLE", this, "angle" );
Nastavíme popis objektu angle na "ANGLE" (uhol po anglicky).
force = new LCDRange( "FORCE", this, "force" );
Nastavíme popis objektu force na "FORCE" (sila po anglicky).
Všimnite si, že sme nezmenili pozície ani veľkosti objektov LCDRange. QLCDNumber v LCDRange bude o niečo menšie, aby vznikol priestor pre popis. MyWidget sa o to nestará, pretože používa LCDRange ako komponent, a spolieha sa na to, že zobrazí svoju hodnotu najlepším možným spôsobom.
Kanón strieľa na terč a po zničení terča je automaticky vytvorený nový.
Vytvorte tlačidlo "Cheat", ktorého stlačenie spôsobí, že CannonField zobrazí na 5 sekúnd trajektóriu strely.
Vytvorte pohyblivý terč.
Umožnite mať viac striel vo vzduchu súčasne. Pomôcka: vytvorte objekt Strela.
Teraz môžete ísť na kapitolu trinásť.
[Predchádzajúci tutoriál] [Ďalší tutoriál] [Hlavná stránka tutoriálu]
Copyright © 1998 Troll Tech | Trademarks | Qt version 1.42
|