V tomto príklade uvádzame kreslenie na pixmapu kvôli odstráneniu blikania. Pridávame aj riadenie sily.
CannonField obsahuje teraz okrem hodnoty uhla aj hodnotu sily (force).
int angle() const { return ang; } int force() const { return f; } public slots: void setAngle( int degrees ); void setForce( int newton ); signals: void angleChanged( int ); void forceChanged( int );
Interface pre silu je rovnaký ako pre uhol.
private: void paintCannon( QPainter * ); QRect cannonRect() const;
Presunuli sme vykreslenie kanóna do osobitnej funkcie a vytvorili sme aj aj osobitnú funkciu vracajúcu definíciu obdĺžnika obklopujúceho kanón.
int ang; int f; };
Sila je uložená v celočíselnej premennej f.
#include <qpixmap.h>
Vložíme definíciu triedy QPixmap.
CannonField::CannonField( QWidget *parent, const char *name ) : QWidget( parent, name ) { ang = 45; f = 0; }
Sila je inicializovaná na nulu.
void CannonField::setAngle( int degrees ) { if ( degrees < 5 ) degrees = 5; if ( degrees > 70 ) degrees = 70; if ( ang == degrees ) return; ang = degrees; repaint( cannonRect(), FALSE ); emit angleChanged( ang ); }
Urobili sme drobnú zmenu vo funkcii setAngle(). Teraz prekreslí len tú časť widgetu, ktorá obsahuje kanón. Argument FALSE indikuje, že určený obdĺžnik nemá byť vymazaný pred odoslaním udalosti prekreslenia widgetu.
void CannonField::setForce( int newton ) { if ( newton < 0 ) newton = 0; if ( f == newton ) return; f = newton; emit forceChanged( f ); }
Implementácia funkcie setForce() je veľmi podobná na setAngle(). Jediný rozdiel je, že nezobrazujeme hodnotu sily, takže nepotrebujeme prekresliť widget.
void CannonField::paintEvent( QPaintEvent *e ) { QRect updateR = e->rect(); QPainter p; p.begin( this ); if ( updateR.intersects( cannonRect() ) ) paintCannon( &p ); p.end(); }
Tu sme optimalizovali udalosť prekreslenia tak, aby prekresľovala len tie časti widgetu, ktoré potrebujú byť prekreslené. Najprv z QPaintEvent získame parametre obdĺžnika na prekreslenie. Potom začneme kresliť na widget. Skontrolujeme, či sa obdĺžnik na prekreslenie prekrýva s obdĺžnikom kanóna. Ak áno, prekreslíme kanón. Všimnite si, že funkcii paintCannon() posielame smerník na painter.
const QRect barrel_rect(33, -4, 15, 8);
Rozhodli sme sa definovať obdĺžnik hlavne kanóna ako konštantu.
void CannonField::paintCannon( QPainter *p ) { QRect cr = cannonRect();
Najprv získame rozmery obdĺžnika obklopujúceho kanón a uložíme ho do premennej cr.
QPixmap pix( cr.size() );
Tu sa stretávame s novou triedou QPixmap. QPixmap je mimoobrazovkové kresliace zariadenie vhodné pre kreslenie bez blikania. Tu vytvoríme pixmapu s rovnakou veľkosťou ako kanón. Chceme nakresliť kanón na túto pixmapu a potom presunúť pixmapu na obrazovku jedinou operáciou. Táto technika sa volá double-buffering (dvojité bufferovanie) a virtuálne odstraňuje blikanie.
QPainter tmp;
Tvoríme dočasný painter tmp
na kreslenie do pixmapy.
pix.fill( this, cr.topLeft() );
Po vytvorení je pixmapa vyplnená náhodnými pixelmi, preto ju najprv vyplníme farbou pozadia widgetu. Musíme určiť pozíciu, kde bude pixmapa umiestnená vo widgete, takže si budeme vedieť poradiť s akýmkoľvek typom pozadia (napríklad pixmapou pozadia (background pixmaps)).
tmp.begin( &pix ); tmp.setBrush( blue ); tmp.setPen( NoPen );
Tu začíname kresliť na pixmapu. Nastavíme modrý štetec a neviditeľné pero, tak ako v predchádzajúcej kapitole. Tentokrát používame skratku. setPen() a setBrush() sú preťažené (overloaded) funkcie v triede QPainter.
tmp.translate( 0, pix.height() - 1 ); tmp.drawPie( QRect( -35,-35, 70, 70 ), 0, 90*16 ); tmp.rotate( -ang ); tmp.drawRect( barrel_rect ); tmp.end();
Tento kó kreslenia kanóna je veľmi podobný na kód z predošlého príkladu. Jediný rozdiel je posunutie, ktoré je teraz relatívne k výške pixmapy, a že obdĺžnik hlavne je konštanta.
p->drawPixmap( cr.topLeft(), pix );
Nakoniec zavoláme funkciu drawPixmap(), ktorá umiestni pixmapu na obrazovku jednou bleskurýchlou operáciou.
QRect CannonField::cannonRect() const { QRect r( 0, 0, 50, 50 ); r.moveBottomLeft( rect().bottomLeft() ); return r; }
Táto funkcia vracia obdĺžnik obklopujúci kanón so súradnicami podľa widgetu. Najprv vytvoríme obdĺžnik s veľkosťou 50x50, potom umiestnime jeho ľavý dolný roh do ľavého dolného rohu widgetu.
Funkcia rect() vracia obdĺžnik obklopujúci widget v súradniciach widgetu (napr. ľavý horný roh je 0,0).
class MyWidget : public QWidget { public: MyWidget( QWidget *parent=0, const char *name=0 ); protected: void resizeEvent( QResizeEvent * ); private: QPushButton *quit; LCDRange *angle; LCDRange *force; CannonField *cannonField; };
Pridávame druhý LCDRange, ktorý bude nastavovať silu.
force = new LCDRange( this, "force" ); force->setRange( 10, 50 ); force->setGeometry( 10, angle->y() + angle->height() + 10, 75, 130 );
Umiestnime LCDRange widget force
pod widget angle
a nastavíme rozsah na 10..50.
connect( force,SIGNAL(valueChanged(int)), cannonField,SLOT(setForce(int))); connect( cannonField,SIGNAL(forceChanged(int)), force,SLOT(setValue(int)));
Spojíme widget force
a widget cannonField
tak ako sme to urobili s widgetom angle
.
force->setValue( 25 );
Inicializujeme hodnotu sily na 25.
Blikanie zmizlo a máme ovládanie sily.
Upravte program tak, aby veľkosť hlavne kanóna bola závislá od sily.
Umiestnite kanón do pravého dolného rohu.
Teraz môžete ísť na kapitolu jedenásť.
[Predchádzajúci tutoriál] [Ďalší tutoriál] [Hlavná stránka tutoriálu]
Copyright © 1998 Troll Tech | Trademarks | Qt version 1.42
|