W tym przykładzie wprowadzimy rysowanie w pixmapach aby zapobiec migotaniu. dodamy także kontrolę siły.
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 );Interfejs dla słiły posiada tą samą precuzję co dla kąta.
private: QRect cannonRect() const;Musimy umieścić definicję prostokąta zawierającego działo w osobnej funkcji.
int ang; int f; };Siła jest przechowywana w rzeczywistej liczbie f.
#include <qpixmap.h>Dołączamy definicję klasy QPixmap.
CannonField::CannonField( QWidget *parent, const char *name ) : QWidget( parent, name ) { ang = 45; f = 0; setPalette( QPalette( QColor( 250, 250, 200) ) ); }Siła (f) jest inicjalizowana przez zero.
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 ); }Dokonaliśmy małych zmian w funkcji setAngle(). Przemalowuje tylko część widgetu, który zawiera działo. Argument FALSE wskazuje, że prostokąt nie powinien być wymazywany zanim do widgetu zostanie wysłane wydarzenie rysowania. To przyśpiesza i upłynnia malowanie.
void CannonField::setForce( int newton ) { if ( newton < 0 ) newton = 0; if ( f == newton ) return; f = newton; emit forceChanged( f ); }Implementacja setForce() jest dość podobna do setAngle(). Jedyną różnicą jest to, że skoro nie pokazujemy wartości siły, to nie musimy przemalowywać widgetu.
void CannonField::paintEvent( QPaintEvent *e ) { if ( !e->rect().intersects( cannonRect() ) ) return;Zoptymalizowaliśmy wydarzenie rysowania tak, aby przerysowywać te części który wymagają uaktualnienia. Najpierw więc sprawdzamy czy musimy coś przesysowywać, jeżeli nie to wracamy.
QRect cr = cannonRect(); QPixmap pix( cr.size() );Teraz utworzymy tymczasową pixmapę, której użyjemy do rysowania bez migotania. Wszystkie operacje rysownicze będą teraz wykonywane na tej pixmapie, a ona bedzie rysowana na ekranie pojedynczą operacją.
Jest to esencja rysowania be zmigotania: Narysuj każdy piksel dokładnie jeden raz. Mniej i dostaniesz błędy rysowania. Więcej i bedziesz miał migotanie.
pix.fill( this, cr.topLeft() );Wypełniamy pixmapę tłem z tego widgetu.
QPainter p( &pix ); p.setBrush( blue ); p.setPen( NoPen ); p.translate( 0, pix.height() - 1 ); p.drawPie( QRect( -35,-35, 70, 70 ), 0, 90*16 ); p.rotate( -ang ); p.drawRect( QRect(33, -4, 15, 8) ); p.end();Rysujemy, tak jak w rozdziale 9, lecz teraz rysujemy w pixmapie.
W tym punkcie, mamy zmienną rysowniczą i pixmapę, która wygląda dokładnie dobrze, ale ciągle nie rysowaliśmy na ekranie.
p.begin( this ); p.drawPixmap( cr.topLeft(), pix );Tak więc otwieramy rysownika na CannonField itself i rysujemy pixmapę.
To wszystko !
QRect CannonField::cannonRect() const { QRect r( 0, 0, 50, 50 ); r.moveBottomLeft( rect().bottomLeft() ); return r; }Tak funkcja zwraca prostokątne granice zawierające działo w koordynatach widgetów. Najpierw tworzymy Kwadrat 50x50, potem je przesuwamy, tak że lewy dolny brzeg równa się lewemu dolnemu brzegowi widgetu.
Funkcja QWidget::rect() zwraca brzegi prostokąta widge w własnych koordynatach widgetu (gdzie górny lewy róg to 0,0).
MyWidget::MyWidget( QWidget *parent, const char *name ) : QWidget( parent, name ) {Konstruktor jest praktycznie ten sam, lecz dodaliśmy kilka linii.
LCDRange *force = new LCDRange( this, "force" ); force->setRange( 10, 50 );Dodamy drugi LCDRange, który będzie używany dla siły.
connect( force, SIGNAL(valueChanged(int)), cannonField, SLOT(setForce(int)) ); connect( cannonField, SIGNAL(forceChanged(int)), force, SLOT(setValue(int)) );Łączymy widget force z widgetem cannonField zupełnie tak jak to robiliśmy z widgetem angle.
QVBoxLayout *leftBox = new QVBoxLayout; grid->addLayout( leftBox, 1, 0 ); leftBox->addWidget( angle ); leftBox->addWidget( force );W rozdziale 9, umiesciliśmy angle w dolnej lewej komórce. Teraz chcemy umieścić dwa widgety w tej komórce, więc tworzymy pionowy prostokąt, w umieszczamy angle i range w pionowym prostokącie.
force->setValue( 25 );Inicjalizujemy wartość siły przez 25.
Umieść działo w prawym dolnym rogu.
Dodaj lepszą obsługę klawiatury. Na przykład niech + i - zwiększają siłę a enter to strzał. Podpowiedź: QAccel i nowe sloty addStep() i subtractStep() w LCDRange, tak jak w QSlider::addStep().
Możesz teraz przejść do rozdziału jedenastego.
[Poprzedni tutorial] [Następny tutorial] [Główna strona tutoriala]
Copyright (c) 2000 Troll Tech | Znaki towarowe |
Wersja Qt 2.1.0
|