Qt rajzolás munkafüzet Elemi Alkalmazások fejlesztése 3.
1. fejezet
Impresszum Qt rajzolás munkafüzet (C)2006 Zimler Attila Tamás
Visszajelzéseket szivesen fogadok. A munkafüzet online formában a következ® címen található: http://people.inf.elte.hu/hijaszu/qtdrawing/index.html
A dokumentum generálásának dátuma: 2006. május 10.
2
Impresszum
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
2. fejezet
Bevezet® Formátumok class Example { /* Ez egy példa kódrészlet */ }; # Egy parancssori utasítás így néz ki (a # a prompt jel).
függvény() Ez a jelölésmód azonban nem a függvény szignatúra helyes írásmódja! (El®fordulhat, hogy van paramétere a függvénynek, de ez nem kerül kiírásra.)
változó Ez egy változó jelölése. Az állományok jelölése ezen a módon lesz megvalósítva.
Megjegyzések El®fordulhat, hogy a Qt környezet speciális beállításokat igényel az adott gépen, a .pro
állományban.
Ehhez további segítség: http://people.inf.elte.hu/hijaszu/eaf3_exercise3.html . Amennyiben így kell használni a Qt-t, akkor az összes "qmake -project" parancs kiadása után újra be kell állítani ezeket a speciális dolgokat a .pro állományban.
A hosszabb programrészletekben az alábbi formátumú megjegyzések lesznek, ezeknek a hely-
zete nem változik, miután belekerültek a kódba. (Tehát ezekhez képest lehet viszonyítani, hogy milyen kódot hová kell írni, és ezért a kód változatlan részletei elhagyhatóak.) /* határolósor */
A harmadik feladatsortól kezdve mindegyik feladat után le lehet fordítani a programot és
meg lehet nézni a futását (ezért nincs is már odaírva a feladatok megoldásához, hogy tegyük ezt meg). A fordítást a következ® paranccsal tehetjük meg: # make
4
Bevezet®
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
3. fejezet
A Qt grakai rendszere A Qt grakai rendszere több komponens együttm¶ködéséb®l áll. A graka során nem közvetlenül a rajzfelületre rajzolunk, hanem egy logikai térbe. A logikai tér a rajzfelületre több lépcs®ben képz®dik, nem feltétlenül az alábbi sorrendben (viszont az eredmény ugyanaz lesz). 1. A logikai tér koordinátái átalakításra kerülnek, így tetsz®leges helyre helyezhetjük a logikai térben az origót. 2. A logikai tér egy transzformációs mátrix segítségével átalakításra kerül, így az ábrát kirajzolás el®tt forgathatjuk és eltolhatjuk.
3.1. ábra. A Qt rajzolási rendszere. Ennek a rendszernek több el®nye is van: Fix koordinátákat rakhatunk a rajzunkba, és az ablak átméretezésénél ez nem fog problémát
okozni.
Eltolhatjuk a rajz közepére az origót, ha az nekünk úgy kényelmes. Elforgathatjuk (akár többször is) a rajzfelületet a rajzolás el®tt. Ebb®l következik, hogy
egy kényelmetlen szögben rajzolás helyett, bármilyen állapotban megrajzolhatjuk az ábrát. Ezután elforgatva az ábrát az eredmény az lesz, mintha a kényelmetlen szögben rajzoltunk volna (persze nehézkes számítások nélkül).
A leképezésnek a logikai és a zikai rajzfelület között nem szükséges, hogy vízszintes és
függ®leges arányban is ugyanolyan arányú legyen.
6
A Qt grakai rendszere
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
4. fejezet
A feladatsorozat célja A munkafüzetben a feladatsorok folyamán egy grakus óra programot fogunk létrehozni. A program a következ® tulajdonságokkal fog rendelkezni: Az aktuális id®t fogja mutatni a program. Az ablak átméretezésekor arányosan nagyítjuk a rajzot. Kiküszöböljük a villogást az ábrákon.
4.1. ábra. A cél
8
A feladatsorozat célja
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
5. fejezet
Alap Qt program létrehozása 1. Hozzunk létre egy main() függvényt és fordítsuk le!
Tipp
Ne felejtsük el, hogy a Qt alkalmazás elindításához a main() függvény két paraméteres formájára lesz szükségünk.
Megoldás
(a) Hozzuk létre az alábbi tartalmú main.cpp állományt: int main(int argc, char* argv[]) { }
(b) Állítsuk el® a qmake .pro állományát: # qmake -project
(c) Fordítsuk le a programot: # qmake # make
2. Hozzunk létre egy f®ablakot és jelenítsük meg a main() függvényb®l! (A f®ablak osztályának a munkafüzet a MainWnd nevet adja.)
Tippek
(a) A következ® fejléc állományokra lesz szükség: qmainwindow.h: a f®ablak származtatásához. qapplication.h: a Qt f®program létrehozásához. (b) A f®ablak osztályának deklarációját (.h) és denícióját (.cpp) mindenképpen két külön állományba kell rakni, mert különben a qmake nem fog rendesen m¶ködni, ha a meta object compiler-t (moc) kell futtatnia. (c) Ne felejtsük el a Q_OBJECT makrót a f®ablak osztályából. (d) Jelenleg elég ha a f®ablak csak egy üres konstruktort tartalmaz. (e) Könnyen elérhetjük, hogy a f®ablak becsukására a program megálljon, ha használjuk a QApplication::setMainWidget() metódusát. (f) Mivel extra állományokat adtunk a projekthez, ezért a projekt állományt aktuálizálni kell. Ezt két módon tehetjük meg:
10
Alap Qt program létrehozása HEADERS konstanshoz hozzáadjuk az új fejléc állományokat, a SOURCES konstanshoz hozzáadjuk az új .cpp állományokat. Amennyiben bármelyik nem létezik, létrehozzuk. Újrageneráljuk a .pro állományt:
a
# rm -f *.pro # qmake -project
Megjegyzés:
Ne felejtsük el a Makele állományt újragenerálni. # qmake
Megoldás (a) Hozzuk létre az alábbi tartalmú mainwnd.h állományt: #ifndef __MAINWND_H__ #define __MAINWND_H__ #include class MainWnd : public QMainWindow { Q_OBJECT public:
MainWnd(); }; #endif /* __MAINWND_H__ */
(b) Hozzuk létre az alábbi tartalmú mainwnd.cpp állományt: #include "mainwnd.h" MainWnd::MainWnd() { }
(c) A main.cpp állományt módosítsuk, hogy a következ®képpen nézzen ki: #include "mainwnd.h" #include int main(int argc, char* argv[]) { QApplication app(argc, argv); MainWnd mw; mw.show(); app.setMainWidget(&mw); app.exec(); }
(d) Generáljuk újra a .pro állományt: # rm -f *.pro # qmake -project
(e) Fordítsuk le a programot. # qmake # make
3. Hozzunk létre a QWidget osztályból származtatással egy egyedi widget-et és azt helyezzük el a f®ablakon! (Ez az osztály a munkafüzetben a Clock nevet viseli.)
Tippek
(a) Ne felejtsük el, hogy a QWidget nem rendelkezik nulla paraméterszámú konstruktorral, hanem alapértelmezett értékei vannak a konstruktorának. Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
11 (b) A f®ablak adattagjai közé mutató formában vegyük fel az újonnan létrehozott widget típust. (c) A QMainWindow::setCentralWidget() metódus segítségével rakhatjuk a f®ablakra az objektumot. (d) Ne felejtsük el a f®ablakot kiegészíteni egy destruktorral, ahol az egyedi widget objektumot megsemmisítjük. (e) Ne felejtsük el a Q_OBJECT makrót. (f) Ne felejtsük el módosítani a .pro állományt.
Megoldás (a) Hozzuk létre az alábbi clock.h állományt: #ifndef __CLOCK_H__ #define __CLOCK_H__ #include class Clock : public QWidget { Q_OBJECT public:
Clock(QWidget* parent = 0, const char* name = 0, WFlags f = 0); }; #endif /* __CLOCK_H__ */
(b) Hozzuk létre az alábbi clock.cpp állományt: #include "clock.h" Clock::Clock(QWidget* parent, const char* name, WFlags f) : QWidget(parent, name, f) { }
(c) Vegyünk fel egy privát _clock
adattagot a MainWnd osztályba és szerkesszük be a clock.h fejlécállományt. Ennek eredményeképpen a következ®képpen kell kinéznie a mainwnd.h állománynak: #ifndef __MAINWND_H__ #define __MAINWND_H__ #include #include "clock.h" class MainWnd : public QMainWindow { Q_OBJECT Clock* _clock; public:
MainWnd(); ~MainWnd();
}; #endif /* __MAINWND_H__ */
(d) Helyezzük a f®ablakra az objektumot. Ehhez módosítsuk a MainWnd osztály konstruktorát: MainWnd::MainWnd() { setCentralWidget(_clock = new Clock(this)); } Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
12
Alap Qt program létrehozása (e) Írjuk meg a MainWnd osztály 3. pontjában már bevezetett destruktorát: MainWnd::~MainWnd() { delete _clock; }
(f) Generáljuk újra a .pro állományt és fordítsuk le a programot: # # # #
rm -f *.pro qmake -project qmake make
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
6. fejezet
A rajzolást végz® kód el®készítése 1. Hozzunk létre egy _draw() metódust a Clock osztály számára!
Megoldás
(a) Módosítsuk a clock.h állományt, hogy a következ®képpen nézzen ki: #ifndef __CLOCK_H__ #define __CLOCK_H__ #include #include
// új sor
class Clock : public QWidget { Q_OBJECT void _draw(QPainter* painter);
// új sor
public:
Clock(QWidget* parent = 0, const char* name = 0, WFlags f = 0); }; #endif /* __CLOCK_H__ */
(b) A clock.cpp állományban hozzuk létre a következ® metódust _draw() metódust: void Clock::_draw(QPainter* painter) { /* Változók deklarálása */ /* Az alap megrajzolása */ /* A vonalak megrajzolása */ /* A számok elhelyezése az óralapon */ /* A nagymutató megrajzolása */ /* A kismutató megrajzolása */ /* A másodperc mutató megrajzolása */ /* Újabb frissítés ütemezése */ }
Megjegyzés: A _draw() implementációját a következ® fejezetekben adjuk meg, most csak a
gerincét hozzuk létre. (c) Fordítsuk le a programot. # make
2. Deniáljuk felül a Clock osztályban a QWidget::paintEvent() metódusát, és ebb®l hívjuk meg a _draw() metódust!
Tippek
14
A rajzolást végz® kód el®készítése (a) (b) (c) (d)
Hozzuk létre a QPainter objektumot. Válasszuk ki a kisebbik méretet, hogy arányosan tudjuk megjeleníteni a rajzot. Állítsuk be a koordinátarendszert. Hívjuk meg a rajzolást végz® rutint (_draw()).
Megoldás (a) Módosítsuk a clock.h állományt úgy, hogy a következ®képpen nézzen ki: #ifndef __CLOCK_H__ #define __CLOCK_H__ #include #include class Clock : public QWidget { Q_OBJECT void _draw(QPainter* painter); void paintEvent(QPaintEvent* event);
// új sor
public:
Clock(QWidget* parent = 0, const char* name = 0, WFlags f = 0); }; #endif /* __CLOCK_H__ */
(b) Valósítsuk meg a következ® Clock::paintEvent() metódust: void Clock::paintEvent(QPaintEvent*) { /* Kett®s pufferelés el®készítés */ /* A QPainter objektum létrehozása */ QPainter painter(this); /* A rajzoló rutin meghívása */ int side = QMIN(width(), height()); painter.setViewport( (width() - side) / 2, (height() - side) / 2, side, side ); painter.setWindow(-50, -50, 101, 101); _draw(&painter); }
/* A kett®s pufferelés ábrájának kihelyezése */
(c) Fordítsuk le a kódot: # make
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
7. fejezet
A rajzolást megvalósító kód 1. Rajzoljuk meg az óra alapját!
Tippek
(a) Állítsuk be a ceruzát fehérre. (b) Állítsuk be a kitöltést fehérre. (c) Rajzoljuk meg a kört a QPainter::drawEllipse() metódussal.
Megoldás (a) Írjuk át a Clock::_draw() metódus fejlécét a clock.cpp állományban, hogy a következ® módon nézzen ki: void Clock::_draw(QPainter* painter)
(b) Adjuk hozzá a Clock::_draw() metódushoz a következ® részletet: ... /* Az alap megrajzolása */ painter->setPen(QPen(white, 1, SolidLine)); painter->setBrush(QBrush(white, SolidPattern)); painter->drawEllipse(-50, -50, 101, 101); /* A vonalak megrajzolása */ ...
2. Húzzuk meg a 12 óra értékhez tartozó függ®leges vonalat!
Tippek
(a) Állítsuk a ceruzát feketére. (b) Húzzuk meg a vonalat a QPainter::drawLine() metódussal.
Megoldás
Illesszük a következ® kódot a Clock::_draw() metódusba: ... /* A vonalak megrajzolása */ painter->setPen(QPen(black, 1, SolidLine)); painter->drawLine(0, -50, 0, -47); /* A számok elhelyezése az óralapon */ ...
16
A rajzolást megvalósító kód 3. Rajzoljuk meg az összes vonalat!
Tippek
(a) Vegyük fel egy QWMatrix típusú objektumot a Clock::_draw() metódusba. (b) Ismételjük meg 60-szor a következ® három lépést: i. Húzzunk egy vonalat, ahogy azt az el®z® feladatban tettük (pontosan ugyanazokkal a koordinátákkal). ii. Forgassuk a QWMatrix objektumot 6 fokkal. iii. Állítsuk be a módosított QWMatrix objektum segítségével a QPainter worldMatrix-át ( QPainter::setWorldMatrix()).
Megoldás (a) Vegyünk fel egy QWMatrix típusú mátrixot a Clock::_draw() metódusba: ... /* Változók deklarálása */ QWMatrix matrix; /* Az alap megrajzolása */ ...
(b) Alakítsuk át a vonalak rajzolását a következ®re: ... /* A vonalak megrajzolása */ for (int i = 0; i < 60; ++i) { painter->setPen(QPen(black, 1, SolidLine)); painter->drawLine(0, -50, 0, -47); matrix.rotate(6); painter->setWorldMatrix(matrix); } /* A számok elhelyezése az óralapon */ ...
4. Legyen minden ötödik vonal vastagabb és hosszabb!
Megoldás
Alakítsuk át a Clock::_draw() metódusát a következ® módon: ... /* A vonalak megrajzolása */ for (int i = 0; i < 60; ++i) { // vastag vagy vékony vonal kell? painter->setPen(QPen(black, i % 5 == 0? 3 : 0, SolidLine)); // rövid vagy hosszú vonal kell? painter->drawLine(0, -50, 0, i % 5 == 0? -45 : -47);
}
// 6 fokonként egy-egy vonal, összesen 60 vonal. matrix.rotate(6); painter->setWorldMatrix(matrix);
/* A számok elhelyezése az óralapon */ ...
5. Helyezzük el a 12-es feliratot az óralapon!
Tippek
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
17 (a) A QPainter::drawText() metódusai között van egy olyan, amelynek meg lehet adni a befoglaló négyzeten belül a szöveg igazitását. (b) A QString típusnak van egy setNum() metódusa, aminek segíségével egy számot könnyen szöveggé lehet alakítani. (c) Körülményes végiggondolni, hogy az el®z® feladat megoldása során a QPainter worldMatrix tulajdonsága milyen állapotban maradt. Egyszer¶bb lehet alaphelyzetbe állítani.
Megoldás Alakítsuk a következ®re a Clock::_draw() metódust: ... /* A számok elhelyezése az óralapon */ QString text; matrix.reset(); painter->setWorldMatrix(matrix); text.setNum(12); painter->setPen(QPen(blue, 0, SolidLine)); painter->drawText( -10, -45, 20, 20, Qt::AlignHCenter | Qt::AlignVCenter, text ); /* A nagymutató megrajzolása */ ...
6. Helyezzük fel az összes számot!
Tipp A QWMatrix::map() metódus segítségével koordinátákat transzformálhatunk a logikai
térben.
Megoldás A Clock::_draw() megfelel® részlete: ... /* A számok elhelyezése az óralapon */ QString text; matrix.reset(); painter->setPen(QPen(blue, 0, SolidLine)); painter->setWorldMatrix(matrix); const int cx = 0; const int cy = -35; const int tw = 10; const int th = 10; for (int i = 3; i < 13; i += 3) { int tcx, tcy;
}
// // // //
x y A A
irányú irányú szöveg szöveg
közepe a szövegnek közepe a szövegnek szélessége (valójában / 2) magassága (valójában / 2)
// átalakított cx, cy
matrix.rotate(90); matrix.map(cx, cy, &tcx, &tcy); text.setNum(i); painter->drawText( tcx - tw, tcy - th, tw * 2, th * 2, Qt::AlignHCenter | Qt::AlignVCenter, text );
/* A nagymutató megrajzolása */ ... Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
18
A rajzolást megvalósító kód 7. Hozzunk létre a Clock osztály számára egy _drawArrow() metódust, amely paraméterben megkapja a QPainter objektumot és a mutató hosszát! Ezután rajzoljuk meg a mutatót és a mutató hegyét!
Megoldás
(a) Az új clock.h állomány tartalma: #ifndef __CLOCK_H__ #define __CLOCK_H__ #include #include class Clock : public QWidget { Q_OBJECT void _draw(QPainter* painter); void _drawArrow(QPainter* painter, int length);
// új sor
void paintEvent(QPaintEvent* event); public:
Clock(QWidget* parent = 0, const char* name = 0, WFlags f = 0); }; #endif /* __CLOCK_H__ */
(b) A Clock::_drawArrow() metódus: void Clock::_drawArrow(QPainter* painter, int length) { /* A ceruza és az ecset beállítása */ painter->setPen(QPen(black, 1, SolidLine)); painter->setBrush(QBrush(black, SolidPattern)); /* A mutató hegyének megrajzolása */ QCOORD triangle[3][2] = { { -3, -length + 10 }, { 0, -length }, { 3, -length + 10 } }; painter->drawConvexPolygon(QPointArray(3, &triangle[0][0])); /* A mutató szárának megrajzolása */
}
(c) A Clock::_draw() metódus szükséges módosítása: ... /* A nagymutató megrajzolása */ matrix.reset(); painter->setWorldMatrix(matrix); _drawArrow(painter, 45); /* A kismutató megrajzolása */ ...
8. Hozzuk létre a mutató szárát.
Tipp
Rajzoljuk meg a kört a mutató szárának végére, hogy szépen nézzen ki.
Megoldás
Alakítsuk át a következ®re a Clock::_drawArrow() metódust: ... /* A mutató szárának megrajzolása */ QCOORD box[4][2] = { { -1, -length + 10 }, { 1, -length + 10 }, { 1, 0 }, { -1, 0 } Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
19
}
}; painter->setPen(QPen(black, 1, SolidLine)); painter->setBrush(QBrush(black, SolidPattern)); painter->drawConvexPolygon(QPointArray(4, &box[0][0])); painter->drawEllipse(-1, -1, 3, 3);
9. Rajzoljuk meg a kismutatót.
Megoldás A Clock::_draw() metódust kell módosítani: ... /* A kismutató megrajzolása */ matrix.reset(); painter->setWorldMatrix(matrix); _drawArrow(painter, 30); /* A másodperc mutató megrajzolása */ ...
10. Rajzoljuk meg a másodperc mutatót.
Megoldás A Clock::_draw() metódusának szükséges módosítása: ... /* A másodperc mutató megrajzolása */ matrix.reset(); painter->setWorldMatrix(matrix); painter->setPen(QPen(red, 1, SolidLine)); painter->drawLine(0, -45, 0, 0); /* Újabb frissítés ütemezése */ ...
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
20
A rajzolást megvalósító kód
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
8. fejezet
Az aktuális id®t beállító kód 1. Valósítsuk meg a nagymutató beállítását!
Tippek
(a) A worldMatrix forgatásával nagyon könnyen megfelel® irányba lehet állítani a mutatót. (b) A QTime::currentTime() metódusával le lehet kérdezni a megfelel® id®t.
Megoldás (a) Szerkesszük be a qdatetime.h fejlécet a clock.cpp-be : #include
(b) Módosítsuk a következ® két részletet a Clock::_draw() metódusban: ... /* Változók deklarálása */ QWMatrix matrix; QTime t = QTime::currentTime(); /* Az alap megrajzolása */ ... ... /* A nagymutató megrajzolása */ matrix.reset(); matrix.rotate(t.minute() * 6); painter->setWorldMatrix(matrix); _drawArrow(painter, 45);
// új sor
/* A kismutató megrajzolása */ ...
2. Valósítsuk meg óra és a másodperc mutató beállítását!
Megoldás
Szükséges módosítás a Clock::_draw() állományban. ... /* A kismutató megrajzolása */ matrix.reset(); matrix.rotate((t.hour() * 60 + t.minute()) / 2); painter->setWorldMatrix(matrix); _drawArrow(painter, 30); /* A másodperc mutató megrajzolása */
// új sor
22
Az aktuális id®t beállító kód matrix.reset(); matrix.rotate(t.second() * 6); painter->setWorldMatrix(matrix); painter->setPen(QPen(red, 1, SolidLine)); painter->drawLine(0, -45, 0, 0);
// új sor
/* Újabb frissítés ütemezése */ ...
3. Kapcsoljuk egy id®zít®re az újrarajzolást!
Tippek
(a) Vegyünk fel egy QTimer adattagot a Clock osztályba. (b) A rajzolás végeztével egy egyszeri lefutású id®zít®t indítsunk 1 másodperces késleltetéssel. Hogy miért? Mert így amikor a rendszer újrarajzolja az objektumot, akkor elölr®l kezd®dik az egy másodperc mérése. (c) Az id®zít® lejártát az update() metódusra kapcsolhatjuk. (d) Amennyiben új adattagot veszünk fel az osztályba, akkor a qmake által generált Makele ezt nem tudja teljesen helyesen értékelni. # make distclean # make
Megoldás (a) A clock.h új tartalma: #ifndef __CLOCK_H__ #define __CLOCK_H__ #include #include #include class Clock : public QWidget { Q_OBJECT QTimer timer;
// új sor
void _draw(QPainter* painter); void _drawArrow(QPainter* painter, int length); void paintEvent(QPaintEvent* event); public:
Clock(QWidget* parent = 0, const char* name = 0, WFlags f = 0); }; #endif /* __CLOCK_H__ */
(b) A szükséges módosítás a Clock::_draw() metódusban:
}
... /* Újabb frissítés ütemezése */ connect(&timer, SIGNAL(timeout()), this, SLOT(update())); timer.start(1000, true);
(c) Amennyiben új adattagot veszünk fel az osztályba, akkor a qmake által generált Makele ezt nem tudja teljesen helyesen értékelni. # make distclean # make
4. Indítsuk el a programot, nagyítsuk fel teljes képerny®sre és gyeljük meg, ahogy villog. Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
9. fejezet
A villogás kiküszöbölése kett®s puerelés (double buering) segítségével Mi is az a kett®s puerelés? A villogást a rajz folyamatos újrarajzolása során a számítások viszonylagos lassúsága okozza. (Ez a számítás a képerny® frissítéséhez képest értend®.) A kett®s puerelés ezt úgy oldja meg, hogy nem közvetlenül a képerny® memóriaterületére rajzolunk. Amikor elkészülünk a rajzzal, akkor egy nagyon gyors rutinnal kicseréljük a képerny®n látható rajzot a háttérben elkészülttel. Az, hogy ez hogy történik a rendszer ügyességén és hardverén múlik: Ha a grakus kártya rendelkezik kell®en sok memóriával, és egy egész képerny®nyi képr®l
van szó, akkor egy egyszer¶ memóriacím átírással meg lehet ezt oldani.
A DMA segítségével a CPU-tól függetlenül lehet adatot mozgatni (közben a CPU számítá-
sokat végezhet).
Megjegyzés:
Ez a feladatsor csak a végén ad vizsgálható eredményt, függetlenül attól, hogy mindegyik lépés után lefordítható.
1. Emlékeztet® az el®z® feladatsor végér®l: Indítsuk el a programot, nagyítsuk fel teljes képerny®sre és gyeljük meg, ahogy villog. 2. Hozzunk létre egy képet, majd méretezzük az aktuális objektumnak megfelel® méret¶re! (Szükség lesz ehhez arra is, hogy a Clock::paintEvent() paraméterét nevesítsük, hogy elérjük.)
Megoldás
(a) A qpixmap.h fejlécet be kell szerkeszteni a clock.cpp állományba. (b) A Clock::paintEvent() megfelel® részlete: void Clock::paintEvent(QPaintEvent* event) { /* Kett®s pufferelés el®készítés */ static QPixmap pixmap;
// módosított sor!
24
A villogás kiküszöbölése kett®s puerelés (double buering) segítségével QRect rect = event->rect(); QSize newSize = rect.size().expandedTo(pixmap.size()); pixmap.resize(newSize); pixmap.fill(this, rect.topLeft()); /* A QPainter objektum létrehozása */ ...
3. Módosítsuk a QPainter objektum létrehozását, hogy a QPixmapre rajzoljon!
Megoldás A Clock::paintEvent() metódus megfelel® részlete:
... /* A QPainter objektum létrehozása */ QPainter painter(&pixmap, this);
// módosított sor!
/* A rajzoló rutin meghívása */ ...
4. A kett®s puerelés képét helyezzük ki az objektumra!
Megoldás A Clock::paintEvent() metódus vége:
}
... /* A kett®s pufferelés ábrájának kihelyezése */ bitBlt(this, event->rect().topLeft(), &pixmap);
5. Vegyük át a QWidget-t®l az objektum teljes rajzolását (kapcsoljuk ki az automatikus újrarajzolási eseményeket)!
Megjegyzés:
Erre azért van szükség, mert különben automatikusan letörli az objektumot a rendszer.
Megoldás
Módosítsuk a Clock::Clock() konstruktorát, hogy a következ® módon nézzen ki: Clock::Clock(QWidget* parent, const char* name, WFlags f) : QWidget(parent, name, f | Qt::WNoAutoErase) { }
6. Fordítsuk le a kódot és gyeljük meg, hogyha odébb mozgatjuk az ablakot, akkor egy másodpercig szétesik a kép! 7. Oldjuk meg, hogy ha megmozdítjuk az ablakot, akkor se essen szét a kép!
Tipp A QMainWindow::moveEvent() metódusát kell felüldeniálni. Megoldás (a) A mainwnd.h új verziója: #ifndef __MAINWND_H__ #define __MAINWND_H__ #include #include "clock.h" class MainWnd : public QMainWindow {
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás
25 Q_OBJECT Clock* _clock; public:
void moveEvent(QMoveEvent*);
// új sor
MainWnd(); ~MainWnd();
}; #endif /* __MAINWND_H__ */
(b) És a MainWnd::moveEvent() metódus megvalósítása: void MainWnd::moveEvent(QMoveEvent*) { _clock->update(); }
Qt rajzolás munkafüzet, (C)2006 Zimler Attila Tamás