Download as pdf or txt
Download as pdf or txt
You are on page 1of 148

Varga Pter

Qt strandknyv
tfog bevezets a Qt keretrendszer hasznlatba
Varga Pter:
Qt strandknyv
Elektronikus kiads
Kiad: E-Kzigazgatsi Szabad Szoftver Kompetencia Kzpont, 2013
ISBN 978-963-08-8255-2

Szerzi jog
Ez a knyv a Creatitve Commons Atribution-ShareAlike 3.0 Unported (CC-BY-SA 3.0) licenc
szerint szabadon terjeszthet s mdosthat. Tovbbi informcik: htp://creatitvecommons.org/
licenses/by-sa/3.0/
A dokumentumban tallhat sszes vdjegy a jogos tulajdonosait illeti meg.
Kiadva: 2013. december 17.
Tartalomjegyzk Qt strandknyv

Tartalom
Bevezets.................................... ................................................................... 6

Mirt pont Qt?........................... ................................................................... 7

Nhny egyb tudnival.. ........ ................................................................... 8

1. Pr sz az esemnyvezrelt programokrl. ...................................... 9

2. Els programunk. Igen, a Hell vilg!, de kicsit bvebben. ....... 10


2.1. A grafikus objektumok s a Property Editor. .................................................. 10
2.2. A signals and slots mechanizmus................................................................. 10
2.3. Elg a kattintgatsbl!............................................................................... 11

3. Sajt signal s slot megvalstsa


meg egy kis QDebug s QMap. .............................................................. 13
3.1. Elszr slot-ot ksztnk.......... .................................................................. 13
3.2. A QMap s a ksznsek. . ........ .................................................................. 15
3.3. Most pedig signal-t runk. . ....... .................................................................. 16

4. Akinek nincs esze.... . ............................................................................. 18


4.1. A QListWidget sznre lp, s megismerkednk az elrendezsek fontossgval......... 18
4.2. A QListWidget elemeinek manipulcija. ...................................................... 20

5. Mentsk, ami menthet!. . .................................................................... 24


5.1. QStringList a feladatok tadsra. ............................................................... 25
5.2. Menk s QAction-k............................................................................... 25
5.3. A FileOperator osztlyunk QFileDialog, QMessageBox s egyb veszedelmek. ..... 25
5.4. Itertor la STL s la Java na s a foreach.................................................. 32

6. A feladatokhoz kategrikat rendelnk..................................... 34


6.1. A Task osztly. . ....................................................................................... 34
6.2. A QList<T> sablonosztly, s nmi rlet a mutatkkal. Megismerkednk a
QSharedPointer osztllyal. . ...... .................................................................. 34
6.3. A ToDoList-et felksztjk a kategrik hasznlatra, de mg nem hasznlunk
kategrikat........................................................................................... 36
6.4. Kategrik kltznek a ToDoList-be............................................................. 39

7. Mdosthat feladatok, felajnlott kategrik s


alaprtelmezetten betlttt feladatlistafjl.......................... 41
7.1. A mr megadott feladatok mdostsa........................................................... 41
7.2. Kategrik felajnlsa a QCompleter osztly hasznlatval. Els pillants a
modellekre............................................................................................. 43
7.3. Automatikus fjlbetlts a program indulsakor a QSettings osztly.................. 46

3
Qt strandknyv Tartalomjegyzk

8. Keress, grafikon s tbbnyelvsg. ................................................ 49


8.1. A keresfl. . ........................................................................................... 49
8.2. Grafikuss vlva grafikont rajzolunk, s a tetejbe mg iterlgatunk is. ................ 50
8.3. Tbbnyelv alkalmazs s a QT Linguist....................................................... 56

9. Ikonok, ablakok, automatikus ments.............................................. 59


9.1. Ikonok s QAction-k a QToolBar-on. ........................................................... 59
9.2. Mdos s egyb prbeszdablakok, a QTextBrowser s jabb adalkok az
erforrsfjlok termszethez...................................................................... 61
9.3. Automatikus ments a QTimer osztly hasznlatval. ...................................... 64

10. Komolyan hasznlni kezdjk a ModelView minta lehetsgeit.69


10.1. Elem-alap s modell-alap widget-ek.......................................................... 69
10.2. jratervezs.. ................. ......................................................................... 70
10.3. Mit tartunk meg az elz vltozatbl?........................................................... 70
10.4. A fablak kialaktsa....... ......................................................................... 71
10.5. A modell, meg az osztly, ami kezeli............................................................. 72
10.6. Knyelem mindenekfelett. . ......................................................................... 74
10.7. Bevetjk a QItemSelectionModell osztlyt. .................................................... 75

11. Adatments s visszatlts modell hasznlatakor. ................... 82


11.1. A Belltsok ablak.. ................................................................................. 82
11.2. A FileOperator osztly reinkarncija........................................................... 84
11.3. Utols lehetsg a mentsre a QMessageBox osztly tovbbi lehetsgei s a Q_
PROPERTY makr.................................................................................... 89
11.4. Automatikus ments s megnyits. .............................................................. 93

12. A modell, a deleglt, meg a proxymodell. ....................................... 94


12.1. Deleglt megvalstsa a QStyledItemDelegate osztly hasznlatval................... 94
12.2. A keresfl s a QSortFilterProxyModel osztly. ............................................. 97

13. Hlzati szinkronizci: a ToDoList felhcskje. ......................... 98


13.1. A webszerver elksztse. ......................................................................... 98
13.2. Kiegsztjk a bellts-ablakot. ................................................................. 100
13.3. Feltlts a WebSynchronizer osztllyal a QNetworkAccessManager s egyb
hlzati rmk. . ............ ........................................................................ 100
13.4. Letlts a WebSynchronizer osztllyal a QNetworkReply osztly s a fjlok
kicsomagolsa................ ........................................................................ 107

14. Tbbszl programot runk............................................................... 109


14.1. Tanulmnyprogram a szlak tanulmnyozsra. ........................................... 109
14.2. Szlakat ptnk a ToDoList-be. ................................................................. 113

15. XMLfjlba mentjk a feladatlistt............................................. 116

4
Tartalomjegyzk Qt strandknyv

16. A ToDoList, mint SQLkliens. ............................................................. 120


16.1. Gyomllunk......................... ................................................................. 120
16.2. Els QSqlTableModel-nk......................................................................... 121
16.3. Trls s visszavonsa barkcsoljunk kzzel QSqlRecord osztly objektumot!. .. 127
16.4. Keress s kiegszts a QSqlQueryModel osztllyal. ....................................... 129

17. Egyszer Qtprogram csomagolsa Linuxrendszerekhez. ......... 132


17.1. Teleptcsomag ksztse Debian alap rendszerekre. ...................................... 133

18. Kirnduls a Qt Quickszigetekre (meg a QML tengerbe). ....... 134


18.1. QML-programot runk............ ................................................................. 134
18.2. Oda-vissza Qt s Qt Quick kztt: gy beszlgetnek egymssal a hibrid programok
rszei................................................................................................... 139

Bcs. . . . ........................................ ................................................................ 145

Milyen osztlyokrl, makrkrl s egyb szpsgekrl


van sz a knyvben?. . ................................................................................. 146

5
Qt strandknyv Bevezets

Bevezets
A Qt strandknyv a Qt keretrendszerrl szl. Nincs benne romnc, feszltsg s drma,
azaz vgs soron nem is igazi strandknyv, de egy kicsit mgis: habknny s fjdalommentes
ismerkedst knl a keretrendszerrel, annak fbb lehetsgeivel.
Szerzt, szerkesztt, lektort az a cl vezrelte, hogy a programoz vagy programozpalnta
anlkl ismerkedhessen meg a Qt-vel (vagy Qt-tal, merthogy a Qt kiejtse krlbell kjt,
azaz cuki, aranyos, des), hogy gy istenigazbl neki kne llnia a tanulsnak. Nincs ht sz
arrl, hogy az Olvas referenciamvet bngszne ppen. Mr csak azrt sem, mert maga a Qt
risi, s elg gyorsan vltozik ahhoz, hogy mire elkszlne egy igazn tfog s szakmailag
korrekt m, mr lennnek olyan pontok, ahol elavultnak szmtana.
Manapsg j sok szakknyvn az olvashat, hogy mindenkinek csak a javra szolgl, ha
elolvassa. Itt is szerepelhetne az, hogy egyarnt hasznos a telefonjval bajld kisiskolstl
kezdve mindenkinek, akinek valami elektromos kerl a kezbe, legyen az akr egy hajszrt.
De azrt igyeksznk ennl korrektebbek lenni.
A knyv idelis olvasja az a programozni tanul ember, aki tl van a C++-szal val
ismerkeds els lmnyein (vagy megrzkdtatsain nzpont krdse a dolog). rt mr pr
tlterhelt fggvnyt, nem ijed meg, ha karakterlncokat kell tmbben trolnia s a tmbt
rendeznie. Elkszlt az els nhny objektumval, s reggelire pldnyost magnak vajas
kenyeret. s, ami a C++-szal foglalkozk esetben elengedhetetlen: ltott mr mutatt, rti a
memriaszivrgs fogalmt de nem kell vrprofi mutatzsonglrnek lennie.
Mi az, amit mg nem kell tudnia? Nem kell ismernie a C++ szabvnyos knyvtrait, s nem
kell tudnia esemnyvezrelt programokat rnia. Az egyikre azrt nem lesz szksge, mert gy
addott, hogy a Qt eszkzkszlete pont elg lesz neknk, a msikra meg azrt nem, mert majd
most gyis megtanulja. (Egybknt mg egy csom mindent nem kell tudnia, de nem soroljuk
fel mindet.)
Kvnjuk, hogy a kvetkez oldalak szolgljanak az Olvasnak plsre, egyben
kikapcsoldsul.
Grdony, 2013. augusztus-oktber

Varga Pter

6
Mirt pont Qt? Qt strandknyv

Mirt pont Qt?


Ma, amikor programozsi nyelvek garmadjbl vlogathatunk, ugyan mgis mrt
vlasztan az ember a Qt keretrendszert? Lssunk nhny rvet!

C++ alapok
A Qt trtnetesen a vilg egyik legismertebb s legelismertebb nyelvt egszti ki. A
hasznlt kiegsztsekbl a Meta Object Compiler (becenevn: moc) a fordtsi folyamat sorn
szabvnyos C++ kdot llt el. Ezt aztn akr a GCC, akr a Visual Studio, akr a MinGW
kpes lefordtani.

Ms nyelvbl
Az a helyzet, hogy a Qt elemeinek hasznlata nem csak C++-bl lehetsges. Az elemkszlet
hasznlhat Java, Python, Perl, Ruby s ms nyelvekbl.1 Ebben a knyvben maradunk a
C++-nl, de ez ne tntortson el senkit a ms nyelvekkel val hasznlattl.

Multiplatform fejleszts
Nem is. Inkbb multiplatform fordts s futtats. A fejlesztst vgezhetjk kedvenc asztali
opercis rendszernkn (az IDE fut Windowson, Linuxon s OS X-en), de a forrs tbbfle
rendszerre is lefordthat. Android s iPhone tmogats a Qt 5.2-ben rkezik persze lehet,
hogy mire e sorokat a nagykznsg is olvasni fogja, mr az rkezett forma a helyes: a Qt 5.2-t
2013 szre vrjuk.

J IDE
A Qt Creator termszetesen vgez automatikus kiegsztst, van krnyezetrzkeny sgja.
Kezel projekteket, tmogat rengeteg verzikvett. Mobileszkzk esetben a Qt Creator
elkszti a teleptcsomagot s telepti a fejleszt gphez csatlakoztatott eszkzre.

Sokfle licencels
E knyv rsakor a Qt programunk licencelse lehet kereskedelmi, ha ezrt vagy azrt nem
szndkszunk kzz adni mvnk forrskdjt. Ha pedig szndkszunk, vlaszthatunk az
LGPL 2.1 s a GPL 3.0 licenc kzl, azaz megvan a mdunk arra, hogy korszer szabadszoftveres
licencet hasznljunk, akr azrt, mert ezt kvetel kdot ptnk a termknkbe, akr azrt,
mert gy keltnk fel reggel.

Kzssgi tmogats s tlls


A Qt jelenlegi tulajdonosa, a Digia vezeti a tmogatkat akr az Olvast is tmrt
-Qt Projectet. A KDE Free Qt Foundation nev alaptvny pedig olyan jogok birtokban
van, amelyek a Digia felvsrlsa, csdbemenetele, vagy ms cggel val sszeolvadsa
esetn lehetv teszik az alaptvny szmra a Qt Free Edition szabadszoftveres licenc alatti
kzreadst. Azaz elvileg nem rheti ilyen okbl csnya baj a Qt-re alapozott hossz tv
zletnket, s nem kell attl flnnk, hogy egy menedzser irodjban meghozott dnts miatt
egyszer csak kihzzk a talpunk all kedvenc keretrendszernket.

1 http://qt-project.org/wiki/Category:LanguageBindings

7
Qt strandknyv Nhny egyb tudnival

Nhny egyb tudnival


Egyezznk meg kt dologban
Ebben a knyvben a forrskd nyelve teljes egszben az angol, mg akkor is, ha a knyv
maga magyar nyelv. Elismerve, hogy megvannak az rvek a magyar nyelv vltoz-,
fggvny- s osztlynevek mellett is, rmutatunk, hogy hasznlatuk hibs gyakorlat
kialakulshoz vezet. Manapsg a programoz j esllyel nemzetkzi projektekben is rszt
vesz, forrskdjt ms nyelven beszlknek is olvasniuk, mdostaniuk, hasznlniuk kell.
Klnsen igaz ez akkor, ha a kezd programoz szabad szoftveres projektben val rszvtellel
igyekszik megszerezni azt a munkatapasztalatot, referencit, amelynek birtokban a siker
lnyegesen nagyobb eslyvel mehet el llsinterjra.
A programjaink eleinte magyarul szlnak a felhasznlhoz, lvn gy fny derl pr dologra
(igen, az kezetek tern), ami amgy stt homlyban maradna.
A szakszavakat csak akkor fordtjuk magyarra, ha a magyar vltozat hasznlata a magyar
szaknyelvben bevett, kikristlyosodott. Azrt is vagyunk vatosak a fordtssal, mert az angol
nyelv internetes szakirodalom trtnetesen gazdagabb mg a magyarnl is. Kifejezetten
zavar, ha a magyar nyelv szakkifejezs angol megfelelje nem jut esznkbe, s ennek
folyomnyaknt nem tudunk jl keresni az interneten. A j magyar szakkifejezs persze
megknnyti az ismeretlen fogalom megrtst, fleg annak, akinek (mg) nem az angol a
msodik anyanyelve.

A knyvben lv forrskdok
A forrskdok a knyv egszhez hasonlan CC-BY-SA licencek, azaz a szerz feltntetse
mellett azonos licenccel kzreadhatk, szrmazkos m kszlhet bellk.2 Mr amennyiben a
knyv vgigolvassa utn valaki mg rszorulna ilyesmire.
A kdokat a legritkbb esetben kzljk teljes egszben, mindazonltal letlthetk a http://
szabadszoftver.kormany.hu/qtstrandkonyv webhelyrl.

Mire lesz szksgnk?


A Qt Library-ra, s a Qt Creator-ra. Mindkett letlthet Windows, Linux s Mac rendszerre
a qt-project.org webhelyrl, br Linuxon j esllyel a disztribcink csomagkezeljvel
rdemesebb teleptennk.

2 Rszletesebben: http://creativecommons.org/licenses/by-sa/3.0/

8
1. Pr sz az esemnyvezrelt programokrl Qt strandknyv

1. Pr sz az esemnyvezrelt
programokrl

Aki mr rt grafikus fellettel br programot, btran lapozzon elre a kvetkez fejezetig


nem lesz messze. A tbbiek olvassanak tovbb.
Amikor az ember programozni tanul, alighanem tudomst szerez arrl, hogy a programok
szekvencilisan, azaz az utastsok sorrendjben futnak. Van termszetesen nhny struktra,
nyelvi elem, ami bonyoltja a helyzetet, nevezetesen az elgazsok s a ciklusok. Ilyenkor
ugye a sok programsor kzl pr kimarad, vagy ppen tbbszr is lefut. Tovbb bonyoltjk
a helyzetet az eljrsok s a fggvnyek. De azrt valamilyen rtelemben mgis szekvencia
marad a szekvencia: mindig megmutathat, hogy pp hol tart a program, s lehet tudni, hogy
mi lesz a kvetkez dolga.
Nos, ms a helyzet egy esemnyvezrelt programban.
Itt ugyanis van egy f ciklus, ami arra vr, hogy bekvetkezzen valamilyen esemny. Az
esemny egyfell lehet hardveres eredet: billentynyoms, egrmozdulat, vagy -kattints,
egyb perifria ltal kivltott megszakts, msfell a program egyes rszei is kivlthatnak
esemnyeket, pldul a fjlmentsrt felels rsz szl, hogy betelt a httrtr. A program
bezrsa egyenrtk a f ciklusbl val kilpssel.
A program a fcikluson kvl lnyegben mr csak esemnykezelkbl ll, a f ciklus velk
vgezteti el a bekvetkezett esemnyek kezelst. Az esemnykezelk a mi esetnkben bizonyos
objektumok tagfggvnyei lesznek. A tagfggvnyek belseje termszetesen megint csak
szekvencilis, ugyanakkor lehetsgnk van a tagfggvnyekbl esemnyt kivltani.
Azaz az esemnyvezrelt program a mi rtelmezsnkben egy vagy tbb objektum, egy
vagy tbb tagfggvnnyel, amelyek akkor futnak le, ha az ltaluk feldolgozand esemny
bekvetkezik.
Akkor nzzk mindezt most a Qt-n bell. Amikor ltrehozunk egy grafikus alkalmazst,
akkor a generlt main.cpp fjl krlbell ilyen lesz:
#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])


{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}

Az emltett f ciklus jelen esetben a QApplication osztlybl pldnyostott a nev


objektum. A pldnyostsa utn ltrehozzuk s meg is jelenttetjk a fablakot, majd
meghvjuk a f ciklus exec tagfggvnyt. Ez utbbi tettnk hatsra lendl munkba a f
ciklus, innentl vgzi a dolgt.

9
Qt strandknyv 2. Els programunk. Igen, a "Hell vilg!"...

2. Els programunk. Igen, a Hell vilg!,


de kicsit bvebben
2.1. A grafikus objektumok s a Property Editor
Indtsuk el a Qt Creator-t, fell kattintsunk a Develop flre, majd az oldal kzepe tjn a
Create Project lehetsgre. Azon bell vlasszuk az Applications s a Qt GUI Application
pontokat, vgl pedig a Choose gombot. Adjunk nevet a kszl mnek, s pr Next utn az
els adand alkalommal kattintsunk a Finish gombra. Ltrejn a projektnk, benne a main.
cpp, illetleg, ha nem babrltunk a belltsokkal, a mainwindow.cpp s a mainwindow.h fjl.
Aki trelmetlen, mr fordttathatja s futtathatja is a gpn lv legjabb alkalmazst, akr
gy, hogy a bal oldali sv als rsze fel tallhat lejtszs-gombra kattint, akr a Ctrl+R
billentykombinci hasznlatval. Megjelenik egy MainWindow cmsor ablak, amit lehet
mozgatni, tmretezni, meg mg be is zrhat. Tegyk is meg: annyira azrt mg nem remek ez
a program, hogy rkig ellegynk vele.
A projekt fjljait megjelent oszlopban vlasszuk a Forms lehetsget, azon bell pedig az
ott rvlkod mainwindow.ui fjlt. Ekkor a kperny talakul. A fjllista helyn a hasznlhat
grafikus objektumok listjt talljuk, kzpen pedig, a forrskdszerkeszt helyn maga a
MainWindow cscsl, br most pp a Type Here felirat olvashat rajta.
Bal oldalrl, az objektumok listjbl vlasszuk a Label-t. Ha nem talljuk, akkor a fenti,
Filter mezben elkezdve gpelni a nevt, alighanem elkerl. Hzzuk r a kzpen lv ablakra.
A felirata (TextEdit) rkattintva is megvltoztathat, de most mgis inkbb a kperny jobb
als sarkban lv Property Editor (tulajdonsgszerkeszt) rszen fogjuk mdostani.
A Property Editor arra val, hogy egy grafikus objektumot knnyen ttekinthessen s
mdosthasson a fejleszt. Jelen sorok szerzje a Delphi nev programozsi krnyezet els
kiadsban, mg Windows 3.1 alatt ltott elszr ilyet (1995), azaz megkockztathatjuk, hogy a
koncepci nem j. Keressk meg a text tulajdonsgot, s mdostsuk pldul Hell vilg!-ra.
Ha azt ltjuk, hogy a felirat kilg a QLabel osztly objektumunkbl (igen, minden Qt-osztly
neve Q-val kezddik), akkor mdostsuk a mretet akr az egernkkel, akr a Property Editor-
ban, a geometry rszen. Futtassuk most a mvnket, s dicsekedjnk el valakinek, pldul az
aranyhrcsgnknek. Megvan az els Qt-programunk!

2.2. A signals and slots mechanizmus


Sem a grafikus fejleszt, sem a Property Editor nem teszi egyediv a Qt-t. A signals and slots
mechanizmus azonban igen: a dolog teljes egszben Qt-specifikus.
Az elz projektnkbl kiindulva
hzzunk ki egy QPushButton-t a
fablakra, majd lltsuk t a szveget
Kilps-re (most is a text tulajdonsg a
bartunk). Fent, a mensor alatt keressk
meg a jobbra lefel mutat nyilat brzol
ikont, melynek a sgja Edit Signals/Slots.
Kattintsunk r. Azon fell, hogy bal oldalt 1. bra: A Signals and Slots ikon

10
2. Els programunk. Igen, a "Hell vilg!"... Qt strandknyv

kiszrkl az objektumok listja, nem sokat ltunk, de ha megprbljuk a gombot odbbhzni,


akkor nem fog menni, helyette nyilat rajzolunk.
A nyilat engedjk el valahol az ablak
terlete fltt, s megjelenik egy j
dialgusablak. Itt lehetsgnk nylik a
QPushButton valamely signal-jt (jelt) a
MainWindow valamelyik slot3-jhoz ktni.
A QPushButton signal-jai kzl
vlasszuk a clicked()-et, a MainWindow-
nl azonban nem ltszik az, amit keresnk.
Tegynk pipt a Show signals and slots
inherited from QWidget (a QWidget-tl
rklt jelek s csapdk megjelentse)
mell, s ekkor megjelenik a close() slot.
Jelljk ki, s kattintsunk az OK gombra. 2. bra: Signal-t ktnk slot-hoz
Futtassuk a programot, s kattintsunk a
gombra. Ol!
Mit is vgeztnk?
A Qt-ban az objektumok kpesek magukbl signal-okat kiadni, kisugrozni, szakzsargon-
ban emittlni. Ezek olyanok, mint amikor valaki ordtozik egy mlygarzsban: korntsem biz-
tos, hogy brki is hallja a zajt. Egy jl nevelt Qt-objektum (s majd figyelnk, hogy az ltalunk
rtak ilyenek legyenek), csak akkor kiabl, ha trtnt vele valami fontos pldul megvltozott
az llapota. A nyomgombunkkal most pont ilyesmi trtnt: rkattintottunk.
A Qt-ban az objektumoknak lehetnek slot-jaik is, azaz olyan specilis tagfggvnyeik, melyek
kpesek hallgatzni: figyelni arra, ha egy msik objektum kiabl.
Amikor a signal-t a slot-hoz ktjk, tulajdonkpp elmondjuk a slot-nak, hogy melyik
objektum melyik kiablsra kell figyelnie. Figyeljnk fel arra a tnyre, hogy a kiabl
objektumnak fogalma sincs rla, hogy valaki figyel-e r: csak kiabl. A kapcsolatrt, azrt,
hogy trtnjen valami, lnyegben a slot objektuma felel.
Amikor lenyomtuk a gombot, az elkiltotta magt, hogy Rm kattintottak!, s a
MainWindow close() slot-ja ezt meghallva srgsen bezrta az ablakot.
Hozzunk ltre egy msik gombot (csak akkor fog menni, ha a fenti ikonsoron a most
lenyomottl balra lv, Edit Widgets ikonra kattintunk elbb), legyen a szveg rajta Csitt! s
ezt kssk a QLabel-hez, a signal-ok kzl vlasszuk megint a clicked()-et, a slot-ok kzl
meg azt az egyet, amit lehet: a clear()-t. Prbljuk ki ezt a mvnket is.

2.3. Elg a kattintgatsbl!


Ez persze nem igaz, de a kvetkez feladat megvalstshoz mr knytelenek lesznk a
billentyzetet is hasznlni. Hozzunk ltre mg egy gombot, s erre rjuk ki azt, hogy Ksznj
mskpp! A feladat az, hogy erre kattintva vltoztattassuk meg a QLabel szvegt. Igen m, de
ht hogyan? A j kis Configure Connection ablak most nem segt. Mirt?
3 A slot sz tbbek kzt bevgst, csapdt jelent. Ez utbbi jelentse krlbell megfelel a jelenle-
gi szerepnek, de nem pontos abban a tekintetben, hogy a slot olyasmit jell, amibe beleillik az,
amit bele akarunk tenni.

11
Qt strandknyv 2. Els programunk. Igen, a "Hell vilg!"...

Ha megnzzk a QLabel osztly dokumentcijt (akr a beptett sgban, F1-et nyomva


az objektumon llva, akr az interneten, a qt-project.org webhelyen), ltni fogjuk, hogy a
slot-jai kztt van olyan, hogy setText(), azaz szveg belltsa. A visszatrs tpust (void)
megltva (mrmint azt, hogy egyltaln van neki ilyen), joggal gyanakszunk arra, hogy a
slot-ok tulajdonkpp specilis tagfggvnyek. A tagfggvnyeknek meg ugye van deklarcijuk,
benne paramterlistjuk. s itt lesz a kutya elsva. A signal-lal a kld (sender) objektumnak pont
ugyanolyan tpus adatot kell emittlnia, mint amilyet a kap (receiver) slot-ja vr.4
Esetnkben ugye meg az a helyzet, hogy a QPushButton.clicked() nem ad olyan adatot
(QString tpust), amit a QLabel.setText(const QString &) vrna. Akkor most mi lesz?
Kattintsunk jobb egrgombbal a nyomgombon, s vlasszuk a Go to slot... lehetsget a helyi
menbl. A signal lehet most is a clicked(). Visszakerlnk a kdszerkesztbe, s megrdott
neknk egy fl fggvny, nevezetesen ott a fggvnydeklarci, csak a fggvny trzst kell meg-
adnunk. Mieltt ezt tennnk, nzzk meg a fggvny prototpust a mainwindow.h llomnyban
(kattintgatunk, vagy F4-et nyomunk). Az osztlydefinci jelen llapot szerint gy nz ki:
class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

private slots:
void on_pushButton_3_clicked();

private:
Ui::MainWindow *ui;
};

Ltjuk, hogy a fggvny prototpusa nem a szokvnyos public/private terleten, hanem az


egyszeri C++-programoz szmra ismeretlen helyen, a private slots rszen van. Most,
hogy ezt megnztk, mehetnk vissza fggvnytrzset rni.
Ha figyelmesen szemlljk a mainwindow.cpp fjl tartalmt, szrevesszk, hogy van egy ui
(user interface, felhasznli fellet) nev objektum. A fablak elemeit ezen az objektumon bell
pldnyostja a Qt Creator. gy aztn a harmadik gombunk lenyomsakor hvott slot-ot gy kell
megrnunk:
void MainWindow::on_pushButton_3_clicked()
{
ui>label>setText("Szia neked is, hrcsg!");
}

A szveg persze gy nem fog kifrni, hzzuk szlesebbre a QLabel-t. s, ha eddig nem tettk
volna, most mr tnyleg mutassuk meg az aranyhrcsgnknek a programunkat.
A kvetkez fejezetben runk sajt signal-t, meg sajt slot-ot.

4 Ez a Qt 5 esetben nem teljesen igaz: ha a tpusok kztt implicit talakts lehetsges, akkor
hasznlhatunk ms tpust is. Mi a knyvnkben e tekintetben a Qt 4 szigorbb megszortst
hasznljuk.

12
3. Sajt signal s slot megvalstsa Qt strandknyv

3. Sajt signal s slot megvalstsa


meg egy kis QDebug s QMap
Ebben a fejezetben egy olyan programocskt runk meg, amelyik visszakszn, mghozz
rtelmesen. Nem hajtjuk persze elvenni a mestersges intelligencival foglalkozk kenyert,
ezrt magunkat visszafogva a kvetkezk megvalstst tzzk ki clul:
Lesz egy MainWindow-unk, rajta egy szvegbeviteli mezvel, egy QLineEdit-tel. Egy msik
objektumbl figyeljk, hogy mi kerlt a szvegmezbe, s ha pp felkiltjelhez rnk, akkor
felttelezzk, hogy vge a ksznsnek. Ha a kszns Szia!, akkor visszaksznnk, hogy
Szevasz!, ha ellenben J napot!, akkor dvzlm! lesz a vlaszunk. Nem lltjuk, hogy
ennek a bonyolult feladatnak a kivitelezshez valban msik osztlyt kell rnunk, de ez ugye
egy strandknyv, s a strandknyvek ritkn jelentik a valsg pontos lekpezst.

3.1. Elszr slot-ot ksztnk


Kezdjnk ht j projektet, a neve legyen polite. Tegynk ki a MainWindow ablakra egy
QLineEdit objektumot, s a Property Editor-ban kereszteljk t az objektumot greetingLine-
ra az objectName tulajdonsg lltsval. A Qt Creator bal oldali svjban kattintsunk az Edit
lehetsgre, majd a Sources feliratra, vagy a projekt nevre (Qt Creator verzitl fggen) a jobb
egrgombbal. A felbukkan menbl vlasszuk az Add new... lehetsget, a megnyl ablakbl a
C++-t, azon bell pedig a C++ Class-t. A Choose gombra kattintva j ablakot kapunk, itt adjuk
a Greeter nevet az osztlynak, s lltsuk be a QObject-et alaposztlyknt (Base Class).
Mikor elkszlt az osztlyunk, keressk meg a greeter.h fjlt, s nzzk meg. Az
osztlydeklarci alatt ltni fogunk egy Q_OBJECT makrt. Egy ujjal se nyljunk hozz,
mert akkor ebben a fjlban fordtskor nem nz krl a moc (amivel a Mirt pont Qt? rszben
ismerkedtnk meg futlag), s nem kszt a Qt-specifikus nyelvi elemekbl olyan kdot, amit
mr egy szabvny C++-fordt is megrt.
Itt a fejlcfjlban hozz is fogunk sajt slot-unk megrshoz: a public slots rszen
helyezzk el a greetingLineTextEdited() nev, void visszatrsi rtk fggvny
prototpust. Igen m, de mi lesz a fggvnynkhz tartoz signal? A QLineEdit osztly
sgjt bngszve kett jelltet is tallunk. Az egyik prototpusa: void textChanged(const
QString & text), a msik pedig: void textEdited(const QString & text). tfutva
a dokumentcijukat, rjvnk, hogy tulajdonkpp mindegy, melyiket hasznljuk, ha mgis
vlasztanunk kell, akkor taln az utbbit szeretnnk. Mr most figyeljk meg, hogy a signal-
nak a megvltozott szveg nem a visszatrsi rtke.
A QString tpus, ha tetszik, a QString osztly objektumok azok, ahol egy jl nevelt Qt-
program a szveges vltozit trolja, mghozz Unicode kdolsban. Ha hasznlni akarjuk
mrpedig akarjuk, akkor #include-olnunk kell, azaz helyezzk el a greeter.h fjl elejn az
#include <QString>

sort.
Nos, pr oldallal korbban olyasmit lltottam, hogy a slot olyan tpus adatot kr, amilyet a
signal emittl, azaz a greetingLineTextEdited() slot prototpust megad sor a kvetkez:
void greetingLineTextEdited(const QString&);

13
Qt strandknyv 3. Sajt signal s slot megvalstsa

Nyomjunk a soron jobb egrgombot, s a helyi menbl vlasszuk a Refactor, azon bell
az Add Definition in greeter.cpp lehetsget, vagy ha ez a lasssg elviselhetetlen, akkor
hasznljuk az Alt+Enter billentykombincit, s nyomjunk Enter-t.
tkerlnk a greeter.cpp fjlba, ott vr bennnket az res fggvnytrzs. Nvtr belltva.
Szval, ha a szveg a greetingLine objektumban megvltozik, ez a fggvny reagl
majd. Mieltt azon trnnk a fejnket, hogy pontosan mi legyen a dolga, nzzk meg, hogy
tnyleg mkdik-e a signal-slot mechanizmus. Adjuk meg, hogy a paramter neve legyen
greeting, majd rassuk ki, hogy mit kaptunk. A kirst a qDebug() fggvnnyel vgezzk.
Hasznlathoz a greeter.cpp fjl elejn szksgnk van egy
#include <QDebug>
sorra. Figyeljnk, hogy mikor qDebug() s mikor <QDebug>, mikor kisbets, mikor
nagybets.
A fggvny egsze mostanra teht a kvetkez formt lti:
void Greeter::greetingLineTextEdited(const QString &greeting)
{
qDebug() << greeting;
}

Most az a rsz kvetkezik, hogy a greeter.h-t hasznlatba vesszk a mainwindow.h-ban,


azaz elhelyezzk az
#include "greeter.h"

sort a fjl eleje fel. Aztn egy


private:
Greeter *greeter;

sorprossal ltrehozunk magunknak egy mutatt, s a cpp-fjlban, a MainWindow


konstruktorban pedig pldnyostunk magunknak egy objektumot a remek kis osztlyunkbl.
Ekkorra a MainWindow konstruktora az albbi formt lti:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
greeter = new Greeter(this);
ui>setupUi(this);
}
Amikor a Greeter sz utn kitesszk a nyit zrjelet, sgt kapunk arrl, hogy itt a
szlobjektum mutatjt kne megadnunk, aminek alaprtelmezett rtke a 0. Mi azrt
adunk meg this-t, azaz magra a MainWindow-ra mutat mutatt, mert ha gy jrunk el,
akkor mkdsbe lp a Qt memriafelgyelete, nevezetesen az, hogy mutat ide vagy oda, a
szlobjektum megsemmislse egytt jr majd a gyermekobjektum megsemmislsvel. Ez
annyira fontos dolog, hogy elmondjuk msknt is: ha csak a QObject osztly leszrmazottaival
dolgozunk, s figyelnk arra, hogy az objektumnak legyen szlje, elfelejtkezhetnk mindarrl
a mizrirl, amit a C++-ban a lefoglalt memria felszabadtsa, pontosabban annak
elmaradsa okozni szokott.
A j hr olvasatn remlhetleg nem csak bennnket, de az aranyhrcsgt is az rm
hullmai jrjk t. Ha t esetleg mgsem, akkor adjunk neki pr centi srgarpt.

14
3. Sajt signal s slot megvalstsa Qt strandknyv

Most mr vgre futtassuk a programunkat! Fordul, de mkdni pldul nem mkdik: a


qDebug() hallgat. Na igen. Ki mondta meg a slot-nak, hogy melyik signal-ra figyeljen?!
Az ui->setupUi(this) sor utn (vagyis, amikorra a greeter mutat mr j helyre
mutat, s helyn van a greetingLine nev objektum is az ui belsejben valahol), rjuk be
a kvetkez sort ami radsul, pusztn az olvashatsg min kt sorba kerl most is, meg
ksbb is gy rdemes majd berni.
connect(ui>greetingLine, SIGNAL(textEdited(QString)),
greeter, SLOT(greetingLineTextEdited(QString)));

Ltjuk, hogy nem egszen szokvnyos C++-utasts a connect. Megadjuk benne, hogy
melyik objektum fogja a signal-t emittlni, a SIGNAL() zrjelben megadjuk, hogy melyik ez
a signal, megadjuk a slot-ot tartalmaz objektumot, majd a SLOT() zrjelben, hogy pontosan
melyik slot-ot kell a signal-hoz kapcsolni. rtuk mr, hogy a Qt 5 nem ragaszkodik teljesen
azonos tpushoz a signal s a slot esetben, meg azt is, hogy ebben a knyvben maradunk a Qt 4
szigorsgnl. Ennek megfelelen a fenti szintaxis a Qt 4-. A Qt 5-ben egy kicsit ms is lehet5,
de ez is mkdik.
Ha most futtatjuk a programunkat, ahogy elkezdnk gpelni a greetingLine-ba, a
qDebug() telepakolja a kpernyt. Remek!

3.2. A QMap s a ksznsek


Szval a slot mkdik, reagl arra, ha runk valamit a greetingLine szerkesztsorba.
Akkor jhet a logika. A ksznseket egy megfeleltetsi tpus, a QMap hasznlatval troljuk.
Az els dolgunk, hogy a greeter.h fejlcllomny elejn jelezzk, hogy a <QMap> fejlcre is
szksg van. Aztn deklarlunk egy privt vltozt a ksznseknek, mgpedig gy:
private:
QMap<QString, QString> greetings;

Mit is jelent mindez? Ltrehozunk egy greetings nev objektumot, amely kulcs-rtk
prokbl ll6. Ennek az objektumnak mind a kulcsai, mind az rtkei QString tpusak. A
greeter osztly konstruktorban fel is tltjk a sztrunkat, mind a kt ismert ksznssel,
illetve a rjuk adand vlaszokkal.
Greeter::Greeter(QObject *parent) :
QObject(parent)
{
greetings["J napot kvnok!"] = "dvzlm!";
greetings.insert("Szia!", "Szevasz!");
}

Pusztn a soksznsg jegyben ktfle mdszert is bemutatunk a kulcs-rtk prok


megadsra: az elsben a kulcs szgletes zrjelbe kerl, s az rtket az rtkads-opertorral
adjuk meg (de, komolyan), mg a msodik esetben az insert tagfggvny paramtereiknt
adjuk meg a kulcsot is, s az rtket is. Nem tnik tl btor kvetkeztetsnek, hogy ez utbbi
esetben a paramterek sorrendjnek szerepe van.

5 http://woboq.com/blog/new-signals-slots-syntax-in-qt5.html
6 Python nyelvben ennek a tpusnak kln neve is van: sztrnak hvjk.

15
Qt strandknyv 3. Sajt signal s slot megvalstsa

A slot-unkat pedig a kvetkezkpp alaktjuk t:


void Greeter::greetingLineTextEdited(const QString &greeting)
{
if(greeting.endsWith("!")){
if(greetings.contains(greeting)){
qDebug() << greetings.value(greeting);
}else{
qDebug() << "n nem ismerem kendet.";
}
}
}

Az endsWith() tagfggvny neve magrt beszl. Neknk azrt kell, hogy ne kszngessnk
egyfolytban, hanem udvariasan megvrjuk, amg a tisztelt felhasznl befejezi a ksznst,
s csak akkor reaglunk. A contains() tagfggvnnyel megvizsgljuk, hogy szmunkra
rtelmes-e a kszns (van-e ilyen kulcs a greetings objektumban), s ha igen, akkor a value
tagfggvnnyel elkotortatjuk a kulcsnak megfelel rtket. Ha meg nincs, akkor elkldjk a
delikvenst.
Akkor eddig minden rendben, a program ksznget, vagy elkld, ahogy arra krtk. Mg
egy utols adalk a QMap osztly hasznlathoz: a value() tagfggvny kpes arra, hogy ha
valamit nem tall, akkor egy elre belltott rtket ad vissza. Ennek fnyben a slot-unk trzse
az albbi kt sorra cskkenthet, anlkl, hogy a mkdse brmennyiben is vltozna:
A Qt 4 s az UTF-8
A mvnket Qt 4 alatt tesztelve arra jutunk, hogy a Szia! ksznsre szpen reagl, de ha
udvariaskodva ksznnk, akkor bizony nem ismer meg. Ez azrt szomor, mert rthet, hogy
az ember programja fiatalos, na de hogy bunk is?! Kis kutats utn rjvnk, hogy a hibt az
kezetes betink okozzk: ha a kulcsot Jo napot kivanok!-ra mdostjuk, a dolog mkdni
kezd. Akkor most Unicode a QString-ben trolt adat, vagy sem? Igen, az, de neknk meg UTF-
8 a beviteli mdunk: a forrsfjlban azt hasznlunk meg a kimenet is olyan. s errl mg nem
szltunk a Qt-nak. A megolds az, hogy a legmagasabb olyan szinten, ahol rtelmt ltjuk,
kiadjuk a
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

parancsot. rdemes akr a MainWindow konstruktorban els sorknt megadni. Ha hasznlni


akarjuk a parancsot, szksgnk lesz a <QTextCodec> fejlcfjlra. Mg valami: mostantl
ennek a parancsnak a hasznlatt nem hangslyozzuk: mindenkinek jusson eszbe magtl.
if(greeting.endsWith(!"))
qDebug() << greetings.value(greeting, n nem ismerem kendet.");

3.3. Most pedig signal-t runk


Eddig ugye a visszaksznst a qDebug() fggvnnyel rattuk ki, ami most mg elmegy, de
egy kiforrott szoftverben taln elvetend megoldsnak minstennk. Szvnk mlyn rezzk,
hogy inkbb valahol a fablakban kellene megjelentennk, s akinek most a mr ismers
QLabel villan be, annak azt mondjuk, hogy legynk nagyratrbbek! Mert a MainWindow
aljban ott cscsl az llapotsor, ami trtnetesen egy QStatusBar osztly objektum, s mint
ilyen, rendelkezik showMessage() tagfggvnnyel. Megnzve az osztly dokumentcijt,

16
3. Sajt signal s slot megvalstsa Qt strandknyv

ltjuk, hogy ez a fggvny radsul egy slot. H! Akkor mr csak egy ugyanilyen
paramterlistj signal-t kell rnunk.
A slot prototpusa void showMessage(const QString & message, int timeout =
0), azaz vr egy zenetet, s ezen fell mg arra is kvncsi, hogy hny ezredmsodpercig kell
kirva lennie az zenetnknek. Nos, ezek szerint ezt a kt paramtert kell emittlnunk.
Elsknt a greeter.h llomnyban helyezzk el a slots sor al az albbi
fggvny-prototpust:
void answerGreeting(const QString &message, int timeout = 0);

Figyeljk meg, hogy a paramterlistt gy alkottuk meg, hogy egyszeren lemsoltuk a slot
paramterlistjt.
Most pedig furcsa figyelmeztetsre ragadtatjuk magunkat: vakodjunk a signal
megvalststl! Nem, nem errl a konkrt signal-rl van sz. Inkbb tfogalmazzuk: Soha,
egyetlen signal-t se valstsunk meg! Komolyan.
A signal hasznlata gy trtnik, hogy kiadjuk az emit parancsot, mg pedig a signal
nevt s paramtereit. Esetnkben a slot-unk jabban mr mindssze kt soros trzsben a
qDebug() fggvnyt hasznl sort cserljk le az albbival:

emit answerGreeting(greetings.value(greeting, n nem ismerem


kendet."), 3000);

Mr csak annyi a dolgunk, hogy a MainWindow konstruktorba az elz connect utasts


al jabbat szrunk be:
connect(greeter, SIGNAL(answerGreeting(QString,int)),
ui->statusBar, SLOT(showMessage(QString,int)));

Ha a programot futtatva, s a ksznsek tletgazdagsgn merengve gondolataink kz


befszkeli magt a gyan, hogy a signals and slots mechanizmus tbbek kztt arra is j,
hogy egy gyermekobjektumbl szalonkpes formban rhassuk a szlobjektum, illetve egy
msik gyerekobjektum tulajdonsgait, hvhassuk a tagfggvnyeiket akkor legynk bszkk
magunkra!
Most mindenesetre htradlhetnk, s vrmrskletnk fggvnyben elfogyaszthatjuk a
maradk srgarpt, illetve odaadhatjuk a hrcsgnek.

17
Qt strandknyv 4. Akinek nincs esze...

4. Akinek nincs esze...


Ebben a fejezetben nekifogunk annak a mnek, melyre tulajdonkpp egyltaln nincs
szksg, mert tonnaszm tallhat ilyen mr kszen. Van ingyen s drgn, van helyben fut s
internetes, van szp s csnya. Szksg teht nincs r, cserbe viszont a knyv vgig ellesznk
vele. Tennival-listt nyilvntart programot ksztnk!
Mirt pont ezt? Kt j okunk is van r. Az egyik, hogy erre szpen felfzhetjk minden
leend tudomnyunkat, mg ha nha erltetett is vlik a dolog, a msik pedig, hogy ez olyan
alkalmazs, amellyel mindenki tisztban van: tudja, hogy mit vrhat tle.
Kezdjnk j projektet az elzhz hasonl mdon. A projekt neve legyen szrnyalj,
fantzia! ToDoList.

4.1. A QListWidget sznre lp, s megismerkednk az elrendezsek


fontossgval
Gyalogoljunk t a mainwindow.
ui llomnyra. Megnylik a grafikus
szerkeszt. Helyezznk el egy QLineEdit
elemet s kt QPushButton-t. A szerkeszt
neve maradjon az alaprtelmezett
lineEdit, a kt nyomgomb pedig
addButton s removeButton. Tegynk
ki mg egy QListWidget-et is, a neve
maradjon az alaprtelmezett listWidget.
Vgl mg egy QLabel is kell. Helyezzk
el s feliratozzuk ket az brn lthat
mdon:
3. bra: a ToDoList els futsa

Ha most egy kicsit tmretezzk az ablakot, az eredmny eszttikai rtke ersen korltozott
lesz.

4. bra: A ToDoList fablaka rosszul tri az tmretezst

18
4. Akinek nincs esze... Qt strandknyv

Prbljunk segteni a helyzeten. A


megolds az elrendezsek (layout)
hasznlata, ha ugyanis efflekpp oldjuk
meg az elemek elhelyezst, akkor szpen
mozognak s tmretezdnek majd az ablak
mretnek vltoztatsval. Az elrendezsek
ki-be kapcsolgatsa, lltgatsa programbl
is trtnhet, de mi inkbb kattintgatunk
most. Alapveten megy a dolog, hogy
kijelljk azokat az elemeket (widget),
amelyeket egymshoz kpest egyms mell
vagy fl rendeznnk, s a Qt Creator fels
5. bra: A jelenlegi elrendezs az
rszn, a Edit Signals/Slots (lsd korbbi
Object Inspector-ban
brnkat) ikon mellett megkeressk a Lay
Out Horizontally (vzszintes elrendezs) s a Lay Out Vertically (fggleges elrendezs)
gombokat. Az elrendezsi hierarchit jobb oldalt, az Object Inspector-ban lthatjuk. Ha valamit
gy rendeztnk el, ahogy nem is annyira j, akkor itt kattintsunk jobb egrgombbal, s a helyi
menben keressk meg a Lay out... / Break Layout (elrendezs megszntetse) menpontot. Az
egsszel ksrletezni kell egy darabig, amg r nem rznk. Most elbb az brn nzzk meg a
jelenlegi elrendezst, aztn prbljuk elvgezni a kvetkez, lpsrl lpsre vezet tmutatt.
Elszr jelljk ki a kt nyomgombot egyszerre ,
s rendezzk ket fgglegesen. Aztn az elemek kzl
hzzunk a jobb oldalukra egy Horizontal Spacer-t
(vzszintes tvtart), s jelljk ki az elz prost,
meg a Spacer-t, majd rendezzk ket vzszintesen.
Ha megvagyunk, akkor a kijellshez vegyk hozz a
lineEdit-et, meg a feladat: feliratot, s rendezzk
ket fgglegesen. Mikor ez is ksz, tegynk aljuk egy
Vertical Spacer-t, s ezzel egytt kijellve vlasszuk a
fggleges elrendezst. Fogjuk meg az als fogantyt (lsd
az brt), s hzzuk a balra lv QListWidget aljig.

6. bra: A fogantyt hzzuk a


QListWidget aljig

19
Qt strandknyv 4. Akinek nincs esze...

Vegyk hozz a kijellshez a


QListWidget-et, s rendezznk
vzszintesen (ha sszeugrana, hzzuk ki
jra). Ezek utn kattintsunk a kijells
mell, a MainWindow terletre, s
kattintsunk akr a vzszintes, akr
a fggleges elrendezsre. Az Object
Inspector ablaka ekkor az brn lthat
hierarchit mutatja.
Most futtatva a programot az egsz
hbelevanc szpen jrarendezdik
s tmretezdik az ablak sarknak 7. bra: A ksz elrendezs hierarchija az Object
hzsval. Mr csak annyi a baj a j kis Inspector-ban
programunkkal, hogy nem j mg semmire,
hinyzik belle a lnyeg. No, tegynk rla!

4.2. A QListWidget elemeinek manipulcija


A hozzads megvalstsval kezdjk. Kattintsunk jobb egrgombbal a fels nyomgombon,
s vlasszuk a Go to slot menpontot a helyi menbl. Megnylik a kdszerkesztben a
fggvnynk, s mr rhatjuk is a trzst.
void MainWindow::on_addButton_clicked()
{
ui>listWidget>addItem(ui>lineEdit>text());
}

A QListWidget osztly objektumokban az addItem() tagfggvnnyel helyeznk


el jakat. A nyit zrjel kirsakor (ha az automatikus kiegsztst hasznltuk, akkor az
Enter lenyomsakor) eszkzsgknt megjelenik a paramterlista, aminek az elejn az 1 of
2 azt jelzi, hogy van msik is. A lefel mutat kurzornyllal meg is tudjuk nzni: a msik
paramterlista (remek dolog a tlterhels, igaz?) egyetlen QString-bl ll. Ilyen meg ugye van
neknk, benne a lineEdit objektumban. Kinyerjk a text tagfggvnnyel7, s ksz. Vagy
nem?
A program mkdik, de elg buta: felveszi a listWidget-re az res feladatokat is.
Gondolhatnnk arra, hogy megnzzk eltte, hogy res-e a lineEdit.text() visszatrsi
rtke, de a szkzt mg gy is felveszi. A text tagfggvny ugyebr QString tpus
objektumot ad vissza. A QString dokumentcijt bngszve meg tallunk egy j kis
simplified() tagfggvnyt, ami azon fell, hogy a sor elejrl s vgrl radrozza a szkzt,
tabultort, soremelst s hasonlkat (angol sszefoglal nevkn: whitespace), mg a sor kzben
is csak egyet-egyet hagy meg olyan helyeken, ahova tbbet is sikerlt elhelyeznie az embernek.
A fggvnynk trzst alaktsuk t gy:

7 Azt mg nem is emltettk, hogy az objektum mutatjnak neve utn nem kell nyilat (->) gpel-
nnk, elg egy pont is, mintha nem is mutat volna, hanem igazi objektum. A Qt Creator intelli-
gensen cserli, ha ennek szksgt ltja.

20
4. Akinek nincs esze... Qt strandknyv

QString txt = ui>lineEdit>text().simplified();


if(!txt.isEmpty())
ui->listWidget->addItem(txt);

Azaz mostanra csak az a feladat kerl be a listWidget-be, ami nem res. Mit lehet mg
tuprozni? Pldul azt, hogy egyttal rljn ki a szerkesztsor. Ezzel gyorsan megvagyunk,
rjunk mg egy sort a fenti hrom al:
ui>lineEdit>clear();

s ha mr ilyen szpen kirtjk, vissza is adhatnnk neki a fkuszt. me a fggvnytrzs


tdik sora:
ui>lineEdit>setFocus();

Persze az se volna pp rossz, ha nem kellene kattintani, hanem elg volna Enter-t
nyomni, amikor megfogalmaztuk valamely bokros teendnket. A QLineEdit osztly
dokumentcijban hamar rlelnk a returnPressed() signal-ra, amihez rhatnnk egy,
a fentivel megegyez slot-fggvnyt, de az ugye a kd karbantarthatsgnak nem tesz jt.
A vals tennivalkat elklnthetnnk kln fggvnybe, s megoldhatnnk, hogy mindkt
slot ezt a fggvnyt hvja, egyszer burkol (wrapper) szerepre krhoztatva a j kis slot-
jainkat. De mg az aranyhrcsgnk is csaldottabban rgcsln az almjt, ha a Qt-ban
nem volna valamilyen elegnsabb megoldsa a problmnak. s van is: mgpedig az, hogy a
connect utasts nem csak egy signal-t s egy slot-ot tud sszektni, hanem kt signal-t is. A
MainWindow konstruktornak trzsben jelenleg egyetlen sor szerepel, ez al rjuk be, hogy:

connect(ui>lineEdit, SIGNAL(returnPressed()),
ui->addButton, SIGNAL(clicked()));

Szabad fordtsban: ha a lineEdit-ben Enter-t nyomnak, csinld azt, mintha rkattintottak


volna az addButton-ra brmi legyen is a teend.
s mi a helyzet, ha valaki a gyorsbillentyk (keyboard shortcut) nagy rajongja? Kedvezznk
neki is! Kattintsunk az addButton-ra, s a Property Editor-ban a text tulajdonsg rtkt
vltoztassuk meg: rjunk az els f bet el egy & jelet (angolul ampersand, ha keresni
akarunk az interneten mondjuk mskor is ampersand-nak hvjk). Az f bet mostantl al
van hzva, s az ALT+F billentykombincival is tudunk kattintani a gombon. Ez remekl
ment, nyilvn nem okoz gondot a msik gomb hasonl belltsa. De hogy naviglunk vissza
a lineEdit-re? Persze megadhatnnk neki is billentykombincit, de miknt oldjuk meg azt,
hogy a felhasznl eltt mindez ne maradjon titokban? Hova rjuk ki neki?
A megolds az lesz, hogy egy QLabel osztly elemet, egy cmkt hasznlunk nlunk
ilyen a label , ennek a feliratban helyezzk el az & jelet. Persze ne az f bet el tegyk,
brmelyik msik viszont j legyen mondjuk az e. A cmkk statikus jellemkbl kifolylag
nem sok mindent tudnak csinlni, mg arra is kptelenek, hogy az Alt+E billentykombinci
lenyomsa utn a fkuszt elfogadjk. Azonban igen nzetlen termszetek, s rmmel adjk
oda az gy kapott fkuszt a pajtsuknak. Mr csak meg kell mondanunk nekik, hogy melyik
elem a pajtsuk.
A pajts angolul buddy, s ennek tudatban sejteni kezdjk, hogy a QLabel osztly
dokumentcijban lv setBuddy() tagfggvny mire lehet j. Mi azonban most nem
programbl oldjuk meg a buddy belltst. Kt lehetsgnk is van: az egyik, hogy az Edit
Signals/Slots s az elrendezsek ikonjainak kzelben megkeressk az Edit Buddies ikont, s az

21
Qt strandknyv 4. Akinek nincs esze...

egrrel nyilat hzunk a label s a lineEdit objektum kz. A msik, hogy a Property Editor-
ban adjuk meg a buddy tulajdonsg rtkl szolgl objektum nevt. A programot futtatva a
billentykombinci hatsra valban a lineEdit kapja meg a fkuszt.
Akkor mr csak a trls gomb munkra fogsa van htra. Ez persze rejt magban nhny
tovbbi rdekessget, gyhogy amennyiben a hrcsg mr lmos, kldjk aludni. Mi meg mg
maradunk.
A szoksos mdon lltsuk el a trls gomb lenyomsra reagl slot-fggvnyt. Aztn
ugorjunk neki a QListWidget osztly dokumentcijnak. Hamar rjvnk, hogy
alighanem a takeItem() tagfggvny lesz a bartunk. Ltjuk, hogy a visszatrsi rtke
QListWidgetItem osztly objektumra mutat mutat, s a neve is zavar. Az a helyzet,
hogy ez a tagfggvny nem trli az objektumot (a listaelemet), hanem csak kiveszi a listbl.
A bemeneti rtke az objektum sornak a szma. Ha a slot trzsbe egyelre csak annyit
helyeznk el, hogy
qDebug() << ui>listWidget>currentRow();

akkor a gomb lenyomsakor ltjuk, hogy a listWidget sorai 0-tl szmozdnak (ne
felejtsk el hasznlatba venni a <QDebug> fejlcfjlt).
Ha most ezt a sort gy egsztjk ki:
qDebug() << ui>listWidget>takeItem(ui>listWidget>currentRow());

akkor a listWidget-bl eltnik a sor, s a qDebug() fggvny kinyomtatja a kivett


QListWidgetItem osztly objektumra mutat cmet. Ha ezt sszevetjk a takeItem()
dokumentcijban olvasott Items removed from a list widget will not be managed by Qt, and
will need to be deleted manually. (a listWidget-bl eltvoltott listaelemeket a Qt nem kezeli,
kzzel kell ket trlni) kittellel, akkor sejteni kezdjk, hogy van mg dolgunk. De menjnk
biztosra, rjuk t a slot-unk trzst efflekpp:
QListWidgetItem *lwi = ui>listWidget>takeItem(ui>listWidget->
currentRow());
qDebug() << lwi;
ui->listWidget->addItem(lwi);

Indtsuk el a programunkat, vegynk fel kt-hrom listaelemet, majd az elst trljk. Egy
pillanatra eltnik (nem is ltjuk), a qDebug() kirja a cmt, majd visszakerl a lista vgre.
Kell ennl jobb bizonytk arra, hogy nem is trltk, csak kivettk?
Ha viszont ez a helyzet, akkor egyrtelm, hogy mi legyen a fggvny trzse:
delete ui>listWidget>takeItem(ui>listWidget>currentRow());

s ksz.
Vagyis dehogy: ezzel az eljrssal csak egy elemet tudunk trlni egyszerre. Az igaz, hogy
egyelre csak egy listaelemet tudunk egyszerre kijellni, azaz ezen kne segtennk elszr.
Taln nem lepdnk meg, ha kiderl, hogy a listWidget tulajdonsgain kell lltanunk,
egszen konkrtan a selectionMode rtkl kell MultiSelection-t megadni. A tbbszrs
kijells megy, de a gomb lenyomsra csak az egyik kijellt elem trldik. Hmmm.
A QListWidget osztly dokumentcijban keresglve felleljk a selectedItems()
tagfggvnyt, s ha mr gy esett, rhatunk egy remek ciklust. A selectedItems() visszatrsi
rtke egy QListWidgetItem osztly mutatkat tartalmaz QList, ezt kell bejrnunk.

22
4. Akinek nincs esze... Qt strandknyv

rtsk ki a slot-unk trzst: kezdjk ellrl. A QList osztly objektumokat gy tudjuk


deklarlni, hogy megadjuk a lista elemeinek tpust is, ezek ezttal ugye QListWidgetItem
osztly objektumokra mutat mutatk lesznek. Az objektumunkat rgtn fel is tltjk. A fenti
kt mveletet egy sorban vgezzk el:
QList<QListWidgetItem*> toBeDeleted = ui>listWidget->
selectedItems();

A lista egy egyszer for-ciklussal bejrhat, s minden egyes tagra hvhat a takeItem()
tagfggvny, ami persze az elem sornak a szmt vrja bemenetknt, neknk olyanunk meg
nincsen. Szerencsre a QListWidget osztlyban megvalstottk a row() tagfggvnyt,
ami sorszmot ad vissza, ha megadjuk a QListWidgetItem osztly objektum mutatjt,
s mutatink trtnetesen vannak is, benne a toBeDeleted listban. Azaz a slot trzsnek
maradk sorai a kvetkezk:
for(int i = 0; i < toBeDeleted.size(); i++)
delete ui>listWidget>takeItem(ui>listWidget>row
(toBeDeleted.at(i)));
Mindez persze nem olyan elegns, viszont jl tlthat. A kvetkez megolds nem olyan
egyrtelm, de bizonyos szemszgbl szebb mint az elz. Ebben a megoldsban a slot trzse
egyetlen sor:
qDeleteAll(ui>listWidget>selectedItems());

A qDeleteAll() egy nem kifejezetten agyondokumentlt fggvny, de kis keresglssel


kiderl, hogy valjban mutatkat trl. Tekintve, hogy a selectedItems() tagfggvny a
kijellt elemek QListWidgetItem* tpus mutatinak listjt adja vissza, a qDeleteAll()
fggvny remek szolglatot tesz neknk.
Levezetsl mg lltsuk be a tab-sorrendet, azaz azt, hogy a grafikus elemek kztt milyen
sorrendben ugrlunk vgig a Tab billenty nyomogatsval. Az ikon mr megint az Edit
Signals/Slots mellett van, keressk meg, s pr kattintssal lltsuk be a megfelel sorrendet.
Ezzel a ToDoList els vltozata ksz is. Remlhetleg a hrcsgnk mostanra kialudta magt,
s a motoszklsval nem hagy bennnket aludni. Pedig rnk fr, nemdebr?

23
Qt strandknyv 5. Mentsk, ami menthet!

5. Mentsk, ami menthet!

Mr a mltkor is motoszklt a fejnkben valami halvny gyan, hogy ugyan mi rtelme


az olyan listnak, amit minden alkalommal jra kell rni, de akkor el voltunk foglalva holmi
pajtsokkal, tab-sorrenddekkel, meg mutatlistk trlsvel. Most azonban semmi sem llhatja
tjt annak, hogy teendinket megrktsk. Kbe vssrl persze sz sincs, megelgsznk
azzal is, ha opercis rendszernk httrtrn elhelyezhetjk ket egy fjlban.
A mentsi mveleteknek ezer ve kt menpont a kiindulsa: a Ments s a Ments
msknt.... A kt menpont mkdse szorosan sszefgg, s nem is trivilis, hogy pontosan
mely pontokon. gyhogy bevetjk azt az eszkzt, amit az els programozs-rink ta nem
hasznltunk: folyamatbrt ksztnk. Voil:
Ezt a sok mveletet (s a betltsrl mg nem is beszltnk) j volna elklnteni, gyhogy
kln osztlyt valstunk meg nekik. gy kezdnk hozz, hogy a fjl-fban a Sources rszre
vagy a projekt nevre kattintunk jobb egrgombbal. A helyi menbl az Add New... pontot
vlasztjuk, majd a prbeszdablakban a C++, azon bell a C++ Class lehetsget adjuk meg.
Az osztly neve legyen FileOperator, az alaposztlya pedig a QWidget8.

Ments Ments msknt

Van fjlnv nincs Fjlnv-prbeszdablakot


trolva? nyitunk

van

elvgezzk a mentst igen


Kaptunk fjlnevet?
a megadott fjlnvvel

nem
troljuk a fjlnevet
egy vltozban hagyjuk a francba

ments ksz

8. bra: A mentsi folyamat

8 Majdnem gond nlkl lehetne QObject is, de egy esetben kelleni fog neki a QWidget s. A
QWidget annyival tbb egy sima QObject-nl, hogy elmondani is nehz: ez az osztly se min-
den olyan osztlynak, amelyekbl a grafikus felhasznli fellet sszell.

24
5. Mentsk, ami menthet! Qt strandknyv

s, mg a komoly munka megkezdse eltt hasznlatba is vesszk az osztlyt: a


mainwindow.h fjlban megadjuk a fejlcek kztt a fileoperator.h-t, s deklarlunk
az objektumunknak egy privt mutatt fileOperator nven. A MainWindow objektum
konstruktorban pedig ltrehozzuk az objektumot, nem feledve magt a MainWindow-t (this)
megadni szlobjektumknt.

5.1. QStringList a feladatok tadsra


A feladatok jelenleg a listWidget belsejben laknak, mghozz gy, hogy a
QListWidgetItem tpus objektumok text() tagfggvnye tudja ket visszaadni.
Deklarljunk ht egy QStringList visszatrsi rtk, createThingsToDoList() nev
privt fggvnyt, s rjuk meg a trzst is. Ne felejtkezznk meg a <QStringList> fejlc
hasznlatrl.
QStringList MainWindow::createThingsToDoList()
{
QStringList list;
for(int i = 0; i < ui>listWidget>count(); i++)
list << ui>listWidget>item(i)>text();
return list;
}

5.2. Menk s QAction-k


Aztn, mieltt a FileOperator osztlyt hizlalni kezdennk, elksztjk a ment. Nyissuk
meg a grafikus szerkesztben a mainwindow.ui fjlt, s az ablakon a Type Here (gpelj ide)
rszre kattintva alaktsuk ki az brn lthat ment.
A men neve s a menpontok megjelennek az Object
Inspector hierarchijban. Kezdjk azzal, hogy tnevezzk
ket: az objektumok ugyanis nem kaphatnak kezetes
karaktereket tartalmaz nevet, s az kezetes karaktereket
a Qt Creator alvonssal (underscore) helyettesti, ami tvol
ll a szmunkra optimlistl. Kzben vegyk szre, hogy a
menn bell a Ments s a Ments msknt... osztlya
QAction lett. 9. bra: A Fjl men
Ha az tkeresztelgetsekkel elkszltnk, a fablak rajza alatti Action Editor rszen a
Ments kaphat egy Ctrl+S billentykombincit. Ugyanitt tudunk a kt action szmra slot-
ot ltrehozni a helyi menbl az immron megszokott Go to slot... menpont vlasztsval.
A prbeszdablakbl kivlaszthat esemnyek kzl mindkt esetben a triggered()
hasznlatt javasoljuk. S ha sikeresen ltrejtt a kt slot (ha akarjuk, letesztelhetjk a
mkdsket egy-egy
qDebug()-zenettel), visszatrhetnk a FileOperator osztly megvalstshoz.

5.3. A FileOperator osztlyunk QFileDialog, QMessageBox s egyb


veszedelmek
Mire is lesz szksgnk? Kelleni fog egy QString osztly privt fjlnv tletesen a
fileName nevet vlasztjuk. Kelleni fog egy save() s egy saveAs() tagfggvny mindkett

25
Qt strandknyv 5. Mentsk, ami menthet!

publikus, ezek lesznek a folyamatbrn is lthat belpsi pontok. Mindkett fggvny


paramtere a feladatokat trol QStringList osztly objektum lesz. Mindkt osztly
hasznlatt jeleznnk kell a fejlcek kztt a fileoperator.h fjlban. Kelleni fog tovbb egy
performSaveOperation() privt tagfggvny, amelyiket a folyamatbrn a rombusz s az
alatta lv tglalap jelkpez. Ez a fggvny vgzi majd a munka dandrjt, s kt helyrl is
hvhatjuk, de csak az egyik esetben (ments) van trolt fjlnevnk, a msik esetben (ments
msknt) mg nincs, vagy nem azt akarjuk hasznlni, gy innen j volna tadni neki. gy
rezzk, hogy tlzs volna tlterhelni ezt a tagfggvnyt, azaz abban maradunk, hogy
inkbb mindkt esetben tadjuk majd neki a fjlnevet, s ennek rmre fel is tntetjk
a paramterlistjban. A vltozkat s a tagfggvnyeket helyezzk el a fileoperator.h
llomnyban, aztn kezdjnk hozz a save() tagfggvny megvalstshoz.
A save() trzse teljes ngy sor (figyeljk meg, hogy a folyamatbrtl egy hangynyit
eltrnk, amennyiben fjlnv hinyban a msik belpsi pontra irnytjuk t a mveletet
persze a belpsi pont s a prbeszdablak megnyitsa a valsgban egybecsszik, szval nem
is biztos, hogy eltrtnk):
void FileOperator::save(QString thingsToDo)
{
if(fileName.isEmpty())
saveAs(thingsToDo);
else
performSaveOperation(fileName, thingsToDo);
}

Akkor ht a saveAs() tagfggvnnyel folytatjuk a mkt. Szksgnk lesz a QFileDialog


osztly getSaveFileName() statikus tagfggvnyre, azaz helyezzk el a <QFileDialog>-ot
a betltend fejlcek kztt.
Folytassuk a performSaveOperation() tagfggvny megvalstsval, de egyelre csak
egy olyan vltozattal, ami csak kirja, hogy hova mentene, s belltja a fileName vltoz j
rtkt. A teljes fggvny gy nz ki:
void FileOperator::performSaveOperation(QString fn, QStringList list)
{
qDebug() << "Saving to:" << fn;
fileName = fn;
}

Ha zavar bennnket, hogy a fordts sorn figyelmeztetst kapunk a nem hasznlt list
vltoz miatt, akkor a fggvnytrzs els soraknt adjuk meg, hogy:
Q_UNUSED(list)

A makrnak a tesztelsi idszakon tl akkor van nagy rtelme, amikor egy beptett
fggvny, ha kell, ha nem tad neknk valamit, amit knytelenek vagyunk el is venni, de
egybknt nem igazn van r szksgnk.
Mostanra mr a hrcsg szerint is csak a saveAs() tagfggvny megvalstsa lehet htra.
Hasznlni szeretnnk pr fejlcfjlt: a <QDir>, a <QFileInfo>, s a <QFileDialog> fejlcrl
van sz.
A QFileDialog osztlybl egy statikus fggvnyt, a getSaveFileName() nevt
hasznljuk arra, hogy megjelentsk a clfjl kivlasztsra szolgl ablakot. A fggvny

26
5. Mentsk, ami menthet! Qt strandknyv

paramterlistjnak els tagja a szlobjektum, aminek QWidget osztlynak kell lennie ht


ezrt dntttnk annak idejn gy, hogy a FileOperator osztlyunk se a QWidget s nem a
QObject.
A fggvny tovbbi paramterei kztt szerepel az a knyvtr is, ahol a prbeszdablak
megnylik. Igen m, de hol is nyljon meg? Ht, ha ez az els ments, akkor legyen mondjuk a
felhasznl sajt mappja, ha meg mr volt, akkor ott, ahova az utols fjlt mentettk. A sajt
mappa elrsi tja platformfggetlenl megtudhat a a QDir osztly statikus tagfggvnynek
hasznlatval. Az utols fjl teljes elrsi tjbl pedig mindenfle karakterlnc-varzslattal
ki tudjuk nyerni a tartalmaz mappt, de most lustk vagyunk, s klnben is a QFileInfo
osztly pp erre j. Azaz elsknt vgezznk az elrsi t belltsval:
QString path = QDir::homePath();
if(fileName != ""){
QFileInfo info1(fileName);
path = info1.absolutePath();
}

Ok, eddig megvagyunk. Megnyithatjuk a prbeszdablakot, aminek a visszatrsi rtke


a teljes elrsi utat is tartalmaz fjlnv. A pldban szndkosan helyeznk el minden
paramtert j sorban, mert annyi van bellk, hogy az tbb a soknl s mg nem is adtuk
meg mindet. Termszetesen csak azrt hasznlunk tbbfle lehetsges kiterjesztst, mert ez
egy strandknyv, s minden strandknyvben gy szoks, meg kicsit azrt is, mert gy jobban
be tudjuk mutatni a QFileDialog::getSaveFileName() tagfggvny lehetsgeit. A
paramterek sorban: szlobjektum, ablak cme, az a mappa, ahol az ablak megnylik, s a
fjltpusok. Utbbiakat kt pontosvesszvel vlasztjuk el.
QString fn = QFileDialog::getSaveFileName(
this,
"Ments msknt...",
path,
"ToDoList-fjlok (*.tdolst);;Szvegfjlok (*.txt);;Minden
fjl (*)"
);

Ha a felhasznl a mgse-gombra kattint, vagy ezzel egyenrtken cselekszik, a fggvny


res karakterlnccal tr vissza. gy a kvetkez lpsnk annak ellenrzse, hogy akkor
vgs soron van-e fjlnevnk. Ha nincs fjlnv, akkor egyszeren befejezzk a fggvnyt
s visszatrnk a fablakhoz. Ha van fjlnv, akkor megnzzk, hogy van-e kiterjesztse9
megint egy QFileInfo osztly objektumot hasznlunk a feladat kivitelezsre. Ha nem
volna, akkor odabiggyesztnk egyet. Mikor mindezzel megvagyunk, meghvjuk a flksz
performSaveOperation() tagfggvnyt.

9 Logikusnak tnhet, hogy a belltott fjltpusnak megfelel kiterjeszts automatikusan kerljn


r a fjl vgre. A Qt prbeszdablaka itt nem segt, aminek okait fknt a Qt multiplatform
mivoltban kell keresnnk. Van opercis rendszer, ahol tulajdonkpp a beptett mentsi ablak
nylik meg, de van, ahol nincs ilyen, s ott a Qt adja az ablakot.

27
Qt strandknyv 5. Mentsk, ami menthet!

if(!fn.isEmpty()){
QFileInfo info2(fn);
QString ext = info2.suffix();
if(ext != "tdolst" && ext != "txt")
fn += ".tdolst";
performSaveOperation(fn, thingsToDo);
}

A saveAs() tagfggvny ezzel ksz. Visszakutyagolhatunk a mainwindow.cpp fjlba, s a


kt slot-ban megadhatjuk a megfelel fggvny hvst:
void MainWindow::on_actionMentes_triggered()
{
fileOperator>save(createThingsToDoList());
}

void MainWindow::on_actionMentesMaskent_triggered()
{
fileOperator>saveAs(createThingsToDoList());
}

Ki is prblhatjuk ket. Persze vals ments nem fog trtnni, de figyelhetnk a megfelel
fjlnv kpzsre, megjelentsre, a msodik-harmadik-sokadik mentskor a ktfle mentsi
md megfelel viselkedsre. Mennie kell.
A hrcsg pointere az asztalunkon lv kekszekre mutat vagy akkor ez most a keksz
pointere? Mg szerencse, hogy nem kutynk van. Mondjuk egy pointer.
A performSaveOperation() tagfggvny rendes megvalstsa irnyba tereljk csapong
gondolatainkat. A feladatokat nyilvn gy volna rdemes mentennk, hogy soronknt egy
feladatot mentnk, gy a fjl ms szoftverbl is kezelhet marad. A fjlok rsa-olvassa Qt-ban
a QFile osztly hasznlatval trtnik gy aztn szksgnk lesz a hasonl nev fejlcfjlra.
A fjl megnyitsakor bellthatunk nhny jelzbitet (angolul flag), amelyeket bitenknti
vagy mvelettel ktnk ssze. Az gy elll szm pontos reprezentcija lesz annak, hogy
mely kapcsolkat, jelzbiteket adtuk meg. Persze a jelzbiteknek megvan a szveges konstans
vltozatuk is, gy nem kell fejben tartanunk, hogy az rsra megnyits pldul a 0x0002, az
meg, hogy szvegfjlknt nyissuk meg, s a soremelsek az opercis rendszernek megfelelen
konvertldjanak (soremels Unix-okon, soremels s kocsivissza Windows-on), a 0x0010 jelzbit.
Ezeket a jelzbiteket bitenknti vaggyal sszekapcsolva 0x0012-t kellene megadnunk a kvetkez
pldban. Szerencsre nem szorulunk arra, hogy a jelzbiteket folyamatosan fejben tartsuk,
erforrsainkat nem kell kdolsukra s dekdolsukra pazarolnunk; ltjuk majd, hogy szp
olvashat formban is kifejezhetjk kvnsgainkat. Mg hozztesszk, hogy azrt rtekeztnk
m ilyen hosszan a mdszerrl, mert a Qt elszeretettel hasznlja tbb osztlyban is.
Az adatok soronknti kirst knnyti meg szmunkra a QTextStream osztly. Ne felejtkez-
znk el a fejlcrl.
Mr csak egy dolgot kell tgondolnunk, mieltt nekikezdennk a performSaveOperation()
tagfggvnyhez, nevezetesen azt, hogy figyelnnk kne r, hogy vgl sikerl-e a ments.
Gondolnnk-e vagy sem, a Qt legtbb modulja nem kezel kivteleket, azaz aki arra gondolt,
hogy most aztn jhetnek a j kis try-catch blokkok, bizony ki kell brndtanunk. s hogy
mirt nem kezeli a Qt a kivteleket? Azrt, mert annyira multiplatformos. Hasznltk olyan
platformon is, ahol a platform szmra ismeretlen volt a kivtel fogalma, a kivtelkezels

28
5. Mentsk, ami menthet! Qt strandknyv

mechanizmusa gy kivtelkezelst csak pr jabb modultl vrhat az ember. Mi pedig


maradunk a j reg vltozlltgats mdszernl, de azrt szlunk a felhasznlnak, ha baj
trtnt. Mondanivalnk kzlsre a QMessageBox osztlybl pldnyostunk objektumot
persze csak a fejlcnek hasznlatba vtelt kveten.
No, akkor ht:
void FileOperator::performSaveOperation(QString fn, QStringList list)
{
QFile file(fn);
bool success = false;
if (file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
QTextStream out(&file);
for(int i = 0; i < list.count(); i++)
out << list.at(i) << endl;
if(file.error() == 0)
success = true;
}
if(success)
fileName = fn;
else{
QMessageBox mb;
mb.setIcon(QMessageBox::Critical);
mb.setText("Nem sikerlt a ments.");
mb.setInformativeText("Prblj valami okosat tenni.");
mb.exec();
}
}

Minekutna ltrejn a fjlnv felhasznlsval a file objektum, s a sikeres mveletet


jelz vltoznak belltottuk a kezdeti hamis rtket, megprblkozunk a fjl rsra val
megnyitsval. Ha sikerlt, akkor egy for-ciklussal vgigjrjuk a QSringList osztly listt,
s az egyes sorokat a fjlba rjuk. Az rssal a program akkor is prblkozik, ha idkzben
mondjuk elfogy a hely, vagy egyb szrnysg trtnik, de ez esetben a file objektum
FileError-szma nulltl eltr lesz. A lehetsges hibakdokat a QFileDevice osztly
dokumentcija tartalmazza.
Ha a mvelet sikeres, akkor belltjuk a fileName vltoz rtkt, ha nem, akkor
megjelentnk egy zenetet arrl, hogy prul jrtunk. A fjlt nem szksges lezrnunk,
ugyanis a QFile osztly destruktora elvgzi ezt a mveletet, mrpedig ahogy a hatkrbl
kilpnk, a stack-ben ltrehozott objektumok destruktora meghvdik.
Akinek kedve van, megrhatja, hogy a program a fablak llapotsorn jelezze a fjlmveletek si-
kert, illetve sikertelensgt neki az A QMap s a ksznsek cm rszben ttekintettek lesznek
nagy segtsgre. Meg kell adnia a signal-t, ami a sikerrl tjkoztat, a performSaveOperation()
tagfggvnyben el kell helyezni a megfelel emit-parancsokat, illetve ki kell alaktania a kapcso-
latot az llapotsor slot-ja s a fileOperator objektum signal-ja kztt.
Hasonl signal-slot mechanizmus kialaktsval megoldhat az is, hogy a fablak cmsorba
kirdjk az pp hasznlatban lv fjl neve.
A mentssel emgyen vgeztnk is. Ha eddig nem adtuk oda a hrcsgnek a kekszet, most
mr igazn megkaphatja. Mi pedig folytassuk a betltssel, amit kvetve az elmlt hsz v
divatjt megnyitsnak fogunk nevezni.

29
Qt strandknyv 5. Mentsk, ami menthet!

Alaktsuk ki a helyt a menben, rendeljk hozz a szoksos billentykombincit, s


llttassuk el a slot-ot, ami majd kezeli az esemnyt.
Mit is kell tennie ennek a slot-nak? Elszr is trlnie kell a listWidget sorait. A kvetkez
lps a fjl betltse, s a benne lv sorok megszerzse. A sorokat alighanem a fileOperator
objektumtl fogjuk elkrni, megint csak QStringList osztly objektum formjban.
Persze, ha a betlts sikertelen, a felhasznl meggondolja magt, vagy hasonl szrnysg
trtnik, esetleg rlnnk, ha mgsem trltk volna ki a jelenlegi listt. rdemes lesz teht
ezt a kt feladatot felcserlnnk. Az utols teend pedig a megszerzett feladatlista betltse a
listWidget-be.
Kezdjk a munkt a FileOperator osztly legjabb tagfggvnyeivel. Az open() fggvny
feladata lesz a kvnt fjlnv kidertse, s ennek ismeretben a performLoadOperation()
tagfggvny hvsa. Ez utbbi QStringList osztly objektumban adja vissza a fjlbl
beolvasott sorokat, amit aztn az open() szpen tovbbad az hvjnak. Az open()
tagfggvny egybirnt igen hasonl lesz a saveAs() nev trshoz:
QStringList FileOperator::open()
{
QString path = QDir::homePath();
if(fileName != ""){
QFileInfo info(fileName);
path = info.absolutePath();
}
QString fn = QFileDialog::getOpenFileName(
this,
"Fjl megnyitsa...",
path,
"ToDoList-fjlok (*.tdolst);;Szvegfjlok
(*.txt);;Minden fjl (*)"
);
QStringList thingsToDo;
if(!fn.isEmpty())
thingsToDo = performLoadOperation(fn);
return thingsToDo;
}

Ezttal is abbl indulunk ki, hogy amennyiben mr van betlttt fjlunk, felttelezzk, hogy
a mostani megnyitsnl ugyanabbl a mappbl szndkszik a felhasznl egy msik fjlt
megnyitni, mondjuk azrt, mert az elz mgsem az volt, amit keresett. Ezttal termszetesen
az erre a feladatra kszlt statikus tagfggvnyt hasznljuk a QFileDialog osztlybl, aminek
a paramterezsben az elzekhez kpest semmi klnbsget nem tallunk. Ha kaptunk
fjlnevet, megksreljk a fjl betltst, de azt mr msik tagfggvnyre bzzuk. Mindenkpp
visszaadjuk a frissen ltrehozott thingsToDo objektumot, adott esetben akr resen is.
A vals munkt vgz performLoadOperation() tagfggvny is sok ponton hasonlt a
mentskor hasznlt prjra:

30
5. Mentsk, ami menthet! Qt strandknyv

QStringList FileOperator::performLoadOperation(QString fn)


{
QStringList thingsToDo;
QFile file(fn);
bool success = false;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
while (!in.atEnd())
thingsToDo << in.readLine();
if(file.error() == 0)
success = true;
}
if(success)
fileName = fn;
else{
QMessageBox mb;
mb.setIcon(QMessageBox::Critical);
mb.setText("Nem sikerlt a megnyits.");
mb.setInformativeText("Prblj valami okosat tenni.");
mb.exec();
}
return thingsToDo;
}

A fjl megnyitst ezttal olvassra s szvegfjlknt vgezzk ez utbbi hangslyozsa


megint a fjlban lv sorvgek opercis rendszerenknti ms-ms jelzse miatt j tlet. Az
elz esethez hasonlan figyelnk arra is, hogy megnyithat-e a fjl s arra is, hogy a beolvass
rendben lezajlott-e. Amennyiben igen, belltjuk a fileName vltozt, hogy a mdostsok
mentse egyszeren megoldhat legyen a Ctrl+S billentykombinci lenyomsval. Ha meg
valami gikszer volt, a felhasznlra hagyomnyozzuk a problma elhrtsnak feladatt. Ebbl
a tagfggvnybl is sz nlkl visszaadjuk az res feladatlistt is majd kezeljk a helyzetet a
fablak slot-jban.
Br pr bekezdssel elbb megbeszltk, hogy a Megnyits menpont kivlasztsakor
viszonylag sokrtek az elvgzend feladatok, maga a slot valszntlenl egyszer:
void MainWindow::on_actionMegnyitas_triggered()
{
QStringList thingsToDo = fileOperator>open();
if(!thingsToDo.isEmpty()){
ui>listWidget>clear();
ui>listWidget>addItems(thingsToDo);
}
}

A trzs els sora deklarlja s inicializlja a feladatlistt trol karakterlnc-listt. Tbbszr


emltettk mr, hogy esetleg res listnk lesz, gy a tovbbi lpsek vgrehajtsa eltt
ellenrizzk, hogy valban gy jrtunk-e. Ha igen, akkor a ttlensg blcs tjt vlasztjuk.
Ha azonban van listnk, egy elegns hvssal kitakartjuk a listWidget objektumot. Utna
pedig pp ciklusrsba fognnk, a ciklusmagban addItem() tagfggvny-hvsokkal, mikor a
QListWidget osztly dokumentcijban rakadunk az addItems() tagfggvnyre, amely
trtnetesen pp QStringList osztly objektumot vr bemeneti rtkknt. Mint a mesben!

31
Qt strandknyv 5. Mentsk, ami menthet!

Menteni s megnyitni remekl tudunk, de mi a helyzet, ha j listt rnnk? Hozzunk


ltre neki menpontot, lltsunk be billentykombincit, majd keressk meg a Go to slot...
lehetsget. Eddig biztos. Mi a teendnk mg? Ht elszr is a listWidget kirtse. Ez
viszonylag gyorsan megvan: a slot trzsben helyezzk el a
while(ui>listWidget>count()>0)
{
delete ui>listWidget>takeItem(0);
}

blokkot. A felletes szemll azt gondoln, hogy el is kszltnk a feladatunkkal, de


szoks szerint ms a helyzet. Ha ugyanis trtnt mr fjlmvelet a program indtsa ta,
akkor a fileName vltoz rtke nem res karakterlnc, ami azt eredmnyezi, hogy a Ments
menpont vlasztsval gy fellrjuk a fjlunkat, hogy csak na. Persze a fileName vltoz
a fileOperator objektumunkban lapul, s radsul privt, ami jl is van gy. Ksztsk el a
FileOperator::newList() tagfggvnyt, aminek a trzse egyetlen sor lesz majd:

fileName = ";

Ha elkszltnk, a fenti slot trzsnek utols utastsaknt adjuk ki a tagfggvnyt hv


fileOperator>newList();

utastst.
Nmi tesztelgets utn vgre felkelnk egy kicsit, s elgedetten einstandot10 jelentnk be az
aranyhrcsg maradk kekszeire. Aztn visszajvnk mg pr percre, utna meg gy is vge a
fejezetnek.

5.4. Itertor la STL s la Java na s a foreach


Az imnt rvendeztnk azon, hogy milyen remek dolog is egy hirtelen jtt addItems()
tagfggvny. Ugyan hov is lettnk volna nlkle? Ht lssuk, mghozz ngyszer. Az
albbiakban ugyanis kpesek lesznk ngyflekpp is vgigjrni a thingsToDo nev,
QStringList osztly objektumot. A kzlt kdrszlet mind a ngy esetben az on_
actionMegnyitas_triggered() slot trzsnek utols, az addItems() fggvnyt hv sort
cserli le.
Az els vltozat egyszer for-ciklus:
for(int i = 0; i < thingsToDo.size(); i++)
ui->listWidget->addItem(thingsToDo.at(i));

A thingsToDo.at(i) kifejezs majdnem egyenrtk a thingsToDo[i] vltozattal.


rtkadsra azonban csak az utbbi hasznlhat.
A msodik vltozat gynevezett STL11-fle itertorral dolgozik:
QStringList::const_iterator ci;
for (ci = thingsToDo.constBegin(); ci != thingsToDo.constEnd(); ++ci)
ui->listWidget->addItem(*ci);
A harmadik vltozat a Qt 4-ben megjelent Java-fle itertor segtsgvel jrja be a

10 http://hu.wikipedia.org/wiki/Einstand
11 Az STL a C++ Standard Template Library (szabvnyos sablonknyvtr) nv rvidtse

32
5. Mentsk, ami menthet! Qt strandknyv

thingsToDo objektumot:

QStringListIterator ji(thingsToDo);
while (ji.hasNext())
ui->listWidget->addItem(ji.next());

Figyeljk meg, hogy egyrszt j osztlybl pldnyostunk objektumot, s gy jutunk


itertorhoz, (ugye nem felejtkeznk el a fejlcrl?), msrszt, hogy ez az itertor az elztl
eltren nem egy adott elemre mutat, hanem elemek kz. gy mr van rtelme annak, hogy a
kvetkez (next) elembl kpeznk elemet a listWidget szmra.
Az STL s a Java tpus itertorok elnyeirl, htrnyairl igazn remek sszefoglal
tallhat a http://doc.qt.digia.com/qq/qq12-qt4-iterators.html weboldalon. Kr volna kihagyni.
Vgl, de utolssorban emltjk meg a tbb nyelvbl is ismert foreach kulcsszt. Qt-ban a
kvetkezkpp hasznlhat:
foreach(QString thing, thingsToDo)
ui->listWidget->addItem(thing);

Ltjuk, hogy ez a mdszer azoknak val, akik szeretik a dolgokat a nevkn nevezni. A
teendk listjbl minden krben a ciklus minden ismtldsekor kivesznk egy konkrt
teendt, s azzal gykdnk valamit. Nem indexszel, nem hivatkozva, nem egy lista kvetkez
elemeknt aposztroflva, hanem pusztn a neve alapjn gondolhatunk r. Ez a mdszer gy
segti gondolkodsunkat, hogy kiiktat a belle egy absztrakcis szintet ami azrt nha
nagyon jl jn.
Alighanem mg a hrcsgnek is feltnt, hogy az eredeti megoldsnl most mind a ngy
mdszer tbbet problmzik, de ez kivteles eset. A legritkbb esetben lesz olyan mzlink, hogy
megsszuk egy gyjtemnyes adattpus elemeinek bejrst.

33
Qt strandknyv 6. A feladatokhoz kategrikat rendelnk

6. A feladatokhoz kategrikat rendelnk

Idkzben annyi teendnk lett, hogy nem gyzzk ket ttekinteni, s az a fnyes tletnk
tmadt, hogy mindegyiket besoroljuk valamilyen kategriba. A programnak igyeksznk meg-
hagyni az eddigi szerkezett, azaz azt, hogy egy osztly kezeli a fjlt, s a fablak dolga a tarta-
lom tvtele s megjelentse. Ezentl azonban nem QStringList osztly objektumot mozga-
tunk a kt programrsznk kztt, ugyanis a feladatok mr nem QString-ek. Ltrehozunk egy
j osztlyt, s ebbl pldnyostjuk azokat az objektumokat, amelyek az egyes teendket s a
hozzjuk tartoz kategrit troljk.
llapodjunk meg abban, hogy mostantl egy feladat (task) egy teendbl (job) s a hozz
tartoz kategribl (category) ll.

6.1. A Task osztly


A Task osztlyt egy teljesen j projektben rjuk meg, mert utna mg lesz vele kis bajunk, s
ezzel jobb, ha az eddigi kdunktl levlasztva ismerkednk meg s lesznk rr rajta. gyhogy
kezdjnk j projektet a szoksos mdon. Hozzunk ltre egy j C++ osztlyt Task nven.
sosztlyknt adjuk meg a QObject-et.
Kis Qt-stilisztika kvetkezik: Qt-ban gy szoks osztlyokat megvalstani, hogy a tagvltoz
neve el valamilyen elttet tesznk, s amit a tagvltoz nevl sznnnk, az valjban a
kiolvas (getter), azaz a tagvltoz rtkt visszaad fggvny lesz. Az elttek lehetnek arra
utalk, hogy ez egy tagvltoz, teht j eltt pldul az "m_". A bellt, rtkad (setter)
fggvnyek neve pedig a set szbl s a tagvltoz nevnek nagybets vltozatbl ll.
Akkor ezt most lesben. A Task osztlynak kt privt tagvltozja lesz, az "m_job" s
az "m_category", mindkett QString tpus. Lesz kt kiolvas fggvnye, a job() s a
category(). Mindkett visszatrsi rtke QString tpus, s egyetlen sorbl ll trzsk
a hasonnev vltoz rtkt adja vissza. Lesz kt bellt fggvnye is az osztlynak, a
void visszatrsi rtk setJob() s a setCategory(). Mindkett paramtere QString,
s egyetlen rtkad utasts szerepel bennk, amely a hasonnev tagvltoz rtkl a
paramterknt kapott karakterlncot adja meg. Termszetsen mind a ngy tagfggvny
publikus.
A mainwindow.h fjl elejn, a fejlcek kztt helyezzk el a task.h llomnyt.

6.2. A QList<T> sablonosztly, s nmi rlet a mutatkkal.


Megismerkednk a QSharedPointer osztllyal
A QList<T> teht egy sablonosztly. Sablonosztly? Az meg mi a nyavalya? Valami olyasmi,
amelyben megadhatjuk, hogy a ltrehozott pldny milyen tpus (igen, ez a nagy T) adatokat
kezeljen. A QList<T> teht olyan osztly, amelynek pldnyai T tpus objektumokat trolnak.
A mainwindow.h fjlban adjuk meg a <QList> fejlcet, majd deklarljuk a privt tasks
listt. gy:
QList<Task> tasks;

Adjunk meg tovbb egy createTasks() tagfggvnyt is. A megvalstsa a kvetkez:

34
6. A feladatokhoz kategrikat rendelnk Qt strandknyv

QStringList jobs = (QStringList() << "job1" << "job2" << "job3");


QStringList categories = (QStringList() << "cat1" << "cat2" <<
"cat3");

for(int i = 0; i < jobs.size(); i++){


Task t;
t.setJob(jobs.at(i));
t.setCategory(categories.at(i));
tasks.append(t);
}

A blokk elejn kes pldjt ltjuk annak, ahogy az ember QStringList tpus objektumot
llt el s tlt fel (st, rtkl is ad) egyetlen sorban. A recept a kvetkez: vgy egy res
QStringList-et, adj t neki QString-eket a << opertorral, s tedd zrjelbe az egsz
hbelevancot, hogy a mveletek az rtkad mveleti jel munkba lpse eltt vgbe is
menjenek.
A maradk rszben, azaz a ciklusban Task tpus objektumokat hozunk ltre. Beljk
pakoljuk a megfelel teendket s kategrikat (esznkbe jut, hogy j volna egy
paramterezhet tlterhelt konstruktort is rni az osztlynak), majd a QList<Task> osztly
tasks objektumhoz fzzk ket.
Akkor most fordtsuk le.
Brr! Hogyaszongya: error: QObject::QObject(const QObject&) is private. Mg a legtbben
csak a szemnket drzsljk ilyen remek hibazenet lttn, a szemflesek gy kiltanak fel:
-Ojj, hiszen ez egy privt msolkonstruktor! Ezek szerint a QObject osztly leszrmazottai
nem msolhatk!
rdgk van. No de korbban keljen fel az, aki rajtunk (gy) akar kifogni. Deklarljuk gy a
QList-et, hogy ne Task osztly objektumokat, hanem a rjuk mutat mutatkat tudja trolni:

QList<Task*> tasks;

s persze a remek tagfggvnynket is trjuk, legalbbis a ciklus belsejben lv els hrom


sort.
Task *t = new Task();
t>setJob(jobs.at(i));
t->setCategory(categories.at(i));

A t immron mutatv avanzslt (vagy degradldott: nzpont krdse), ennek megfelelen


a bellt tagfggvnyeket mr nem ponttal, hanem nyllal ktjk. s vgre lefordul az
osztlyunk!
A hrcsg extzisban rgja a dibelet. Elszr mg mi is, de aztn kellemetlen gyan
fszkeli magt kicsi szvnkbe. Mi lesz a sok objektumunkkal, ha a QList osztly tasks
objektum elhallozik, akrmirt is? Gondolkodjunk: trldik egy raks mutat. s maguk az
objektumok? De nem m! Na ezt hvjk memriaszivrgsnak.
Tbbek kztt ilyen esetekre talltk ki a smart pointereket (okos mutatkat). A smart
pointer12 attl olyan smart, hogy automatikus forrsfelszabadtst vgez, azaz ha mr sehol

12 A Qt smart pointereirl remek sszefoglals olvashat itt http://blog.qt.digia.com/


blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/ s itt
http://www.macieira.org/blog/2012/07/continue-using-qpointer/

35
Qt strandknyv 6. A feladatokhoz kategrikat rendelnk

nem hivatkoznak az objektumra, akkor megsemmisti azt. A Qt sok smart pointere kzl mi a
QSharedPointer osztlyt hasznljuk fel.
A mainwindow.h llomnyban adjuk meg a <QSharedPointer> fejlcet is, a tasks
deklarcijt pedig alaktsuk t ilyenkppen:
QList<QSharedPointer<Task> > tasks;

Ne felejtsk ott a csillagot, s figyeljnk arra is, hogy a kt zr > karakter kztt legyen szkz.
A mainwindow.cpp fjlban a Task *t = new Task(); sort pedig vltsuk fel ezzel:
QSharedPointer<Task> t(new Task());

Nincs egyenlsgjel.
Az gy ltrehozott mutatt gy hasznljuk a tovbbiakban, mintha a vilg legkznsgesebb
mutatja volna. A hivatkozott objektum tagfggvnyeit is a legkznsgesebb -> nyilakkal
hvjuk a pont helyett, gy a tagfggvny maradk rsze nem is vltozik.
A nehezn immr tl vagyunk, gyorsan ellenrizzk le, hogy tnyleg hasznlhat lesz-e gy
az osztlyunk. Kezdjk azzal, hogy megrjuk a tlterhelt, paramterezhet konstruktort, amit
az imnt hinyoltunk:
Task::Task(QString job, QString category, QObject *parent) :
QObject(parent)
{
m_job = job;
m_category = category;
}

Aztn ezt hasznlatba is vesszk, trjuk a createTasks() tagfggvny trzst ilyenre:


QSharedPointer<Task> t(new Task(jobs.at(i), categories.at(i)));
tasks.append(t);

A fablakra hzzunk ki egy QPushButton-t s kt QListWidget-et. Hagyjuk meg az eredeti


neveiket. A szoksos kattintgatsokkal lltsuk el a nyomgomb slot-jt, s benne alaktsuk ki
az albbi trzset:
createTasks();
for(int i = 0; i < tasks.size(); i++){
ui>listWidget>addItem(tasks.at(i)>job());
ui>listWidget_2>addItem(tasks.at(i)>category());
}

Vegyk szre azt, amit elvileg persze tudunk, nevezetesen, hogy a tasks.at(i) mutatt ad
vissza, minek folyomnyaknt a Task osztly tagfggvnyeit a -> opertorral hvjuk.
Tanulmnyprogramocsknk ezzel elkszlt (a forrsa termszetesen ennek a programnak is
elrhet a knyv weboldaln), visszatrhetnk a ToDoList-hez.

6.3. A ToDoList-et felksztjk a kategrik hasznlatra, de mg


nem hasznlunk kategrikat
Most, hogy elolvastuk ezt a remek cmet, s kicsit sszezavarodtunk, taln vilgoss vlik
minden, ha megtudjuk, hogy a konkrt kategrik helyett egyelre egy helyrzt adunk csak
meg. Azzal kezdjk a munkt, hogy a ToDoList projekt mappjba tmsoljuk a task.cpp s

36
6. A feladatokhoz kategrikat rendelnk Qt strandknyv

a task.h llomnyt. A projekt betltst kveten a fjlvlaszt rsz Sources feliratn jobb
egrgombot nyomva az Add Existing Files lehetsget vlasztva a projekt rszv tesszk
ket. A task.h llomnyt a <QList> s a <QSharedPointer> fejlcekkel egytt felvesszk
mind a fileoperator.h, mind a mainwindow.h llomny elejre. Alaktsuk tovbb vissza
a feladattrlst gy, ahogy elszr elksztettk: hogy csak egyetlen elemet trl egyszerre
a gomb. A gomb feliratnak talaktsa a legkisebb, br a legltvnyosabb vltozs. A hozz
tartoz slot trzse legyen ismt:
delete ui>listWidget>takeItem(ui>listWidget>currentRow());

Termszetesen a listWidget tulajdonsgai kzl a selectionMode-ot is vissza kell


lltanunk SingleSelection-ra a tulajdonsgszerkesztben.
s akkor most tlnk a hrcsg mell s mikzben abba a tvedsbe esve, hogy kaja ll a
hzhoz, eljn, elfilzgatunk kicsit a programunkon. Az adatok eddig egyszer karakterlncok
voltak, de ez mr a mlt, s gy a mlt vlik az is, hogy a fablakon bell tbbnyire a
listWidget trolta ket, s csak kzvetlen azeltt emeltk t ket egy lthat megjelensi
formval nem rendelkez objektumba a QStringList osztly thingsToDo-ba , hogy
tadtuk volna az egszet a fileOperator objektumnak ments vgett.
Most azonban ez nem jrhat t: az elz tanulmnyprogramunkhoz hasonlan nll
objektumra van szksgnk az adatok trolsra. Ugyanezt az objektumot kell majd tadnunk
a fileOperator objektumnak ments vgett, radsul megnyitskor is ilyen objektumot
kapunk majd vissza tle.
Otthagyva a hrcsgt kezdjk a munkt azzal, hogy a mainwindow.h llomnyban
privtknt deklarljuk azt a vltozt, amelyik majd a feladatokat trolja. Puskzhatunk az elz
programocskbl, de akr innen is trhatjuk:
QList<QSharedPointer<Task> > tasks;

A createTasks() tagfggvny mehet a kukba, a deklarci csakgy, mint a megvalsts.


Az addButton kivtelvel az sszes slot trzst alaktsuk megjegyzss.
Lssuk, mit alaktunk az addButton slot-jn! gy dntnk, hogy a szerkesztsor tartalmt
kt helyre tesszk. Az egyik mint eddig a listWidget lesz, a msik pedig maga a tasks
lista. Ez al a sor al helyezzk el az a kt sort, amelyik a listhoz is hozzfzi a feladatot:
QSharedPointer<Task> t(new Task(txt, "default category"));
tasks.append(t);

Az els sor ugyebr elllt egy Task osztly objektumra mutat mutatt. Ha mg
emlksznk, runk egy tlterhelt konstruktort is, gy a szerkesztlc tartalmt (a txt vltoz)
s a kategria helyrzjt mr az objektum (t) ltrehozsakor megadjuk. Az objektum mutatjt
pedig utols elemknt hozzbiggyesztjk a tasks listhoz. (Ugye nem fogjuk elkvetni azt a
kezd hibt, hogy a {} kapcsoszrjel-prat elfelejtjk a ciklusmag kr kitenni?)
Ha kiprbljuk mvnket, s eddig minden ok, akkor lssunk neki a trlsnek. Vagy mgis
inkbb a trls megvalstsnak. A QList osztly dokumentcijban hamar megtallja az
ember a takeAt() tagfggvnyt, ami a listWidget takeItem() fggvnye miatt ismersnek
tnik, s valban, ugyangy kiveszi a listbl az indexnek megfelel valamit, visszaadva a
hvnak. Neknk ez a valamint egy mutat, azaz a delete utastssal trlhetjk is a mutat
jelezte objektumot. Ha azonban immron a Qt-dokumentci gyakorlott olvasjv fejldtnk,

37
Qt strandknyv 6. A feladatokhoz kategrikat rendelnk

mr rg szrevettk a takeAt() ismertetse alatt is megbv lsd tovbb (See also) rszt,
benne pedig a removeAt() tagfggvnyt. Ez lesz a mi bartunk!
A QList::removeAt() tagfggvny ugyanazt a szmot vrja paramterl, mint a
listWidget::takeItem(), legalbbis a mi esetnkben. gy a removeButton slot-jnak trzse
a kvetkez formt lti:
int index = ui>listWidget>currentRow();
delete ui>listWidget>takeItem(index);
tasks.removeAt(index);

A Ments, illetve a Ments msknt menpontoknak megfelel slot-on csak annyi vltoztatni
valnk van, hogy a fggvnyhvsok paramtere mostantl a tasks lista.
A Megnyits menpont slot-ja azonban ennl egy fl fokkal bonyolultabb lesz. Elszr is,
mostantl a fileOperator objektum open() tagfggvnye olyan objektumot ad vissza, amit
a tasks objektumunk rtkl kaphat. Br ezt a hangynyi vltozst a FileOperator osztly
mg nem tudja. Meg van mg pr dolog, amit tn nem tud... na, majd mindjrt felvilgostjuk!
A kvetkez vltozs, hogy sajnos le kell mondanunk arrl az addItems() tagfggvnyrl,
amin akkort lelkendeztnk pr oldalnak eltte. Marad helyette a mezei addItem(), amit
csak a plda kedvrt hasznltunk az itertorokkal val ismerkedsnkkor. S ha mr itertor,
legyen mondjuk Java-fle. Ne feledjk, hogy az itertor sem Task objektumot, hanem Task*
mutatt (egszen pontosan QSharedPointer<Task> tpus objektumot) ad vissza, azaz a
tagfggvnyeket most sem a pont opertor hasznlatval tudjuk hvni. A slot trzse az albbi:
tasks = fileOperator>open();
if(!tasks.isEmpty()){
ui>listWidget>clear();
QListIterator<QSharedPointer<Task> > ji(tasks);
while(ji.hasNext())
ui>listWidget>addItem(ji.next()>job());
}

A fablakkal vgeztnk, irny a FileOperator osztly. Az els dolgunk, hogy a


fejlcllomnyban ahol eddig a tagfggvnyek paramtere, illetleg visszatrsi rtke
QStringList osztly objektum volt, ott most lltsunk be QList<QSharedPointer<Task>
> tpust. Radsul taln van rtelme felszmolni a mg mindig egyszer felsorolsra utal
thingsToDo nevet, j lesz helyette a lnyegesen laposabb, de ezttal mgis elfogadhatbbnak
tn list nv. Az egyes sorok trsakor megjelen kis villanyg arra figyelmeztet, hogy
el ne felejtsk a signature-kn is vltoztatni, amit egybirnt a Qt Creator az Alt+Enter
billentykombinci lenyomsakor fel is ajnl. Ha mindent trtunk, amit kellett, vltsunk t a
fileoperator.cpp szerkesztsre (F4, emlksznk mg)?
Itt is t kell rnunk a thingsToDo-kat list-ekre a deklarciknl persze a tpust
is megfelelen mdostanunk kell. Tekintve az imnt bemutatott Alt+Enter-nyomkods
eredmnyt, a save(), a saveAs() s az open() tagfggvnyen ms vltoztatnival nincs is.
Hja, krem, a gondos tervezs!...
A performSaveOperation() tagfggvny esetben a fentieket mr nem mernnk
kijelenteni eltekintve a tervezs minsgt firtat megllaptstl, termszetesen. Szerencsre
a cserbe nem fogunk belerokkanni, amennyiben mindssze az a feladat vr rnk, hogy az out
<< list.at(i) << endl; sor helyett az

38
6. A feladatokhoz kategrikat rendelnk Qt strandknyv

out << list.at(i)>job() << "|" << list.at(i)>category() << endl;

sort helyezzk el. Figyeljk meg, hogy a fjlunkban egy sor tovbbra is egy feladatot trol,
de annak kt rszt a teendt s a kategrit egy cs (|, magyar billentyzetkiosztson
AltGr+W) karakter vlasztja el egymstl. Azrt pont ez a karakter, mert a htkznapi letben
elg ritkn hasznljuk mondanivalnk megfogalmazsra.
A performLoadOperation() tagfggvnybe ismt bele kell trni pr sornyit. A while-
ciklus magjt kell kiss megvltoztatnunk, st, felhizlalnunk:
QStringList sl = in.readLine().split("|");
QSharedPointer<Task> t(new Task(sl.at(0), sl.at(1)));
tasks.append(t);

Az els utasts a beolvasott sort a cs karakternl kettszedi, az eredmnyt egy karakterlnc-


listba tltve. A msodik sor ebbl a karakterlnc-listbl ltrehozza a Task osztly
objektumot, s annak mutatjt, nagyon hasonlan ahhoz, amikor a szerkesztsorbl s a
default category helyrzbl alaktottuk ki. A harmadik sor pedig, ahogy mr rmutattunk
pr bekezdssel korbban: biggyeszt.
Mr csak az j listt kezd slot maradt htra. A vltoztatsa teljes egy sor lesz, lvn annyi
ugye a vltozs, hogy az adatszerkezetet mr nem a listWidget trolja, gy a lista rtsn tl
a tasks objektum listjt is ki kell pucolnunk. me:
tasks.clear();

Egyszer menne a billentyzettakarts ilyen gyorsan!


Az a nagy helyzet, hogy ksz vagyunk. Futtassuk a programunkat s rvendezznk.
A klcsn nem sokat javult ha nagyon ragaszkodunk a tnyekhez, visszafejldsrl
kell beszlnnk, hiszen a tbbszrs kijells s trls eltnt. Akinek nagyon hinyzik, a
QListWidget::selectedItems() tagfggvny hasznlata mellett lehetsge nylik az jbli
megvalstsra. Szval, a klcsn nem javult, na de a belbecs! A programunk immron kszen ll
a kategrik bevezetsre.

6.4. Kategrik kltznek a ToDoList-be


A kategrikrl eddig mg keveset rtekeztnk. A Task osztly megvalstsbl taln mr
kiderl, hogy egy teend legfeljebb egy kategrival brhat. A kategria megadsa megint csak
QLineEdit osztly szerkesztsorban fog trtnni, azaz a teend megadsra szolgl sor
lineEdit elnevezse zavarv vlik. Jobb lenne, ha ez az edit job lenne. Teht jobEdit.
Az tnevezst kt lpsben ejtjk meg. Az elsben a grafikus felhasznlifellet-szerkesztn
kijelljk az elemet, s akr a jobb egrgomb lenyomsra megjelen helyi men megfelel
pontjt vlasztva, akr a Property Editor-ban megadjuk az j nevet. A msodik lps pedig az,
hogy a kdszerkesztben a kurzort a lineEdit valamely elfordulshoz helyezzk, majd a
menbl a Tools - C++ - Rename Symbol Under Cursor (a kurzor alatti szimblum tnevezse)
lehetsget vlasztjuk. Ekkor a Qt Creator megmondja, hogy hol s mennyi elfordulst
fedezte fel a lineEdit-nek, mi meg megadjuk az j nevet (jobEdit), majd a Replace (csere)
gombbal vgre is hajtatjuk a mveletet. Futtassuk a programunkat a sikeres fordts s nmi
tesztels bizonyoss teszi, hogy minden szndkainknak megfelelen zajlott le.
Folytassuk azzal a munkt, hogy elhelyezzk a kategria szerkesztsort. Viszonylag knnyen

39
Qt strandknyv 6. A feladatokhoz kategrikat rendelnk

a helyre tall az elrendezsek rengetegben, elvileg nem lesz vele gondunk. St, alighanem a tab-
sorrenden sem kell mdostanunk, valsznleg az is azonnal j lesz. A szerkesztsor-objektum
neve legyen categoryEdit. Fl helyezznk el egy cmkt (QLabel osztly objektumot), rajta
a szveg nem meglep mdon kategria lesz, st: &kategria, hogy az Alt+K billentykombinci
mkdhessen. S ha mr itt tartunk, nem felejtkeznk el a kispajts (buddy) megadsrl sem.
Mr csak az fjdtja a szvnket, hogy a kategria megadst kveten hiba nyomunk
Enter-t, az j feladat nem vtetik fel. Hogy is oldottuk meg ezt a problmt a jobEdit
szerkesztsor esetben (br akkor a jobEdit mg lnykori nevn, lineEdit nven szerepelt)?
Egy j kis connect utastssal a fablak konstruktorban, amellyel a szerkesztsor
returnPressed() signal-jt az addButton objektum clicked() signal-jhoz kapcsoltuk.
Akkor ezek szerint hasonl rmnyt kell elkvetnnk a categoryEdit esetben is? gy van.
Ne feledjk az on_addButton_clicked() slot-ot gy trni, hogy a kategria szerkesztsora
is trldjn.
Akkor ksz is? Dehogyis, hiszen tovbbra is minden kategria default category lesz:
nem vesszk figyelembe, hogy drga egyetlen felhasznlnk (aki egybirnt mi magunk
vagyunk) mit rt a kategriaszerkesztbe. Mondjuk nem ettl fognak elkopni a kzzleteink:
annyi mindssze a dolgunk, hogy az imnt emlegetett on_addButton_clicked() slot
QSharedPointer<Task> t(new Task(txt, "default category")); sort a kvetkezre
cserljk:
QSharedPointer<Task> t(new Task(txt, ui>categoryEdit>text().
simplified()));

Persze arrl, hogy a mdostsunknak haszna is van, csak gy tudunk meggyzdni, ha a


mentett fjlba belekukkantunk. Tegyk meg, s rvendezznk!
gy, a nap vgn, megmutatjuk a hrcsgnknek aki pp karalbt majszol a tenyernkben,
s ekkpp nem igazn r r odafigyelni , hogy mekkora j programot rtunk. Azonban menet
kzben rbrednk, hogy a megetetni a papaglyt az mgis a msik j, mert gy nagyon
butn nz ki a sz. s akkor most trlni kell a feladatot s ltrehozni egy jat, mert a meglv
feladatok mdostsra kptelen a program? Nos, ma mg megtesszk, de legkzelebb gy
trjuk a programot, hogy csak na!

40
7. Mdosthat feladatok, ... Qt strandknyv

7. Mdosthat feladatok, felajnlott


kategrik s alaprtelmezetten
betlttt feladatlistafjl

Ha mr a mltkor ilyen csnyn elragadtattuk magunkat a szrnyasunk miatt, fogjunk hozz


a feladatok mdosthatsgnak megvalstshoz. Minekutna ezzel megvagyunk, akkor olyat
varzsolunk, hogy a kategriaszerkeszt a mr meglv kategrikat felajnlja a kategria
gpelse kzben. Amikor mr ez is megvan, akkor az maradt, hogy troljuk az utoljra mentett
fjl nevt, s a fjlt automatikusan betltetjk indulskor.

7.1. A mr megadott feladatok mdostsa


Valahogy gy kpzeljk a dolgot, hogy ha rkattintunk egy feladatra, akkor annak a teendje
s a kategrija megjelenik a megfelel szerkesztsorban. Mi ri kedvnknek megfelelen trjuk
akr az egyiket, akr a msikat, majd szerzi mkdsnk vgeztvel a ktsoros remekmvet akr
j feladatknt, akr az elz helyre mentve, annak mdostsaknt visszahelyezzk a listba.
Termszetesen a signals and slots mechanizmusra szeretnnk tmaszkodni, gy els
kzeltsben a QListWidget osztly dokumentcijt bngsszk t hasznlhat signal
utn kutatva. Kis gondolkods utn vagy pp anlkl, a szabadon marad erforrsok
fggvnyben gy dntnk, hogy a currentRowChanged(int currentRow) signal-t
fogjuk hasznlni. Jobb egrgombot kattintunk a Qt Creator felhasznlifellet-tervezjben
a listWidget-en, s a Go to slot... lehetsget vlasztjuk, a felbukkan listbl pedig
megkeressk a kinzett signal-t.
Az a vgyunk, hogy a kijellt feladat teendje kerljn a jobEdit, a kategrija pedig
a categoryEdit szerkesztbe. A ltrejtt slot fggvny trzst a kdszerkesztben a
kvetkezkpp adjuk meg:
ui>jobEdit>setText(tasks.at(currentRow)>job());
ui->categoryEdit->setText(tasks.at(currentRow)->category());

Szpen kihasznljuk a signal-tl kapott currentRow paramtert. Tesztelskor jnak is


tnik minden, egszen addig, amg az utols feladatot is ki nem trljk, ugyanis ekkor a
programunk elszll, kezdeti jkedvnkkel egytt.
Na, akkor mg a vgn knytelenek lesznk gondolkodni, pedig valaki mr emltette, hogy
ilyen grafikus fejlesztkkel azok rnak programot, akiknek nem a gondolkods az erssgk.
Remlhetleg nagyjbl a kvetkez gondolatmenetet jrjuk be:
valjban
zz a signal nem akkor jn, amikor kijellnk egy sort, hanem amikor az aktulis sor
megvltozik ez a kt dolog nem pontosan ugyanaz
amit
zz kitrlnk az nem lehet aktulis
trlskor
zz az aktulis rtk vltozik
amikor
zz az utols elemet trljk, nem lehet valdi az aktulis rtk (ha qDebug() -gal
kiratjuk, ltjuk is, hogy a -1. elem lesz az aktulis)
a
zz mutatlista -1. elemnek tagfggvnyt hvni az lmosknyv szerint csupa rosszat jelent

41
Qt strandknyv 7. Mdosthat feladatok, ...

Akkor most mi lesz? Az egyik lehetsg az, hogy egy felttelvizsglattal vesszk krbe a
fenti kt utastst: csak akkor trtnjenek meg, ha a currentRow rtke legalbb 0. gy j is a
programunk, de a tovbbi tesztelgets sorn felfedeznk mg egy zavar mellkhatst. Ugye
mi az aktulis elem megvltozshoz ktttk a kt szerkesztsor kitltst. Mrpedig trlst
kveten is vltozik az aktulis elem, ami azrt baj, mert ilyenkor olyan elemek adatai is a
szerkesztsorokba kerlnek, amelyekkel pp nem hajtottunk foglalkozni.
gyhogy vissza a dokumentcihoz, ahol ezttal arra az itemClicked(QListWidgetItem
*item) signal-ra fanyalodunk r, amelyrl az imnt pp azrt siklott tovbb tekintetnk, mert
nem sort ad vissza, hanem elemet. A sor meg mg ki kell nyernnk.
Nos, ha mr gy jrtunk, megint llttassuk el a megfelel slot-ot (a rgi trlsekor a
fejlcfjlban lv deklarcirl se felejtkezznk el), s alaktsuk ilyenn:
void MainWindow::on_listWidget_itemClicked(QListWidgetItem *item)
{
int row = ui>listWidget>row(item);
ui>jobEdit>setText(tasks.at(row)>job());
ui>categoryEdit>setText(tasks.at(row)>category());
}

No, ezzel mr nem lesz baj, fleg amennyiben a mainwindow.h llomnyban megadjuk a
<QListWidgetItem> fejlcet is. Ha a szveget a szerkesztsorok valamelyikben mdostvn
gy dntnk, hogy j feladatknt kvnjuk rgzteni vltoztatsaink eredmnyt, akkor semmi
tovbbi programoznivalnk nincs. Amennyiben a a jelenlegi feladatot mdostannk, akkor
viszont mg akad egy s ms.
Helyezznk el egy mdostgombot modifyButton() nven. lltsunk be neki
gyorsbillentyt, s lltsuk el a slot fggvnyt.
Most, a sokadik slot elllttatsakor taln elfilzgatunk rajta, hogy amikor sajt signal-t s
slot-ot rtunk, ssze kellett ket kapcsolni egy connect utastssal. Ilyenkor erre mirt nincs
szksg? Nos, azrt nincs, mert a slot ellltsakor olyan fggvny jn ltre, amelynek neve
bizonyos szablyokat kvet, nevezetesen:
on-nal
zz kezddik, amit alvons (_) kvet
ezt
zz kveti a signal-t emittl objektum neve, majd ismt alvons
vgl
zz a signal neve s a paramterlista kvetkezik
Termszetesen kzzel is rhatunk ilyen fggvnyt. Ha gy jrunk el, fordtskor
automatikusan kialakul a kapcsolat, tovbbi teendnk nincs.
Akkor vissza a slot-hoz.
Kt feladatunk van: a vltoztatsokat bevezetni a tasks listba a megfelel listaelem
jellemzinek hasznlatval , illetleg a teend kirsa a listWidget-be. Mindezt azonban
csak akkor rdemes elvgeznnk, ha a teend nem lett res ha igen, akkor szljunk a
felhasznlnak, hogy hasznlja a trls gombot, ha mr egyszer megrtuk neki. A slot trzse az
albbi:

42
7. Mdosthat feladatok, ... Qt strandknyv

QString txt = ui>jobEdit>text().simplified();


if(!txt.isEmpty()){
int row = ui>listWidget>currentRow();
tasks[row]>setJob(txt);
tasks[row]>setCategory(ui>categoryEdit>text());
delete ui>listWidget>takeItem(row);
ui>listWidget>insertItem(row, txt);
}else{
QMessageBox mb;
mb.setIcon(QMessageBox::Information);
mb.setText("Feladatot nem mdostok resre.");
mb.setInformativeText("Ha tnyleg ezt akarod, akkor trld.");
mb.exec();
}

Minthogy a teend szerkesztsornak tartalmt tbb helyen hasznljuk, vltozban troljuk


az rtkt. A listWidget sorait, elemeit nem tudjuk mdostani. gy aztn a felhasznl ell
elrejtve, sutyiban kivesszk s trljk a kijellt elemet az eljrs mr ismers a trls gomb
slot-jbl. Annak a sornak a szmt, ahonnan az elemet kivettk, a row vltozban tesszk
el. Az egyetlen j elem az insertItem() tagfggvny, amellyel az elzleg trlt elem rgi
helyre beszrjuk az j feladatot.
Hurr, tudtunk feladatot mdostani! gy mr sokkal knnyebben lesz a papaglybl papagj,
igaz?

7.2. Kategrik felajnlsa a QCompleter osztly hasznlatval.


Els pillants a modellekre
Azt szeretnnk, ha a felhasznl nem adna meg flslegesen sok j kategrit, mert ugye
ha csak egy adott kategriba tartoz feladatokat hajtunk megjelenteni (igen, ilyesmit
is akarunk), akkor ugye nem j, ha a hrcsgnkkel kapcsolatos feladataink egyszer hri,
msszor hrcsg, megint msszor aranyhrcsg kategriba kerlnek. A sok kategria
hasznlatt megtiltani persze nincs rtelme, de ha okosan felajnljuk a hasonl kategrikat,
akkor a felhasznl hajlamosabb lesz egy mr ltez kategrit kiszemelni, mint jat szlni. Ez
pedig vgs soron a program hasznlhatsgt nveli, igaz?
Els feladatunk egy mutatt deklarlni a QCompleter osztly dokumentumunknak.
Agyunk hosszas erltetsvel a *completer nevet sikerl kitlennk. Megadjuk a
<QCompleter> fejlcet is. A fablak konstruktorban pldnyosthatjuk is. Ha megnzzk
a QCompleter osztly dokumentcijt, lthat, hogy halmozottan htrnyos, szakszval
tbbszrsen tlterhelt fggvnyrl van sz. Mi ezttal azt a verzit hasznljuk, amely
QStringList osztly objektumbl ajnlja fel a vlasztsi lehetsgeket, s egyelre
statikus listt adunk meg. Az objektum pldnyostst kveten azonnal be is lltjuk a
categoryEdit objektumot gy, hogy vegye hasznlatba legjabb jtkszernket:

completer = new QCompleter((QStringList() << "hrcsg" <<


"papagj" << "szmtgp"), this);
ui->categoryEdit->setCompleter(completer);

Ht, ez remekl megy, igaz? Mr csak azt kell megoldanunk, hogy a lista a valdi
kategrikat ismerje. A feladat kt ponton knl kihvsokat.

43
Qt strandknyv 7. Mdosthat feladatok, ...

Az egyik az, hogy mikor is frisstjk a lehetsges kategrikat? A knlkoz md az lenne,


hogy akkor, amikor ltrehozunk egy feladatot. Ha a feladat kategrija mg nem szerepel az
eddigi kategrik kztt, akkor rjuk hozz a listhoz. Ht igen, s ha mdostunk egy feladatot,
akkor lehet, hogy a kategrit is trjuk. Ha meg trlnk egyet, akkor meg kell nzni, hogy
ez volt-e a kategria utols hasznlata, s ha igen, akkor pusztuljon a listbl is. Feladatlista
betltsekor meg kategrialistt is kell generlnunk. Ez mr azrt tbb kettnl, igaz?
gyhogy nem gy jrunk el. Kicsit tn erforrs-ignyesebb lesz a mi vltozatunk, de taln
egyrtelmbb is. Meg ht az a helyzet, hogy lassan a ToDoList is attl szenved, amitl a tanul-
programok ltalban: sok mindent kiprblnak rajtuk, s ettl nha kicsit flre ll a fejk.
gy fogjuk megoldani a feladatot, hogy figyeljk, mikor kap fkuszt a categoryEdit
objektum. Tbb mdszer is szolgl e clra, mi most csak az egyiket nzzk meg. Amikor
az objektumunk megkapja a fkuszt, gyorsan frisstjk a kategrik listjt. A QLineEdit
osztly dokumentcijban hiba keresnk a fkusz megkapsakor emittlt signal-t,
nincsen ilyen. De pr lpcsvel magasabban, a QApplication osztlynl megtalljuk a
focusChanged() signal-t. Van neknk ilyen osztly objektumunk? Esetleg eddig nem
nagyon tudatosult bennnk, de a main.cpp fjlban ott bjik egy, amely a hangzatos s
fantziads "a" nevet viseli. Volt mr rla sz, mg a rgi szp idkben, a knyv elejn, amikor
a grafikus programok fciklusrl beszltnk.
A MainWindow-on bell a main.cpp llomny egyb objektumaira nem hivatkozhatunk, gy
a kapcsolatot kipt connect utastst knytelenek lesznk magban a main.cpp-ben kiadni.
Elbb azonban rjuk meg a slot deklarcijt s trzsnek kezdemnyt a MainWindow-ban.
A focusChanged() signal-tl kt, QWidget osztly objektumra mutat mutatt kapunk: az
els arra az objektumra mutat, amelyik volt a fkusz, a msodik arra, amelyik lett. Ennek
fnyben a slot piszkozata a kvetkez:
void MainWindow::appFocusChanged(QWidget *old, QWidget *now)
{
qDebug() << "focus changed";
}

A main.cpp fjlban kell kiadnunk a connect utastst. A szokott formhoz kpest kt


ponton trnk el. Az egyik, hogy megadjuk az osztlya nevt is (statikus tagfggvnyrl
van sz). A msik pedig az, hogy a kt objektum mutatjra van szksgnk, ezeket el kell
lltanunk. Az utasts gy nz ki:
QObject::connect(&a, SIGNAL(focusChanged(QWidget*, QWidget*)), &w,
SLOT(appFocusChanged(QWidget*, QWidget*)));

Helyezzk a return a.exec(); sor el. Futtassuk s prbljuk ki, megy-e minden rendben.
Szval szre fogjuk venni, ha belp a felhasznl a categoryEdit szerkesztbe. Ekkor majd
el kell lltanunk a lehetsges objektumok listjt, s bepakolni az egsz hbelevancot egy
karakterlnc-listba. A kvetkez tagfggvny pont ezt a feladatot ltja el:

44
7. Mdosthat feladatok, ... Qt strandknyv

QStringList MainWindow::categories()
{
QStringList list;
for(int i = 0; i < tasks.size(); i++)
{
QString category = tasks.at(i)>category();
if(!list.contains(category))
list << category;
}
return list;
}

Mr csak az a krds, hogy miknt vegyk r a completer objektumot az esetleg


megvltozott lista hasznlatra? Hiszen a mltkor egy ksz listval pldnyostottuk, s
a pldnyosts utn meg hiba vltoztatjuk meg a kiindul listt. Az egyik megolds az
lenne, hogy az appFocusChanged() slot-ban mindig j QCompleter-t pldnyostunk a
categories() tagfggvny kimenetbl, s a categoryEdit objektumnl megadnnk az j
pldnyt completerknt. A msik megolds hasznlathoz jat kell tanulnunk. s ugyan mi
egybrt olvas az ember strandknyvet, mint hogy sok jat tanulhasson?
Aki mr olvasott programtervezsi mintkrl (design patterns), aligha kerlte el az MVC (Mo-
del-View-Controller, modell-nzet-vezrl) mintt, aki meg nem olvasott, az most kap egy egyet-
len mondatos sszefoglalt. A tapasztalat szerint szp s okos dolog sztvlasztani a programnak
azt a rszt, amely az adatszerkezet vltozsaival foglalkozik (a modell) attl, amely megjelenti
az adatot (a nzet) s attl, amelyen keresztl a felhasznl vltoztatgat az adaton (a vezrl).
A Qt a fenti koncepci View s Controller rszeit View nven egyestve13 a Modell-View
mintt preferlja, azaz van egy adatmodellnk, meg egy valamink, ami azt megjelenti s
amin keresztl mdostani tudjuk. s most jn a lnyeg: a Qt-ban a modellt hasznl osztlyok
nmkden szreveszik s feldolgozzk a modell vltozsait. Egyelre ennyit az MV mintrl,
egy-kt fejezet, s kapunk belle csstl.
A QCompleter osztly objektum persze nem nzet a sz alaprtelmben, de abban az
rtelemben igen, hogy a pldnyostst kveten is szreveszi, ha a modellje vltozik.
Azaz minekutna a mainwindow.cpp llomnyban elhelyeztk els modellnk, a
QStringListModell hasznlatt lehetv tev fejlcet s a

QStringListModel *completerModel;

sorral deklarltunk egy j kis mutatt, a MainWindow konstruktorban a completer


objektumot pldnyost sort vltsuk fel az albbi kettvel:
completerModel = new QStringListModel(this);
completer = new QCompleter(completerModel, this);

Az els sor ellltja azt a modellt, ahova friss, ropogs mutatnk mutat, a msodik pedig
hasznlatba is veszi. Mr csak az a baj, hogy a modell mg res. Mikor is kell neknk elszr
a completer? Akkor, amikor elszr gyalogolunk bele a categoryEdit objektumba, a
kategriaszerkeszt-sorba. Azt ugye meg mr szre is vesszk egy appFocusChanged() slot-
tal. Ja, nem. Azt vesszk szre, hogy mshova kerlt a fkusz, de ugye mi nem akarunk minden
fkuszvltskor kategrialistt kpezni, s azt modellbe tlteni, igaz?
13 Vannak vitk arrl, hogy a Qt terminolgija helyes-e: http://stackoverflow.com/questions/
5543198/why-qt-is-misusing-model-view-terminology

45
Qt strandknyv 7. Mdosthat feladatok, ...

A slot kt paramtert kap, az egyik a fkuszvlts eltt fkuszban volt elem, a msik a
fkuszvltst kveten fkuszba kerlt elem mutatja. (Ez utbbinak now a neve.) Miknt
tudjuk meg, hogy melyik elem kerlt fkuszba? Az elem osztlyt pp ki tudjuk halszni a
now>metaObject()>className();

utastssal, de ez neknk deskevs: mg ebben a kis programban is kt azonos osztly elem


van a felhasznli felleten. A fkuszba objektum neve kellene, az meg nem trivilis.
Elindulunk megtancskozni a dolgot a hrcsggel, de flton rjvnk... hogy mutatt
kaptunk, amin remekl lehet sszehasonlt mveletet vgezni!
Szval a slot trzse ez lesz:
Q_UNUSED(old);
if(now == ui>categoryEdit){
completerModel>setStringList(categories());
}

Az els sorban lv makr arra j, hogy a fordt ne figyelmeztetgessen bennnket arrl,


hogy nem hasznljuk az old vltozt. A msodik sor megvizsglja, hogy a minket rdekl
objektum kapott-e fkuszt, s ha igen, akkor a harmadik sorban a categories() tagfggvny
QStringList osztly kimenett betltjk a QStringListModel osztly completerModel-
be. A modell vltozst a megfelelen pldnyostott completer objektumunk automatikusan
szreveszi, s mindig a legfrissebb listt tolja elnk.
Kiprblhatjuk a programunkat, s ha megy, akkor az imnt a kzeledtnkre elbj
hrcsgnek vihetnk nmi nasit szegny gy hoppon maradt az elbb, hogy nem nyilvnval
az llatpszicholgus mellzhetsge.
Ha a hrcsg megvigasztaldott, akkor a fablak konstruktorban helyezznk el mg kt
remek sort, a completer objektum pldnyostst kveten:
completer>setCaseSensitivity(Qt::CaseInsensitive);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);

Az els megoldja, hogy a kis p begpelskor a Pali s a papagj is megjelenjen, a msik


pedig arrl tesz, hogy a modell tartalmt rendezzk megjelents eltt, mghozz nem figyelve
a kis- s a nagybetkre.

7.3. Automatikus fjlbetlts a program indulsakor a QSettings


osztly
A QSettings osztly objektumok feladata a program belltsainak mentse, mgpedig
gy, hogy kihasznljuk az opercis rendszerben megszokott mdszereket. Ez a Unix-szer
rendszereken a legegyszerbb, itt egyszer INI-fjlokrl van sz. OS X-en XML belltsfjlokat
hasznl, Windows-on pedig a rendszerler adatbzisba (registry) menti a belltsokat. A
programoznak pusztn annyi a dolga, hogy megadja, milyen bellts milyen rtket kapjon
a belltsok, belltsfjlok elhelyezse mr nem az dolga.
Az objektum pldnyostsa sorn meg kell adnunk a cgnk s a termknk nevt a
belltsok helyt ez alapjn lmodja meg a QSettings. Ha a programbl tbb helyen, tbb
objektumban is babrlunk a belltsokkal, akkor rdemes a program indulsakor megadnunk

46
7. Mdosthat feladatok, ... Qt strandknyv

az alkalmazs nevt, az internet-tartomnyt14 s a cg nevt a QCoreApplication osztly


statikus tagfggvnyeinek hasznlatval. Ha gy jrunk el, a QSettings osztly objektum
pldnyostsakor nem kell jra meg jra megadni az adatokat: kiolvassa magnak a trolt
rtkeket.
Helyezzk el pldul a programunk mainwindow.h llomnynak betltend fejlcei kztt a
<QCoreApplication> fejlcet, majd a fablak konstruktornak elejn helyezzk el a:

QCoreApplication::setOrganizationName("Sufni & Co.");


QCoreApplication::setOrganizationDomain("example.com");
QCoreApplication::setApplicationName("ToDoList");

sorokat. A FileOperator osztly privt tagjai kztt pldnyostsunk magunknak


objektumot (szksg lesz a <QSettings> fejlcre):
QSettings settings;

A performSaveOperation() tagfggvny sikeres mentst kveten a fileName vltoz


rtkl megadta az fn helyi vltozban trolt fjlnevet. Ezzel az utastssal mg el is menti ezt
a belltst:
settings.setValue("GeneralSettings/ListFileName", fn);

Futtassuk a programunkat s mentsk az adatainkat.


A ments utn elvileg azonnal megvannak az adataink, de lehet, hogy kicsit ksnek. Ha ez
zavar, nzzk meg az osztly dokumentcijban a sync() s a status() tagfggvnyt.
Ubuntu Linuxon ellenrizve:
raerek@cucc:~$ cat .config/Sufni\ \&\ Co./ToDoList.conf
[GeneralSettings]
ListFileName=/home/raerek/eztkelltennem.tdolst
A Windows rendszerler adatbzisban pedig ez ltszik:
Az, hogy az egyes platformokon hol van a belltsok pontos helye, az osztly
dokumentcijbl kiderl.

10. bra: Belltsaink a Windows registry-ben


A bellts visszatltse sem szmt igazn bonyolult mveletnek. Azt rdemes rla
megjegyeznnk, hogy lvn egy szveges fjlban trolt rtkrl nehz els blikkre
megmondani, hogy milyen tpus a QSettings::value() tagfggvny visszatrsi tpusa
QVariant. Hogy az meg mi a csuda? Ht, szabad fordtsban magyarul nagyjbl gy hangzik:

14 OS X-en van jelentsge ott van szoksban ennek az adatnak a hasznlata

47
Qt strandknyv 7. Mdosthat feladatok, ...

mittomn. Azaz neknk kell a toInt(), a toString(), a toMiegyms() tagfggvnnyel a


kvnt tpusra alaktanunk.
Mr csak az a krds, hogy miknt valstsuk meg a teendk automatikus betltst. Betltjk
a belltsokat pontosabban azt az egyet, ami eddig megvan, a listafjl nevt , eddig tiszta. s
aztn?
Gondoljuk t, miknt trtnik jelenleg egy fjlmegnyits:
1. a felhasznl a megnyits QAction osztly objektumn kivltja a triggered() signal
emittlshoz vezet esemnyt kattint, vagy Ctrl+O billentykombincit nyom
2. a fablak slot-ja futni kezd, s
3. hvja a fileOperator.open() tagfggvnyt, amely
I. fjlnevet szerez s ezt tadva hvja a fileOperator.
performLoadOperation(QString fn) tagfggvnyt, amely
II. betlti a fjlt, ellltja a QList objektumot s tadja
III. a fileOperator.open() tagfggvnynek, ami tadja
4. a slot-nak, ami folytatva a munkt, megjelenti az eredmnyt a listWidget-ben
Arab szmokkal a MainWindow-on belli mveletek, rmai szmokkal a fileOperator
objektum munkja.
Ez azonban arra az esetre kszlt, amikor nincs fjlnevnk. Neknk meg most van, a
settings objektum rulja el. gy persze hvhatnnk kzvetlenl a performLoadOperaton()
tagfggvnyt. Gyorsan alaktsuk is publikuss.
A kvetkez dolgunk az, hogy a slot-bl (becsletes nevn: on_actionMegnyitas_
triggered()) kln tagfggvnybe tesszk t azt a rszt, ami a kapott QList osztly
objektumbl kipakolssza a teendket a listWidget-be. Ezzel a slot trzsbl az els sor
kivtelvel mindent elvesznk. Az j tagfggvny neve legyen showJobs(). A slot-bl persze
hvnunk kell, gy a slot trzse vgl mgiscsak kt soros lesz.
Utolsknt mg az a feladatunk van, hogy runk egy tagfggvnyt, amely kiolvassa a
megfelel belltst. Ha van ilyen, akkor a tallt fjlnv hasznlatval megprblja betlteni a
feladatokat, majd a szp, j, ropogs showJobs() fggvnyt hasznlva meg is jelenti ket. me:
void MainWindow::openLastSavedTdoListFile()
{
QSettings settings;
if(settings.contains("GeneralSettings/ListFileName")){
tasks = fileOperator>performLoadOperation(settings.
value("GeneralSettings/ListFileName").toString());
showJobs();
}
}
Ne feledjk a MainWindow konstruktorbl hvni ezt a remek kis tagfggvnyt.
Ksz vagyunk. Odaadhatjuk kicsit a mkuskereket a hrcsgnek, teperjen most mr is.

48
8. Keress, grafikon s tbbnyelvsg Qt strandknyv

8. Keress, grafikon s tbbnyelvsg

Ebben a fejezetben az els dolgunk az lesz, hogy vgre hasznljuk is a megadott kategrikat:
megvalstjuk az egy adott kategrihoz tartoz feladatok megjelentst. Ha ezzel elkszltnk,
megrajzoljuk a leggyakrabban hasznlt kategrik grafikonjt. A grafikonrajzols utn pedig
megismerkednk a tr fggvnnyel s azzal, hogy miknt tehetjk programunkat tbbnyelvv.

8.1. A keresfl
Ha ilyesmit szeretnnk kialaktani, a grafikus felhasznlifellet-tervezben lv elemek
kzl essen vlasztsunk a Tab Widget-re. Hzzuk ki a fablakba, s kis gyeskedssel pakoljuk
bele egsz eddigi mvnket. Ha sikerlt, kattintsunk valahova az els fl terletre, de az
elemeink mell, azaz sehova hasonlan ahhoz, amikor az els elrendezst alaktottuk ki.
Ekkor jtt a dnt lps: itt kellett akr vzszintes, akr fggleges elrendezst alkalmazni
a lnyeg az volt, hogy valamilyen elrendezs legyen. Nos, legyen most is valamilyen
elrendezsnk, s szpen tmretezhet lesz a fablak, benne az sszes elemmel.
Adjunk nevet a kt flnek. Az els annyira nem is fontos, a msodik, csak azrt, hogy a
knyvvel szinkronban legynk, legyen keress - igen, az alhzs a gyorsbillentyt jelli.
A keress fln helyezznk el egy jabb QLineEdit osztly objektumot, a neve legyen
searchEdit. Ide fogjuk berni, hogy melyik kategria rdekel bennnket, s ekkppen volna
rtelme ezt a szerkesztt is rvenni, hogy hasznlja a j kis automatikus felajnlsokat.
gyhogy helyezzk el a fablak konstruktorban az albbi sort:
ui>searchEdit>setCompleter(completer);

Ha j tletnek tartjuk, hogy a lehetsges kategrikat tartalmaz completerModel akkor


is frissljn, mikor ez a szerkesztsor kap fkuszt, akkor rjuk t az appFocusChanged()
tagfggvnyben megfogalmazott felttelt ilyenre:
if((now == ui>categoryEdit)||(now == ui>searchEdit))

Eddig minden szp s j, taln azt az egyet leszmtva, hogy a tallatok nem jelennek meg.
Helyezznk el szmukra egy msik QListWidget osztly objektumot, a neve maradhat
listWidget_2.
A showJobs() tagfggvnyrl ersen puskzva alaktsuk ki azt a tagfggvnyt, ami majd a
searchEdit tartalmnak felhasznlsval kirja a tallatokat:

void MainWindow::showJobsHavingCategory()
{
ui>listWidget_2>clear();
QListIterator<QSharedPointer<Task> > ji(tasks);
while(ji.hasNext())
if(ji.next()>category() == ui>searchEdit>text())
ui>listWidget_2>addItem(ji.next()>job());
}

Eltntettk a tasks objektum ressgt vizsgl sort, s rtunk egy jat is, amelyik a
while-ciklus kzepben dnt arrl, hogy az adott elemet kirjuk-e, avagy sem. A magunk
blcsessgben gy dntnk, hogy nem helyeznk el a keress megindtsra szolgl

49
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg

kln nyomgombot, hanem a searchEdit egyik signal-jt, az editingFinished() nevt


hasznljuk. Az imnt megrt j kis fggvnynket minstsk t privt slot-t, a fablak
konstruktorba pedig rjuk mg be a
connect(ui>searchEdit, SIGNAL(editingFinished()),
this, SLOT(showJobsHavingCategory()));

utastst. Elkszltnk a keresfllel.

8.2. Grafikuss vlva grafikont rajzolunk, s a tetejbe mg


iterlgatunk is
Elkpzelhet, hogy amit ebben az alfejezetben fejlesztnk, nem teljesen letszag. Ugyanis
most azzal fogjuk tlteni az idt, hogy a drga kis QTabWidget osztly elemnkn
ltrehozunk egy harmadik flet, amin grafikont ksztnk a leggyakrabban hasznlt
kategrikrl. Ha valahogy nem jelenik meg lelki szemeink eltt az a felhasznl, aki pp azon
filzgat egy ilyen grafikont nzegetve, hogy Nincs elg aranyhrcsg kategrij tennivalm
akkor most nem trdm eleget vele?, akkor nem tuti, hogy bennnk van a hiba.
Ez azonban, mint azt mr szmos alkalommal megllaptottuk, egy strandknyv. Nem a
valsg. s ugyan melyik strandknyvhs15 szalasztana el egy ilyen alkalmat Qt-ismereteinek
gyaraptsra?
Az elz fejezetben mr megemlkeztnk arrl, hogy a Qt elszeretettel hasznlja a Model-
View programozsi mintt, s elmondtuk a dolog lnyegt is. Eszerint van egy modellnk,
amelynek f funkcija az adatok trolsa. Van tovbb egy nzetnk, amely elkri az adatokat
a modelltl, megjelenti ket, s esetleg lehetv teszi a felhasznlnak az adatok vltoztatst.
Ez utbbi esetben a vltozsokat visszarja a modellbe. Mindezt egy nagyon egyszer pldn
mutattuk be: egy QStringList osztly objektumon, amely egy QCompleter osztly
trsnak szolglt modelll.
Ezttal a nzet egy QGraphicsView osztly objektum lesz. A tabWidget-re vegynk fel
egy harmadik flet, majd bal oldalrl, az elemek kzl hzzunk ki r egy nzet-objektumot. Az
automatikus tmretezst biztostand a szoksos mdon mell, de mg a tabWidget kereten
bell kattintva adjunk meg vzszintes vagy fggleges elrendezst. A nzettel mr csak egy
dolgunk lesz: ki kell majd jellni szmra a modellt. Modellnk egyelre tl kevs van, legfbb
ideje ht kialaktani egyet.
Lvn a fablak cpp-fjlja mr gy is nagyon hossz, meg amgy is egy jl krlhatrolhat
feladatkrt ellt tagfggvny-egyttes kialaktsa lesz az alfejezet tovbbi tmja, a modellt
s a segd-tagfggvnyeit j objektumban helyezzk el. A QObject s DiagramCreator
osztly diagramCreator nev objektumrl van sz. Hozzuk ltre az osztlyt s a fejlct
helyezzk el a mainwindow.h llomnyban. Adjuk meg a mutat deklarcijt, majd a fablak
konstruktorban a new utastssal lltsuk el az objektumpldnyt is, nem felejtkezve el a
QObject-leszrmazottak automatikus memriakezelst lehetv tv this s megadsrl:

diagramCreator = new DiagramCreator(this);

Slattyogjunk vissza a diagramcreator.h llomnyba, s publikus mutatknt adjuk meg a


sznpadot, ha tetszik: jtkteret persze csak a hasonnev fejlc hasznlatba vtelt kveten.
15 Persze, hogy ennek a strandknyvnek az aranyhrcsg az els szm hse. De egy msodvonal-
beli hsnek is lehetnek Qt-ambcii.

50
8. Keress, grafikon s tbbnyelvsg Qt strandknyv

QGraphicsScene *scene;

Az osztly konstruktorban ltre is hozhatjuk a megfelel objektumot:


scene = new QGraphicsScene(this);

Ezutn visszasompolygunk a fablak konstruktorba, s az elbb bert sor utn j kertknt


elrendezhetjk a nzet s a modell (ez esetben: szn) egymsra tallst:
ui>graphicsView>setScene(diagramCreator>scene);

Mostantl a sznen lv valamennyi trtns azonnal lthatv vlik a harmadik fln. Mr


csak az a szinte emltsre sem mlt feladatunk van, hogy a sznt benpestsk.
A diagramCreator objektumnak kt feladata lesz. Az els, hogy a tasks objektum
adatainak ismeretben ellltsa azt a hrom soros, kt oszlopos adatszerkezetet, amelyben a
leggyakrabban elfordul hrom kategria neve s szmossga lesz. A msodik, hogy ennek az
adatszerkezetnek a felhasznlsval megrajzolja a grafikont.
A fablakon bell nem szeretnnk sokat bajldni a dologgal, gy aztn az tnik a megfelel
tnak, hogy tadjuk a tasks objektumot a diagramCreator objektum egy tagfggvnynek,
s onnantl a fablakban legalbbis fejleszti szemszgbl elfelejtjk a problmt. Azrt
hangslyoztam a fejleszti szemszget, mert a graphicsView nzet termszetesen reagl majd
a modellje a scene objektum vltozsaira, de ez mr fejleszti beavatkozst nem ignyel.
Hozzuk ht ltre a DiagramCreator osztlyban azt a publikus tagfggvnyt, amelyik majd
elvgzi illetve elvgezteti a fent vzolt kt feladatot. A deklarci a kvetkez alakot lti:
void drawDiagram(QList<QSharedPointer<Task> > tasks);

Kialakthatjuk az egyelre res defincit is, majd, mg mieltt elfelejtkeznnk rla,


kullogjunk vissza a fablakba s ott is a tervezi nzetbe. A tabWidget objektumon kattintsunk
a jobb egrgombbal, s a felbukkan menbl a Go to slot... lehetsget vlasztva kattintsunk a
currentChanged(int index) signal-ra. Az elll slot-ba pedig rjuk meg azt a trzset, amely
a harmadik teht a msodik szm fl aktivldsakor az imnt kialaktott tagfggvnyt hvja:
if(index == 2)
diagramCreator->drawDiagram(tasks);

Ha gy gondoljuk, a drawDiagram tagfggvny trzsben elhelyezett qDebug() << Oh,


yeah! utastssal tesztelhetjk is, hogy eddig minden klappol-e.
Mikor kiprbltuk, kukzhatjuk az utastst, gyis runk helyette sok jt hamarosan. De
elbb beszljk meg, hogy milyen lesz a grafikonunk.
A hrom leggyakoribb kategrit jelentjk meg, mghozz olyan oszlopdiagramknt,
melynek oszlopai valjban vzszintes svok. A leggyakoribb kategria svja legyen mondjuk
200 kppont hossz, a kt kvetkez meg arnyosan rvidebb. A svokat ne egyszer sznnel
tltsk ki, hanem valamilyen szntmenettel, s a knnyebb azonosthatsg vgett a svokra
rrjuk a kategria nevt.
Nos, ha ilyen grafikont szeretnnk, akkor a legels dolgunk a hrom nyertes kategria
megllaptsa, illetve annak feljegyzse, hogy melyik hnyszor fordult el. Mr most
kijelentennk, hogy nem hajtjuk jraimplementlni egyik bevlt grafikonrajzolt sem. Ha
pldul nlunk ngy azonos elforduls-szm kategria van, akkor megjelentjk az elsnek
fellelt hrmat, s ksz.

51
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg

A hrom pontosabban a valahny


legtbbet hasznlt kategria nevnek
s elforduls-szmnak begyjtsre
kln tagfggvnyt runk, amelyet a
drawDiagram() hv majd, egyb bokros
teendi kzepette. A fggvny visszatrsi
rtke QMap<int, QString> lesz. Persze
elbb arra gondoltunk, hogy ez gy logikus,
hogy kategrianv - elfordulsszm
prokat trolunk, de akkor esznkbe
jutott, hogy a QMap osztly objektumok a
bennk troltakat kulcs szerint rendezik,
s neknk ez a rendezs igen jl jn mg 11. bra: Ilyen lesz a grafikonunk
akkor is, ha knytelenek lesznk visszafel
bejrni majd az objektumot, hogy elsnek a legtbbszr elfordul kategrit kapjuk meg.
Szval ezrt a QMap<int, QString> vltozat: ell a hnyszor, utna a mi.
A fggvnynek t fogjuk adni a tasks objektumot (pontosabban annak diagramCreator-
bli msolatt, aminek szintn tasks a neve), s azt a szmot, amelybl megtudja, hogy hny
kategrit kell kirajzolnia. A drawDiagram() tagfggvny majd a kvetkezkpp hvja a
csilivili j tagfggvnyt:
QMap<int, QString> best3Categories = bestCategories(tasks, 3);

Figyeljnk a vltoz s a tagfggvny nevei kztti egyetlen klnbsgre. A 3-as szmra a


tagfggvny belsejben (meg persze mr a deklarcijban is firstX nven utalunk).
Lssuk, mihez kezd a bestCategories() tagfggvny a neki juttatott adatokkal. Elszr
is kialaktunk egy QHash<QString, int> adatszerkezetet, sszeszmolva benne az sszes
kategria sszes elfordulst:
QHash<QString, int> categoriesUsage;
for(int i = 0; i < tasks.size(); i++){
QString cat = tasks[i]>category();
if(categoriesUsage.contains(cat))
categoriesUsage[cat] += 1;
else
categoriesUsage[cat] = 1;
}

Valami ilyesmit kapunk majd:


aranyhrcsg 7
szmtgp 2
bicikli 1
szekrny 2
papagj 4
A QHash<QString, int> alakbl arra kvetkeztetnk, hogy ez olyasmi, mint a QMap. Nem
is tvednk, hiszen els blikkre csak egyetlen, de annl nagyobb klnbsg van a kt osztly
kztt: mg a QMap osztly objektumok bejrskor mindig a kulcsuk szerint vannak rendezve
(s ekkpp a kulcsuk csak olyan dolog lehet, amire rtelmezett a kisebb mint, nagyobb mint

52
8. Keress, grafikon s tbbnyelvsg Qt strandknyv

sszehasonlts), a QHash osztlyak megjsolhatatlan sorrendben bukkannak fel. Azrt hasz-


nltunk QHash osztly adatszerkezetet a QMap osztly helyett, mert ebben a strandknyv-
ben szeretnnk tolsztoji magassgokba emelkedni legalbbis ami a szereplk szmt illeti. A
QHash osztly radsul kicsit gyorsabb is a QMap-nl a legtbb esetben ez nagyjbl hsz,
vagy annl tbb feladat esetben vlik igazz16. De ez aligha jelents szempont a mi esetnkben.
A ksz leltrt ttltjk egy, immron a tagfggvny visszatrsi rtknek megfelel QMap-ba:
QMap<int, QString> categoriesOrdered;
QHash<QString, int>::const_iterator ci = categoriesUsage.
constBegin();
while(ci != categoriesUsage.constEnd()){
categoriesOrdered.insertMulti(ci.value(), ci.key());
++ci;
}

Az ttltshez STL-tpus konstans itertort hasznlunk futlag mr megismerkedtnk


vele rgebben. Az adatok ttltst a QMap osztly categoriesOrdered objektumba nem a
szoksos categoriesOrdered[kulcs] = "rtk" formban vgezzk, mert szmtunk r,
hogy esetleg aranyhrcsg kategrij feladatunk ugyanannyi van, mint csekkbefizets
kategrij. (Br az nem lehet. Mg elkpzelni is rossz... Brr... ) Ilyenkor a msodikknt ttlttt
kategria fellrn az elst. Szerencsre azonban itt a QMap::insertMulti() tagfggvny,
amellyel egy kulcshoz tbb rtket is megadhatunk.
Az ellltott s csurig feltlttt categoriesOrdered objektum tartalma nagyjbl a
kvetkez:
1 bicikli
2 szekrny
2 szmtgp
4 papagj
7 aranyhrcsg
Merthogy a QMap ugyebr kulcs szerint rendezett. Mr csak az vele a baj, hogy ami neknk
kell belle, az az utols hrom sor, radsul a vgrl. Nosza, pusztuljon, amin flsleges! A tr-
ls sorn megint STL-tpus itertort hasznlunk, de nem konstans itertort, mert ugye ha vala-
minek kitrljk egy rszt, akkor az a valami mr nem konstans. A firstX vltoz az, amely-
ben a tagfggvny a hvsakor a szksges, visszaadand sorok szmt kapta paramterl.
QMap<int, QString>::iterator i = categoriesOrdered.begin();
while(categoriesOrdered.size() > firstX){
i = categoriesOrdered.erase(i);
}

Ha figyelmesen bngsszk a fenti ngy sort, feltnhet, hogy a while-cikluson bell sosem
lptetjk az itertort. Vagy mgis? A QMap::erase() tagfggvny visszatrsi rtke az a hely,
ahova az aktulis elem trlse utn az itertor mutat.
Minekutna a categoriesOrdered objektumot immron firstX sorra tprtettk, laza
mozdulattal visszaadjuk a hvnak:

return categoriesOrdered;

16 http://woboq.com/blog/qmap_qhash_benchmark.html

53
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg

Mieltt nekiltnnk, hogy most mr tnyleg megrjuk a drawDiagram() tagfggvny


trzst, megvakargathatjuk a hrcsg picike pocikjt. Ki ne nyomjuk belle az imnt elrgcslt
mogyorkat. Se.
Akkor ht drawDiagram(). Haladjunk sorban. Mg mieltt sort kertennk a
best3categories deklarlsra s feltltsre, elvgznk pr feladatot:
scene>setSceneRect(0,0,200,160);

QFont font("Serif");
font.setWeight(QFont::Bold);

QColor backGroundColor(250, 255, 250);


QBrush backGroundBrush(backGroundColor);
scene->setBackgroundBrush(backGroundBrush);

QColor startColor(145,232,66);
QColor endColor(210,255,82);

QLinearGradient linearGradient(QPointF(0, 0), QPointF(200, 0));


linearGradient.setColorAt(0, startColor);
linearGradient.setColorAt(0.9, endColor);

QColor textColor(109,0,25);

A sort a szn burkolngyszgnek belltsval kezdjk. Ez egy olyan ngyszg, amely


valamennyi, a sznen szerepl objektumot magba foglalja. A szlessge 200 kppont, mert ezt
beszltk meg a grafikon svjainak maximlis hosszul. A magassga meg azrt 160 kppont, mert
a svok 50 kppont szlesek lesznek, ami hrom svnl 150 kppont, de kztk mg ki is hagyunk
5-5 kppontot.
A kvetkez kt sor a hasznland bettpus megadsa. Nem adunk nagyon konkrt
bettpus-nevet, mert nem sok eslynk van elmondani, hogy a nagy multiplatformits
kzepette ugyan milyen bettpusok leledzenek majd a cleszkzn, de annyit azrt
kijelentnk, hogy serif, azaz talpas betket szeretnnk. Az opercis rendszer meg majd
eladja az alaprtelmezett talpas bettpust. Utbb azt is megadjuk, hogy ha mr font, legyen
flkvr.
Az eztn begpelt hrom sorban RGB-kdokkal megadunk egy nagyonhalvnyszrke sznt,
a sznnel pemzlit (j-j: ecsetet) inicializlunk, s ezzel a pamaccsal tsznezzk a sznpadot.
Megadhatnnk egy sorban is:
scene>setBackgroundBrush(QBrush(QColor(250,255,250)));

A startColor s az endColor nev sznt a lineris szntmenet kiindul- illetleg


vgpontjaknt kvnjuk felhasznlni. Szntmenetbl tbbflt is ismer a Qt, mi most
leragadunk a legegyszerbbnl. Meg kell adnunk a kt vgpontot (a QPonitF osztlyban
az F a Floating point-ot jelli), illetve azt, hogy a tvolsg mekkora rsznl kell elrnie a
szntmenetnek a nevezett sznt. Esetnkben a szntmenet a startColor sznnel kezddik, s
mr majdnem a vgre rnk, mire endColor-r kell alakulnia. Egy szntmeneten bell annyi
sznt adhatunk meg a 0-1 tvon bell, amennyit nem szgyellnk.
Vgezetl megadunk egy olyan sznt, amellyel majd szvegeinket kvnjuk kiemelni az
alaprtelmezett feketesgbl.

54
8. Keress, grafikon s tbbnyelvsg Qt strandknyv

Most jtt el az ideje annak, hogy tsomfordljunk a diagramcreator.h llomnyba s


megadjunk nhny privt mutatt:
QGraphicsRectItem *rect1;
QGraphicsRectItem *rect2;
QGraphicsRectItem *rect3;
QGraphicsTextItem *category1;
QGraphicsTextItem *category2;
QGraphicsTextItem *category3;

Az els hrom a svokra mutat majd, a msodik hrom a kategrik neveinek kirsra.
Visszasunnyoghatunk a tagfggvnynkbe.
Visszarve a mr ismertetett mdon ellltjuk a legjobb hrom kategrit bjtat, QMap
osztly adatszerkezetet. De nem elgsznk meg ezzel, hanem rgtn itertort is definilunk
neki, radsul ezttal Java-flt. Ha mg emlksznk, egy jl szitult Java-fle itertor nem az
elemekre, hanem azok kz, el, illetve mg mutat (mondjuk akkor is oda mutat, ha vletlen
nem emlkeznnk). Minthogy neknk elsknt az utols elem kell a best3categories
objektumbl, gyorsan be is lltjuk az itertort a kategrik utnra:
QMap<int, QString> best3Categories = bestCategories(tasks, 3);
QMapIterator<int, QString> ji(best3Categories);
ji.toBack();

A kvetkez sorban meglmodjuk, hogy mennyi legyen a grafikonsvok alapegysge. Azt


mondtuk, hogy amibl a legtbb van, az 200 kppont hossz lesz. Azaz:
float diagramUnit = 200/ji.peekPrevious().key();

Hmm, mi is az a peek? Kukucskls? Na, ezt gondoljuk vgig. Az a helyzet, hogy az


itertorunk az utols elem utn ll. Ha egy ji.previous().key() hvssal krnnk el az
eltte ll, azaz a legutols elem kulcst, akkor az itertor is visszalpne egyet. Mrpedig
neknk pillanatokon bell szksgnk lesz az utols elem rtkre is. Azaz most csak
visszakukucsklunk az elz elemre, lekukucskljuk a kulcst, s majd csak akkor lpnk
visszbb, ha az rtket nzzk le.
A kvetkez ngy sorral kirajzoljuk az els svot, s rrjuk a kategria nevt:
rect1 = scene>addRect(0, 0, diagramUnit*ji.peekPrevious().key(),
50, Qt::NoPen, linearGradient);
category1 = scene>addText(ji.previous().value(), font);
category1>setPos(10,10);
category1->setDefaultTextColor(textColor);

Az addRect() tagfggvnnyel krnk egy tglalapot a sznre. A rect1 mutatban eltesszk


a ltrejtt tglalap memriabli helyt, amire sok szksgnk elvileg nem lesz, de azrt
eltesszk. Az els ngy paramter a grafikonsv tljnak vgeit jelli. A diagramUnit*ji.
peekPrevious().key() szorzatnak a gpi szmbrzols hatrai okozta hibn bell kutya
ktelessge 200-nak lenni, s pusztn a kvetkez grafikonsv megadshoz val hasonlatossg
okn rtuk gy. Az tdik paramter QPen osztly, ez a konstans rtk pp azt jelenti, hogy
a svunk krl ne legyen semmifle vonal. A hatodik paramter QBrush osztly, s a svot
kitlt pemzli sznt adja meg. A szn lehet szntmenet is, esetnkben is ez a helyzet.
A msodik sor a kategrianevet rja ki a szveg mutatjt megint csak eltesszk. Vegyk
szre, hogy a ji.previous().value() tagfggvny-hvs a kategria nevnek visszaadsn

55
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg

tl az itertort is lpteti most mr nem csak kukucsklunk. A szveg bettpusa a korbban


megadott font vltozban belltott rtknek megfelel lesz.
A harmadik sor a szveg burkolngyszgnek bal fels sarkt lltja mshova, a negyedik
meg a pr sorral feljebb belltott sznre sznezi a kategria nevt.
A kvetkez svot kirajzol kdot mg megnzhetjk itt is, a harmadikat meg mr
bizonyosan egyedl is sszehozzuk, s ha mgis msknt trtnne, ht letltjk a forrskdot a
knyv webhelyrl. Kvessk nyomon, ahogy elszr csak kukucsklunk, majd ismt lptetjk
az itertort:
rect2 = scene>addRect(0, 55, diagramUnit*ji.peekPrevious().key(),
50, Qt::NoPen, linearGradient);
category2 = scene>addText(ji.previous().value(), font);
category2>setPos(10,65);
category2->setDefaultTextColor(textColor);

gy trtnt, hogy elkszlt a grafikon.

8.3. Tbbnyelv alkalmazs s a QT Linguist


Az elejn azt grtk, hogy a forrskdban minden angolul lesz, de ezt az gretnket csak
rszben tartottuk be. Eddig.
Mieltt brmit jtannk, gyorsan rjuk t angolra az sszes elfordul karakterlncot
a programban nem gz, ha nem lesz nagyon angolos, csak gy nagyjbl elmenjen. Kt
helyen vannak magyar szvegek: a fileoperator.cpp s a mainwindow.cpp fjlban. A
karakterlncokon kvl t kell rni a felhasznli fellet sszes elemnek feliratt, s a ment is
(rdemes hasznlnunk az Object Inspector ablakt, a menuBar objektumra ugyanis problms
lehet rkattintani). A men meg ugyebr QAction-ket definil. Ilyen kis programnl van vagy
ngy objektumunk ebbl az osztlybl, gy gy dntnk, hogy ezeknek az objektumoknak nem
csak a feliratt, de a nevt is mdostjuk. Ha azonban gy tesznk, akkor knytelenek lesznk
a kapcsold slot-ok nevt is trni. Nosza, tegyk meg ezt is, nem felejtkezve el a fejlcfjlban
lv deklarcikrl sem.
Futtassuk a programunkat, s nzzk meg, hogy nem hagytunk-e ki valamit.
A kvetkez dolgunk, hogy a megjelentend karakterlncokat krbevesszk a tr()
fggvnnyel, aminek a neve egybirnt a translate, magyarul lefordt igbl szrmazik. Azaz,
ami az elz alfejezet vgn mg
mb.setInformativeText(Csinlj valami okosat.");

volt, az mostanra
mb.setInformativeText(tr(Try and do something wise."));

lett.
Nem kell tr() -rel krlvenni a felhasznli fellet elemeinek, illetve a QAction-knek
a nevt. Ha az objektum text tulajdonsgt vizsgljuk a Property Editor-ban, s a text sz
eltti nylra kattintunk, ltszani fog, hogy az objektum alaphelyzetben is translatable, azaz
lefordthat.
Akkor mostanra van egy remek (khmmm) angolsggal beszl alkalmazsunk, ami egy pr
perce mg zes magyar nyelven beszlt, tyuhaj. Most pedig ismt megoldjuk, hogy beszljen

56
8. Keress, grafikon s tbbnyelvsg Qt strandknyv

magyarul, de sokkal bonyolultabban. Igazi kocka-feladat.


Elsknt olyan helyre nylklunk, ahova eddig mg nem: a projektler fjlba, a ToDoList.
pro-ba. Helyezznk el benne egy

TRANSLATIONS = ToDoList_hu.ts

sort. A fjlnv hagyomnyosan, de nem ktelezen az alkalmazs nevbl, alvonsbl s


a nyelv nevnek rvidtsbl ll. A kiterjeszts ts. Ha tbb nyelvi fjlt is ksztnk, akkor
azokat szkzzel elvlasztva helyezhetjk el, vagy ha szpen akarjuk csinlni, akkor a sortrst
visszaperrel jelezzk:
TRANSLATIONS = ToDoList_hu_HU.ts \
ToDoList_de_DE.ts

Ha Qt 4-gyel dolgozunk, s a fordtsfjlok UTF-8 kdolsak, akkor mg a


CODECFORTR = UTF-8

sorra is szksg lesz. Qt 5 esetn nem kell.


Ezt kveten a Qt Creator Tools menjbl vlasszuk az External-Linguist-lupdate
lehetsget. Figyeljnk a kimenetre a lenti ablakban. Fontos, hogy a lupdate, s a prja, az
lrelease futtatst megelzen mentsk a projektfjlt a mentegetsrl mostanra elvileg
csnyn leszoktunk a fordtskor felajnlott automatikus ments miatt. A lupdate egybirnt
nem egyb, mint egy parancssori eszkz, azaz futtathatjuk onnan is, ha pp ri kedvnk gy
diktlja. s hogy mire val? Megnyitja a .pro fjlt, megkeresi, hogy mi lesz a fordtsokat
tartalmaz fjl neve, s ltrehozza a fjlokat. Szp XML-eket kapunk, benne az sszes
lefordtanivalval. Ha a forrson mdostunk, a fordtst megelzen jra futtatnunk kell a
lupdate alkalmazst, amely a vltozsoknak megfelelen frissti a ts-fjlt.
Mg mindig nem kezdnk a fordtshoz, elbb mg pr sorral bvtjk a main.cpp-t. Kell bele
egy olyan, amelyik betlti a <QTranslator> fejlcet, meg mg hrom sor a main fggvny
trzsbe:
QTranslator translator;
translator.load("ToDoList_hu_HU");
a.installTranslator(&translator);

Ha azt szeretnnk, hogy a programunk a meglv fordtsok kzl automatikusan vlassza ki


a felhasznl nyelvi belltsainak megfelelt, akkor a fenti blokk msodik sort cserljk ki az
albbi kettre:
QString locale = QLocale::system().name();
translator.load(QString("ToDoList_") + locale);

Ha pedig azt szeretnnk, hogy ms kezdknyvtr-belltssal azaz akrhonnan indtva


is megtallja a programunk a qm-fjljt, akkor a fenti kt sor kzl a msodikat cserljk erre:
translator.load(QString(QCoreApplication::applicationDirPath() +
"/ToDoList_") + locale);

Persze a <QCoreApplication> fejlc betltsrl gondoskodni illik. Windows-on sem


lesz zr abbl, hogy nem visszapert hasznlunk a nyit idzjel utn, de ha mgis flnk,
akkor hasznljuk a QDir::toNativeSeparators() tagfggvnyt az elre-vissza problma
kezelsre.

57
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg

Fordtsuk le a programunkat. Ne zavarjon bennnket, hogy egyelre sehol nincs a fordts,


a Qt-t sem zavarja. gy ll a dolog, hogy a Qt-alkalmazs futtatskor megnzi, hogy van-e
fordtsfjlja, s ha nincs, ht az sem baj: beszl a forrskdba drtozott karakterlncokkal.
gyhogy futtassuk is a mvnket, s gynyrkdjnk abban, hogy az elz futtats ta semmit
sem vltozott, pedig mennyit melztunk.
Ezek utn nyissuk meg a Qt Linguist alkalmazst, amelyben nem tallunk j menpontot,
hiszen a fordtsfjlt a lupdate programmal kell ltrehozni. Nyissuk meg a ToDoList _
hu _ HU.ts-t, s fordtsuk le, amit gy tartja kedvnk. A karakterlncok fjlonknt vannak
felsorolva, mellettk ott a forrs is, htha segt. Ne felejtkezznk meg a gyorsbillentykrl,
amiket ugyangy jellnk, mint eddig: egy &-jellel a megfelel karakter eltt. Amelyik fordts
ksz s tutira j gy, a mellett a krdjelet lltsuk pipra.
Ha elkszltnk, mentsnk, s a Qt Creator-ban vlasszuk az External-Linguist-lrelease
lehetsget. Tnykedsnk hatsra a ts-fjlokbl elllnak azok az optimalizlt, qm-
kiterjeszts prjaik, amelyeket a program megprbl majd a futsa kezdetn megnyitni ha
erre a main.cpp-ben utastst adtunk.

12. bra: Haladgatunk a fordtssal: pipa jelzi, amivel elkszltnk


Ha most futtatjuk a programunkat alighanem az a knos meglepets vr rnk, hogy mg
mindig angolul beszl, akkor is, ha az opercis rendszer nyelvi belltsa magyar. Nos, ez
azrt van gy, mert a fordtst a Qt Creator alaprtelmezs szerint nem a forrsok knyvtrban
kszti, hanem valahol mshol. Tbbnyire a projekt knyvtrnak szlknyvtrban kell
keresnnk valamilyen szokatlanul hossz, de a projektre utal knyvtrnevet. Nos, futtats
eltt ide kell tmsolnunk a qm-fjlt.
s futtats, s ln bbeli zrzavar. De ht pp ezt akartuk, igaz?
Sikereink rmre a hrcsg kaphat taln egy saltalevelet. J hallgatni, ahogy ropogtatja.

58
9. Ikonok, ablakok, automatikus ments Qt strandknyv

9. Ikonok, ablakok, automatikus ments


A kilencedik fejezet els rszben vgre kiderl, hogy miknt passzol egymshoz a
tennivalk listja s a hrcsg. Miutn a titokrl lehull a lepel, ablakokat nyitogatunk pedig
nem is szeretnnk szellztetni. A megnyitott ablakban pedig belltjuk, hogy hny percenknt
krnk automatikus mentst. s, ha mr belltottuk, meg is valstjuk.

9.1. Ikonok s QAction-k a QToolBar-on


Egyre tkletesebb alkalmazsunk mindez idig fjdalmasan nlklzi az ikonokat. Ezen
azonban hamarosan vltoztatunk. Szerezznk be ikonokat. sszesen tt szeretnnk hasznlni:
egy magnak az alkalmazsnak, hrom a fjlmveleteknek j, megnyits, ments , s egy a
majdan elkszl belltsok ablaknak. Grafikusabb hajlamak rajzolhatnak maguknak, elvg-
re nem kell mindig csak programozni a szmtgpen. A grafikus hajlamukban vagy kpess-
gkben korltozottabbak pedig keressenek maguknak a neten. A knyvhz tartoz letlthet
forrsokban lv ikonok tbbnyire e knyvhz hasonlan Creative Commonos licencek,
azaz legalbb lesz dolgunk a licenc s a kszt feltntetsvel. Sebaj, gyis mindjrt megtanu-
lunk ablakot kszteni, s gy lesz mire hasznlnunk j ismereteinket. A fjlformtumra ne le-
gyen nagy gondunk ha nem valami nagyon egzotikussal llunk el, akkor a Qt-nak sem lesz.
Az ikonokat clszeren a projekt egy alknyvtrban helyezzk el. Ha nem gy tennnk, a Qt
Creator gy is pampogni fog miatta, gyhogy menjnk elbe a feladatnak.
Az ikonok trolshoz nem volna ppen ktelez erforrsfjlt17 ltrehozni, de rdemes,
gyhogy jrjunk el gy.
Erforrs-fjlt az ember gy kszt,
hogy jobb egrgombbal kattint ott,
ahol j osztlyfjlt is ksztene azrt
fogalmazunk rbuszokban, mert ez a
hely ugye Qt Creator verzinknt ms s
ms lehet , s az Add new... lehetsget
vlasztja. A megjelen prbeszdablakban
felsorolt sablonok kzl a Qt csoportban
lv Qt Resource file lesz a bartunk. Az
erforrsfjl nevt vlaszthatjuk akr
a programunk nevvel megegyezre,
a kiterjesztse meg gyis qrc lesz. Ha
megnylt, akkor elszr is meg kell adnunk 13. bra: Erforrsfjlt ksztettnk
egy prefixet (elttet), ami al kerlnek
majd a betlttt fjlok. Ha a fjlok a projekt alatti alknyvtrban vannak, akkor a knyvtr
neve is bekerl az elrsi tjukba, azaz kis projektnk esetben megkockztathat, hogy a
prefix egy egyszer per jel (/, s nem a kolostorfnk) legyen. A prefixum megadst kveten
lehetsgnk nylik a fjljaink betltsre, s e lehetsggel lnk is.
Ne felejtsk menteni a fjlt, mert a grafikus felhasznlifellet-szerkesztben csak akkor
vlaszthat ki a szvnknek kedves bra, ha a qrc fjl mr az adathordozn terpeszkedik.

17 Nem tkletes a fordts de ez van. Angolul: resource file, nem keverend ssze azzal a forrs-
fjllal, amiben a forrskdok vannak, s ami angolul csak source file.

59
Qt strandknyv 9. Ikonok, ablakok, automatikus ments

Kezdjk akkor az alkalmazs ikonjval. Mindssze annyi a dolgunk, hogy a fablak kijellse
utn a Property Editor-ban megkeressk s belltjuk a windowIcon tulajdonsgot. A lefel
mutat nylra kattintva felbukkan mencskbl a Choose Resource menpontot kell
vlasztanunk. A program fordtst s futtatst kveten a fut programokat felsorol
helyeken tlca, Alt+Tab, miegyms mr a szp j ikonunk ltszik:
A futtathat llomny az opercis rendszer
fjlkezeljben lthat ikonjnak belltsa szerencsre
platformspecifikus, gy itt most nem foglalkozunk vele18.
A Fjl men menpontjainak ikonjaival folytatjuk a
munkt. Ha mg emlksznk (s amennyiben
trtnetesen nem emlkeznnk, akkor is), a ltrehozott
menpontok QAction osztly objektumok. gy aztn a 14. bra: Hrcsg a mkuskerkben
felhasznlifellet-szerkesztben elhvhat Action - kell-e kifejezbb ikon egy
tennivallista-alkalmazsnak?
Editor rszen lthatk ha nem volna ilyen rsz a
monitorunkon, a Window men Views almenjn kell elhelyeznnk egy pipt.
Az Action Editor-on bell kattintsunk
jobb egrgombbal valamelyik soron, s
a forrsfjlbl vlasszuk ki a hozz val
ikont. Ezzel a menben mr ltszik is
a mvnk, de elbb mg tegynk meg
annyit, hogy a mensor (menuBar) alatt, de
minden ms fltt megjelen, helyesebben
bujkl eszkztrra (toolBar) hzzuk ki
egrrel az ikonnal is br QAction-ket.
Ilyen lesz a kp:
Mit is mveltnk? A QAction osztly
objektumaink most egyszerre kt helyen
15. bra: A QAction-k bekltztek
jelennek meg: a menben s az eszkztron. az eszkztrra (is).
Ez a kt megjelens egy s ugyanaz az
objektum, a hozz kapcsold slot is egy maradt. Nem kell kt helyen figyelni r, ha valamit
vltoztatunk. Bar.
Programunk tbbnyelvstse ta most elszr fogunk j karakterlncokat felvenni, gyhogy
bevezetnk egy j eljrst is: itt, a knyvben angolul rjuk a karakterlncot, majd megadjuk a
magyar fordtst is, ha fordtand. A fordtsi eljrs persze elolvashat az elz fejezetben,
de azrt mg egyszer, rviden: lupdate, hogy az j karakterlncok tkerljenek a ts-fjlba
(ha tbbfle fordts is kszl, akkor fjlokba), fordts a Qt Linguist-ben, majd lrelease,
hogy ellljon a qm-fjl, amit szksg szerint a qm-fjl msolsa kvet a futtathat llomny
knyvtrba.
Hozzunk ltre j ment Others (Egyebek) nven, majd helyezznk el benne kt menpontot
Settings (Belltsok) s About (Nvjegy) szveggel. Az actionSettings objektumnak
lltsuk be az ikonjt, majd hzzuk az eszkztrra. Ksztsk el a slot-jt mindkettnek, a
triggered() signal-ra.
Jhet a kvetkez alfejezet.

18 De aki nem nyugszik nlkle, annak tessk: http://qt-project.org/doc/qt-4.8/appicon.html

60
9. Ikonok, ablakok, automatikus ments Qt strandknyv

9.2. Mdos s egyb prbeszdablakok, a QTextBrowser s jabb


adalkok az erforrsfjlok termszethez
A nvjegy ablaknak megvalstsval folytatjuk kisded programunk fejlesztst. j osztlyt
ksztnk az ablaknak, de nem a sima C++-osztlysablonok kzl vlogatunk, hanem a Qt
csoporton belli Qt Designer Form Class lehetsget vlasztjuk. A megjelen prbeszdablakbl
pedig a Dialog without Buttons vlik szimpatikuss. Az osztly neve legyen AboutWindow. A
mainwindow.h fjl fejlcei kztt helyezzk el az aboutwindow.h llomnyt, majd ksztsnk
az ablakunknak privt mutatt:
AboutWindow *aboutWindow;

Az About menpont slot-jnak trzsben pldnyostsuk az objektumot, ahova a mutat


mutatni fog:
aboutWindow = new AboutWindow(this);

Ha most futtatjuk a programunkat, akkor a megfelel kattintgatsokat kveten ltrejn az


ablak, pp csak nem ltszik. Mr az kori ptszek sem szerettk a lthatatlan ablakokat, s
ezzel lnyegben mi sem vagyunk mskpp. Mutassuk ht meg!
Elbb azonban van nmi megfontolnivalnk. Az angolul beszl okosok szerint egy ablak
vagy modal (mddal br, modlis, mdos), vagy modeless, ms szval non-modal (md
nlkli)19.
Az elbbi csoportba tartoz ablakok felbukkansuk utn lehetetlenn teszik a felhasznl
szmra a program ms ablakaival val kommunikcit: adatbevitelt, gombok nyomkodst,
miegymst. St, megklnbztethetnk ablakmodlis s alkalmazs-modlis ablakokat: az els
esetben csak a szlablak fel gtolt a felhasznli interakci, a msodik esetben az alkalmazs
valamennyi egyb ablaka fel. Ilyen ablakokat akkor hasznlunk, ha valami elkpeszten
fontosat akarunk megtudni a felhasznltl, vagy tutibiztosak akarunk lenni abban, hogy a
felhasznl megrtette az elkpeszten fontos zenetnket.
A modeless ablakok pedig, legyen brmilyen meglep, de pp az ellenttei az elz csoportba
tartozknak, azaz fellk nyugodtan foglalkozhatunk a tbbi ablakkal, ahogy pp ri kedvnk
diktlja. Tipikusan ilyen a szvegszerkesztnk helyesrs-ellenrz ablaka, ami felhvja
a figyelmnket a helyesrsi hibra, s lehetv teszi, hogy az eredeti szvegben, a msik
ablakban javtgassuk a szveget.
A Qt-ban tbb mdszerrel is elrhet egyik vagy msik viselkeds, s ebben a
strandknyvben termszetesen a legegyszerbb esetet ismertetjk20. Aki a ToDoList nvjegyt
modal ablakknt nyitn meg, a slot trzsnek msodik soraknt hasznlja a
aboutWindow>exec();

sort. A tbbiek pedig gy jutnak modeless ablakhoz, hogy inkbb a


aboutWindow>show();

19 Hogy mirt pont gy hvjk ket, az a kvetkez magyarzatbl nem fog kiderlni, de nmi Wi-
kipedia-olvasgatssal kpet kaphatunk a dologrl. Akit rdekel a dolog httere, annak ajnlott a
http://en.wikipedia.org/wiki/Modal_window s a http://en.wikipedia.org/wiki/Mode_error#Mo-
de_errors hivatkozson tallhat szvegek ttanulmnyozsa.
20 A tmban elmlyedni vgyk kedvrt persze itt egy j kis hivatkozs: https://qt-project.org/
doc/qt-5.1/qtwidgets/qdialog.html#details

61
Qt strandknyv 9. Ikonok, ablakok, automatikus ments

utastst adjk ki. Esetnkben lnyegben mindegy, hogy melyik utat vlasztjuk, de mieltt
dntennk, prbljuk ki mindkt lehetsget.
Kisebb elmlkeds kvetkezik, amit vgigkvetve taln jobban megrtjk a kt viselkedsmd
mlysgeit, radsul kiszrnk egy ronda programhibt is. Most ugyebr mutatt adtunk meg
az ablakunknak, s a heap-en hoztuk ltre az ablakot, azaz a ltrehozott ablak letben marad a
MainWindow::on_actionAbout_triggered() slot lefutst kveten is.
Megtehettk volna azonban azt is, hogy az ablakot a slot-ban helyi hatskr (angolul: local
scope) vltozba pldnyostjuk az
AboutWindow aboutWindow;

sorral. Ha ekkor az
aboutWindow.exec();

sorral nyitjuk meg az ablakot, akkor a program szpen megvrja, amg be nem zrdik az
ablak a slot futsa addig felfggesztdik , s minekutna az ablak bezrul, a program elri
a slot vgt, s megsemmislnek a helyi objektumok, gy az ablakunk is. A felhasznl a
vltozsbl semmit sem vesz szre.
Amennyiben az ablak megjelentsre az
aboutWindow.show();

mdszert vlasztjuk, a felhasznl majd panaszkodik, hogy nincs nvjegy-ablak. Pedig van,
csak pp az utastsunkat kvetve a slot vigyorogva tovbb fut, s j esllyel mg azeltt vget
r, hogy az ablaknak ideje lenne megnylni.
Elmlkedsnk els tanulsga teht, hogy modeless ablakot okos fejleszt a heap-en hoz ltre.
A fenti mondatbl az kvetkezik, hogy lesz mg tanulsg. Valban: nekiltunk a
programozsi hiba elhrtsnak.
Mi is trtnik, amikor a slot lefut? Ltrehozunk egy AboutWindow osztly ablakot, a
memriacmt rtkl adjuk az aboutWindow mutatnak, majd megmutatjuk az ablakot. Slot
vge. Persze tudjuk, hogy nem kell szszlnnk az ablak objektumnak megsemmistsvel,
hiszen a this szl miatt a Qt majd takart helyettnk. Igen m, de ha a felhasznl jra
megnzn a nvjegyet, akkor lesz mg egy ilyen ablakunk, radsul az elznek elfelejtjk a
cmt. Az elz nvjegy-ablak gy aztn ott fog tblbolni a memriban, majdnem mindenkitl
elfeledve, amg vget nem r az alkalmazsunk futsa. Erre meg semmi szksg, nem igaz?
(A jelensg szemltetsre a pldnyostst vgz sor utn helyezzk el a qDebug() <<
aboutWindow; sort. Valahnyszor megnyitjuk a nvjegyet, mindig ms memriacmet ltunk
majd.)
Valahogy j volna ellenrizni, hogy a mutatnk null-pointer, vagy sem, mert ha az, akkor
mg nincs ilyen ablakunk, ha meg nem az, akkor mr van. A legokosabb, ha elszr pldul a
fablak konstruktorban az
aboutWindow = 0;

sorral NULL rtkre inicializljuk a mutatt kzben megllapthatjuk, hogy a konstruktor


mostanra csnyn elhzott. Ezek utn a slot-ot az albbi formban vglegestjk:

62
9. Ikonok, ablakok, automatikus ments Qt strandknyv

void MainWindow::on_actionAbout_triggered()
{
if(!aboutWindow)
aboutWindow = new AboutWindow(this);
aboutWindow>show();
}

gy a slot nem kszt mindig j ablakot, csak jra meg jra megmutatja neknk a rgit. Ha
az ablakon mr lesz mit vltoztatni, akkor mg a vltozs is megmarad majd kt megmutats
kztt.
Elmlkedsnk msodik tanulsga teht az, hogy vigyznunk kell, nehogy ellepjenek
bennnket az ablakok, kivve, ha pp ez a cl.
Mg egy utols megjegyzs (aztn befejezzk az elmlkedst, s vgre nekiltunk a
nvjegyablak benpestsnek): ha a felhasznl esetleg csak httrbe tette a modeless ablakot,
akkor knnyen elfelejtkezik rla, s nem rti, hogy a menbl megnyitva mirt nem vlik
lthatv. Ilyen esetek kezelsre rdemes megismerkednnk a QWidget::raise() slot-tal s a
QWidget::activateWindow() tagfggvnnyel.
Nos, most, hogy megnyithat a nvjegyablak, kutyagoljunk t az aboutwindow.ui fjlra.
Az ablak cme (windowTitle tulajdonsg a Property Editor-ban) legyen About ToDoList
(A ToDoList nvjegye). Megnylik a grafikus szerkeszt. Az ablak aljra helyezznk el egy
QPushButton-t, s a feliratt lltsuk I just got smarter (Okosabb lettem) rtkre.
A nyomgomb fl pedig hzzunk ki egy QTextBrowser-t, azaz szvegnzegett. A
QTextBrowser osztly a QTextEditor (szvegszerkeszt) leszrmazottja. A szveget csak
megjelenteni tudja ilyen rtelemben le van buttva , viszont extrt is tud: lehet a benne lv
hiperszvegben naviglni.
A textBrowser objektum ltrejttt kveten lltsunk be elrendezseket, hogy az
tmretezdsek szpen menjenek, majd gondolkodjunk el, mifle okossgokat runk majd ide.
A knyv webhelyrl letlthet verzi kes angol nyelven r pr sort arrl, hogy mi is ez az
alkalmazs, majd a licenceket kezdi taglalni, aminek tbb j oldala is van: rszint a felhasznlt
ikonok licence teszi ezt ktelezv, rszint lesz okunk kpet s hivatkozst is elhelyezni. A Qt
Creator-ban a textBrowser-re kattintva meg is szerkeszthetnnk a tartalmt gy kapnnk
egy statikus, bedrtozott szveget , de az tl egyszer volna.
Kicsit lmodozzunk: ttelezzk fel, hogy a projektnk mr nagy s nemzetkzi, s a nylt
forrskdnak ksznheten nkntesek hada dolgozik rajta. A hozzjrulk szma verzirl
verzira n, s mr a kutya nem gyzi a nvsoruk frisstgetst. Szerencsre az intelligens
verzikezel, meg a scriptjeink mindig ellltjk a nvsort, de semmi kedvnk a msols-
beillesztgetshez minden fordts eltt s bokros teendink megakadlyoznak abban, hogy erre
kln mechanizmust implementljunk. Nos, a Qt itt is segt tiszta j fej, nem?
A QTextBrowser osztly objektumok forrsul ugyanis megadhat egy nmileg csupasz,
HTML4 nyelv forrsfjl. Csupasz alatt azt rtjk, hogy az elejrl lehagyjuk a !DOCTYPE
rszt (a Qt majd odateszi), lnyegben csak a <html> s a </html> cmkt, illetve a kzttk
lv rszt adjuk meg benne. Radsul nem is hasznlhatjuk a teljes nyelvi kszletet, de azrt
ellesznk ennyivel21. Ha ezt a html-fjlt elhelyezzk az erforrsok kztt (pontosan gy,
mint az ikonokat, lsd az elz alfejezetben), akkor a textBrowser objektumot kivlasztva,

21 Pontos lista: http://qt-project.org/doc/qt-4.8/richtext-html-subset.html

63
Qt strandknyv 9. Ikonok, ablakok, automatikus ments

a Property Editor-ban a source tulajdonsgnl megadhat ez a fjl. Az ikonokhoz hasonlan


a html-fjlt is azrt rdemes az erforrsok kz helyezni, mert gy nincs tbb gondunk arra,
hogy a futtathat fjllal egytt msolgassuk.
Nos, hogy ez az eljrs miknt segti a npes fejlesztgrda nvsornak napra kszen tartst
az eljvend nagy projektnkben? Ht, ha a html-fjl ellltsa megoldott, akkor a fordtsok
sorn mindig az pp aktulis vltozat kerl a programunkba. Remeg dolog, prbljuk is ki!
Ksztsnk egy html-fjlt, helyezzk el az erforrsok kztt, majd adjuk meg a textBrowser
objektum forrsaknt. Meg is jelenik a tartalma, mr a felhasznlifellet-szerkesztben is
ltjuk. Ha mdostjuk a fjlt, s szeretnnk megnzni a vltozst, nos az nem ltszik (legalbbis
a szerz Qt Creatorban nem), mg akkor sem, ha jra belltjuk a textBrowser forrsaknt.
Mondjuk ez nem olyan kardinlis problma mert a program fordtst s futtatst kveten az
igazi nvjegy mr tkrzi a vltozsokat, s ez az, ami fontos.
Ha az a vgyunk, hogy a html-fjlban lv hivatkozsokat egrkattintsra megnyissa az
alaprtelmezett bngsz, akkor a Property Editor-ban tegynk pipt az openExternalLinks
tulajdonsg mell.
Megmutattuk mr a hrcsgnek, hogy rajta van az ikonon? s legalbb megismerte magt?
Ja, mg valamit. Ha kevs a QTextBrowser, illetve a QTextEditor osztly HTML-ismerete,
akkor hasznlhatjuk pp a QWebView osztlyt is, amely lnyegben bngszt varzsol a Qt-
alkalmazsunkba. Az elll bngsz a hasznlt Qt-verzitl fggen vagy a WebKit-re, vagy
a Chromium-fle motorra pl. A Qt ugyanis nagyjbl e fejezet elkszltvel egy idben
vltott az elbbirl az utbbira22. Persze egy kis nvjegy megjelentshez ers tlzs lett volna
egy komplett bngszt indtani a httrben ha napjaink szemlyi szmtgpeinl ez nem is
annyira szempont, vannak mobileszkzk, ahol ersen az.

9.3. Automatikus ments a QTimer osztly hasznlatval


Elsknt a Belltsok ablakot ksztjk el. Lnyegben gy dolgozunk, ahogy az imnti
ablaknl: j osztlyt hozunk ltre a prbeszdablaknak SettingsWindow nven, s az on_
actionSettings_triggered() slot-ban megjelentjk a settingsWindow nev pldnyt,
mgpedig az exec() tagfggvny futtatsval, azaz modal ablakknt. A klnbsg annyi, hogy
ezt az ablakot a stack-en, azaz a slot-on bell pldnyostjuk.
Mire is lesz j ez a belltsablak? Ht arra, hogy megadhassuk: akarunk-e automatikus
mentst, s ha igen, hny percenknt.
A felhasznlifellet-tervezben lltsuk be az ablak cmt. Helyezznk el az ablakon egy
QCheckBox-ot. Ebbe tesszk a pipt, ha krnk automatikus mentst, gyhogy a neve legyen
autosave todo list file (feladatlistafjl automatikus mentse). Kell mg egy QSpinBox a
percek szmnak megadsra. lltsuk be a PropertyEditor-ban a maximum tulajdonsgt 60-
ra, a singleStep tulajdonsgt 5-re (ennyivel fogja lptetni a nyilacska az rtket), tovbb
alaprtelmezetten legyen kikapcsolva, azaz az enabled tulajdonsg melll vegyk ki a pipt.
Tegynk a QSpinBox el s mg egy-egy QLabel-t, autosave in every (automatkus ments),
illetve minutes (percenknt) felirattal. Az elsnl adjuk meg buddy-knt a QSpinBox-
ot. Helyezznk mg el kt nyomgombot. Az egyik neve legyen buttonOk, a msik
buttonCancel, a feliratuk pedig and thats how it should be (s legyen gy), illetve silly me
(butuska voltam). lltsunk be gyorsbillentyket s ksztsnk elrendezst. Ha van kedvnk, a
22 http://blog.qt.digia.com/blog/2013/09/12/introducing-the-qt-webengine/

64
9. Ikonok, ablakok, automatikus ments Qt strandknyv

fordtst is elkszthetjk legalbb ltjuk,


hogy a Qt nem akad fenn azon, hogy ha
magyarul ms hosszsg egy szveg, mint
angolul.
A kvetkez dolgunk nmi
kattintgatssal slot-ot kszteni a checkBox
objektum stateChanged() signal-jhoz.
A slot trzst gy akarjuk elkszteni, hogy
ha a checkBox-ba pipt tesz a felhasznl,
akkor bekapcsoljuk a spinBox objektumot,
ha meg kiveszi a pipt, akkor kikapcsoljuk.
me a slot a maga teljes szpsgben: 16. bra: A belltsablak elrendezse

void SettingsWindow::on_checkBox_stateChanged(int arg1)


{
if(arg1 == Qt::Unchecked)
ui>spinBox>setEnabled(false);
else if(arg1 == Qt::Checked)
ui>spinBox>setEnabled(true);
}

Az egsz tpus arg1 vltoz megmondja, hogy milyenre vltozott a checkBox llapota.
Valjban elvileg hrom eset lehetsges: piplatlan, rszlegesen piplt s piplt. A hrom
llapotnak rendere a 0, 1 s 2 szm , illetve a Qt::Unchecked, a Qt::PartiallyChecked
s a Qt::Checked nevestett konstans (angolul: enum) felel meg. Ha az egyszeri programoz
csodlkozik, hogy valami hogy lehet rszlegesen piplva, akkor megjegyezzk, hogy vannak
esetek, amikor a bellts alatti hierarchiaszint tovbbi belltsai kztt egyes rtkek
piplva vannak, msok nem. Az ilyesmit halovny pipa jelzi grafikusan. Szerencsre piciny
programunkban ilyen borzadlyos eset nem fordul el.
Kvetkez dolgunk a kt nyomgomb slot-jainak megrsa. A buttonCancel gomb clicked()
signal-jt a settingsWindow ablak close() slot-jhoz ktjk. Rgen mveltnk mr ilyet, vi-
szont akkor teljesen grafikusan tettk ha mr elfelejtettk volna a dolog mikntjt, lapozzuk fel a
msodik fejezetet, azon bell is A signals and slots mechanizmus cm rszt, s informldjunk.
A buttonOk slot-ja mr hzsabb tma: itt az ideje a belltsok trolsnak. gy dntnk,
hogy ismt hasznlatba vesszk a QSettings osztlyt. Ha mg emlksznk, ez gy megy, hogy
ha belltottuk a cg nevt, internetes cmt s az alkalmazs nevt mrpedig mi belltottuk
, akkor a helyben pldnyostott QSettings osztly objektum azonnal tudja, hol kell trolni
a belltsainkat. gy aztn troljuk is ket. A trols vgeztvel meg ugye csak be kell zrni az
ablakunkat. Mindez egytt gy nz ki:
void SettingsWindow::on_buttonOk_clicked()
{
QSettings settings;
settings.setValue("GeneralSettings/AutoSaveEnabled", ui>checkBox
->isChecked());
settings.setValue("GeneralSettings/AutoSaveMinutes", ui>spinBox
->value());
this>close();
}

65
Qt strandknyv 9. Ikonok, ablakok, automatikus ments

Nem vagyunk mg ksz a slot-tal, mert nem szltunk mg a fablaknak, hogy mostantl
mentegessen vagy pp ne tegye , de ezt egyelre elhalasztjuk. Inkbb azzal trdnk,
hogy ha jranyitjuk az belltsablakot, akkor olvassa be, s jelentse meg a belltsokat.
Sejtjk mr, hogy akkor megint kell egy QSettings osztly objektum. Van rtelme a
konstruktorban elhelyezni, mgpedig a beolvas-megjelent utastsokkal egytt. gyhogy a
SettingsWindow osztly konstruktora az albbi hrom sorral bvl:

QSettings settings;
ui>checkBox>setChecked(settings.value("GeneralSettings/
AutoSaveEnabled", "false").toBool());
ui>spinBox>setValue(settings.value("GeneralSettings/
AutoSaveMinutes", 0).toInt());

A toBool() s a toInt() tagfggvny mr ismersnek tnik. A QSettings osztly


value() tagfggvnynek visszatrsi rtkei ugyebr QVariant tpusak, amirl korbban
mr megemltettk, hogy lnyegben a mittomnmiez tpust azonostjuk vele. A QVariant
osztly persze knl olyan fggvnyeket, amelyekkel a benne trolt rtkeket emberi tpusokra
tudjuk alaktani: a toBool() s a toInt() fggvny pp ilyen.
Mr csak az a krds, mik a QSettings::value() tagfggvny vessz utni paramterei,
nevezetesen a false, illetve a 0? Nos, ezek alaprtelmezett rtkek. Ha a belltsok kztt
nincs ilyen bellts, akkor a settings objektumunk ezeket adja vissza. gy megsprolhat
egy kr, amiben a belltsi kulcs jelenltt ellenriznnk a QSettings::contains()
tagfggvnnyel.
Eddigre taln szget ttt a fejnkbe, hogy ha mr gyis ktszer pldnyostunk magunknak
QSettings osztly objektumot, nem volna-e rtelme a stack helyett a heap-en elhelyezni, this
szlvel, hogy az ablak megsemmislsvel ez az objektum is megsemmisljn? De, alighanem
volna, gyhogy tegyk is meg. Ne feledjk a value() s a setValue() tagfggvny-hvsok
eltti pontot nylra cserlni, hiszen a settings mostantl mutatknt l az osztlyban. Nem
mernnk arra nagyobb sszegben fogadni, hogy a programunk ezzel minden platformon
gyorsabb is lett, de tlthatbb igen, s ugye az a mai vilgban a legtbb esetben fontosabb, mint a
sebessg.
Akkor ht visszatrhetnk ahhoz a problmhoz, hogy a fablakot is rtesteni kellene a
helyzet vltozsrl, ha a felhasznl gy dnt, hogy az j belltsok jk.
Ugye megint az a helyzet, hogy a gyerekobjektum szl a szlhz, azt meg gy rdemes neki,
hogy signal-t emittl, amit a szl slot-ja elkap majd.
Vagy mgsem? Nem annyira, ugyanis a QObject::connect() tagfggvny a kld
objektum mutatjt szeretn megkapni, neknk meg helyi objektumunk van, mutatnk nincs
hozz. Ha nagyon akarunk, persze tudunk hozz mutatt szerezni, de van egyszerbb mdszer:
visszaadjuk, hogy rvnyesteni akarja-e a felhasznl a vltoztatsait, vagy sem: a buttonOk,
vagy a buttonCancel gombot nyomta-e le.
Mindezt gy valstjuk meg, hogy a SettingsWindow::on_buttonOk_clicked() slot
utols sort msikra cserljk:
this>done(QDialog::Accepted);

Ez a sor is bezrja az ablakot, a vezrls visszakerl a MainWindow::on_actionSettings_


triggered() tagfggvnyhez. A close() tagfggvny hasznlathoz kpest az a klnbsg,
hogy itt megmondhat, hogy vgl is milyen eredmnnyel zrult be az ablak.

66
9. Ikonok, ablakok, automatikus ments Qt strandknyv

A QDialog::Accepted megint csak egy enum, az rtke 1. Lnyegben csak azrt nem azt
rjuk oda, hogy 1, mert gy jobban olvashat marad a kd.
gy a Belltsok ablakot megnyit tagfggvnyben mr van rtelme vizsglni a
settingsWindow.exec() utasts kimenett, hiszen az vgre nem minden eseteben 0. Az
on_actionSettings_triggered() slot belsejbe teht rhatnnk, hogy

if(settingsWindow.exec())
tegynkOkosat();

mi azonban, megint csak a jobb olvashatsg kedvrt a


if(settingsWindow.exec() == QDialog::Accepted)
tegynkOkosat();

formt vlasztjuk. Mr csak az a krds, hogy mi legyen az az okossg? Sok vlasztsi


lehetsgnk van, de figyelembe kell vennnk, hogy akkor is be kell olvasnunk a belltsokat,
azaz gyis kellene egy privt tagfggvny , ami beolvassa ezt a kt belltst, s ami a program
fablaknak konstruktorbl hvhat.
Mit is kell majd tennie pontosan ennek a tagfggvnynek? Beolvasni a kt belltst, eddig
fix. Kelleni fog hozz egy QSettings osztly objektum. Minthogy a fablakban mshol
gy sem hasznljuk, meg ht annyira sokszor elvileg nem fog kelleni (a legtbb esetben csak
a program indtsakor), ltrehozhatjuk a tagfggvny stack-jn is. s mi lesz a beolvasott
adatokkal?
Most vesszk el az alfejezet cmben begrt QTimer osztlyt. Az ebbl az osztlybl
pldnyostott objektumok azt tudjk, hogy a belltott id leteltvel, illetve belltott
idkznknt (azaz ismtlden) emittlnak egy szp kvr timeout() signal-t, magyarn
elordtjk magukat, hogy Id!. Nos, a tagfggvnynk az idzts belltst vgzi majd el.
A mainwindow.h fjl fejlcei kz vegyk fel a <QTimer> nevt is, majd hozzunk ltre egy
QTimer osztly objektumra mutat mutatt timer nven, a fablak konstruktorban (Eml-
tettk mr, hogy nagyon hzik?) pldnyostsunk hozz objektumot timer nven ne felejtkez-
znk el a szl (this) belltsrl. Ha szeretnnk, a qDebug() << timer>isSingleShot();
utastssal megbizonyosodhatunk rla, hogy a ltrejtt objektum ismtlden ordtozik majd,
ha letelik az id.
Deklarljuk vgre a privt fggvnynket:
void loadAutoSaveSettings();

formban. A trzst pedig alaktsuk ilyenre:


QSettings settings;
if(settings.value("GeneralSettings/AutoSaveEnabled", "false").
toBool())
timer>start(settings.value("GeneralSettings/AutoSaveMinutes").
toInt()*60000);
else
timer->stop();

Ltrehozunk benne egy settings objektumot, ami beolvassa, hogy kell-e automatikusan
menteni (ha nincs ilyen bellts, akkor is "false" rtkkel tr vissza. Ha kell, akkor
beolvassuk azt is, hogy hny percenknt kell. Ezttal nem mondjuk meg, mi a teend, ha nincs
ilyen bellts, ugyanis, hacsak kzzel nem piszkljuk a belltsokat, akkor vagy mindkett

67
Qt strandknyv 9. Ikonok, ablakok, automatikus ments

bellts van, vagy egyik sem. A percek szmt megszorozzuk sszesen 60000-rel, merthogy
a timer objektum ezredmsodpercekben gondolkodik. A kapott szmmal elindtjuk a
visszaszmlls-sorozatot. Ha nem kell automatikus ments, akkor szmtunk r, hogy eddig
esetleg be volt lltva, csak pp most kapcsolta ki a felhasznl, gy lelltjuk az idztst. Ha
eddig sem ment, akkor sem lesz baj a dologbl.
A ksz tagfggvnyt hvnunk kell a fablak konstruktorn kvl az on_actionSettings_
triggered() slot-bl is, onnan, ahol az imnt mg csak annyit tudtunk, hogy valami okosat
kell tennnk.
Odig jutottunk ht, hogy van egy szp s pontos timer objektumunk, amely pontosan jelzi,
ha itt az Id!, de a franc sem figyel r. A megolds persze megint a fablak konstruktort
hizlalja:
connect(timer, SIGNAL(timeout()),
this, SLOT(on_actionSave_triggered()));

Kisebb problma, hogy a slot sajtossgai miatt, ha mg nem volt mentve a fjl, akkor az id
lejrtval kapunk az arcunkba egy krdst a leend fjlnvrl. Ezzel azonban nagyvonalan
nem trdnk.
Itt az Id!, hogy kitakartsuk a hrcsg terrriumt, a kvetkez fejezetben pedig a
magunk hza tjn takartunk.

68
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

10. Komolyan hasznlni kezdjk a Model


View minta lehetsgeit
Ha vgeztnk a hrcsg terrriumnak kitakartsval, jra tgondoljuk, hogy mit is tudunk
a Model-View (magyarul: modell-nzet) mintrl. Van ugye egy nzetnk, ami nem ms, mint
egy Qt-objektum. Meg van egy modellnk, ami egy msik Qt-objektum. A kt objektumot
sszekapcsoljuk, mgpedig gy, hogy a nzet tudja, hogy ki az modellje, a modellnek viszont
halvny lila gze nincs arrl, hogy ki az nzete. St ilyesmit mg nem mveltnk, de akr
mvelhettnk is volna egyetlen modellhez tbb nzetet is trsthatunk.
s itt a lnyeg: a modell vltozsairl a nzet rtesl, s a vltozsoknak megfelelen frissti
magt, mindezt anlkl, hogy akr mi, akr a hrcsg egyetlen mozdulatot tennnk az gy
rdekben.
Eddig kt esetben hasznltunk modellt. Az egyik esetben egy QStringListModel
objektumot hasznltunk az automatikus kiegszts mkdtetsre, mgpedig gy, hogy a
QCompleter osztly objektumunknak megmondtuk, hogy melyik QStringListModel
az modellje, azaz honnan vegye a tippjeit az automatikus kiegsztshez. Amikor ezek
utn kedvnk szottyant, mdostottuk a modellt, s a QCompleter osztly objektumunk
errl rteslt, pedig nem szltunk neki. Azzal ugyanis, hogy a QCompleter::setModel()
tagfggvny hvsval megmutattuk neki, hogy melyik objektum a modellje, rruhztuk azt a
feladatot s felelssget is, hogy a modell vltozsait figyelje.
A msik esetben egy QGraphicsView osztly objektumot hasznltunk nzetknt, a
modelll pedig egy QGraphicsScene osztly objektum szolglt. Ez esetben a nzet nevben
is benne van, hogy egy View, azaz nzet. A grafikonunk oszlopait, svjait a sznen, a
QGraphicsScene osztly objektumban helyeztk el, s sosem szltunk a nzetnek, hogy mit
gykdnk a sznen, mert mindig a nzet dolga erre figyelni.
A kt fenti esetbe azonban pp csak rintettk a Modell-Nzet minta hasznlatban
rejl lehetsgeket, ugyanis a programunk lnyegi rsze, nevezetesen a teend-kategria
prosokbl ll feladatlista mindezidig nem kerlt modellbe, minden vltoztats esetn kzzel
gykdtnk annak rdekben, hogy a vltozs ltszdjk is. Na, majd most.

10.1. Elem-alap s modell-alap widget-ek


Ebben a rvidke alfejezetben megismerkednk az talakts sorn hasznlt egyik
legfontosabb widget-nkkel, s bemutatjuk a testvreit: a hgt s a nvrt.
Kicsit zavar ebben a magyar nyelv knyvben, hogy legalbb kt szakkifejezst fordtunk
az elem szval. Az egyik kifejezs a widget, azaz a grafikus felhasznli fellet eleme.
Fordthatnnk pp bigynak is, de valahogy gy alakult, hogy a magyar szaknyelvben nem
honosodott meg a sz eredett (window + gadget) tekintve helyes, de komolytalanul hangz
ablakmtyr, ablakbigy sz. Ms programozsi krnyezetekben a widget szval nagyjbl
egyenrtk control (magyarul: vezrlelem, vagy rviden vezrl) sz terjedt el, de ebben a
strandknyvben pont a Qt-rl van sz, ott meg widget-ek vannak. A msik sz pedig az item,
azaz valamilyen sornak, adatszerkezetnek az eleme.
E hosszabb kitr utn lssuk a widgeteinket. Ha a Qt Creator grafikus felhasznlifellet-szer-
kesztjnek bal oldaln nv szerint szrnk, mondjuk a list szval, akkor kt widget-et tallunk:

69
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

Az egyik az elem-alap QListWidget, a msik a modell-


alap QListView. Figyeljnk a widget s a view nvre.
Mindkett widget (mert persze mind a kett widget, azaz
grafikus elem) arra val, hogy listt jelentsnk meg
vele, de az egyik esetben elemenknt, azaz itemenknt
pakolgatjuk a megjelentend elemeket, a msikban az
elemek egy modellbe kerlnek, s a QListView nev
widget tulajdonsgai kztt megadjuk a modellt. 17. bra: Listk megjelentsre
alkalmas elemek
Szrjnk most a table szra!
Megint ketten vannak. s, ahogy mr sejtjk, az egyik
megint elem-alap, a msik meg modell-alap. Ezt az
elemet, a QTableView nev widget-et fogjuk a
kzeljvben hasznlatba venni. A hgval, a QListView
nzettel ellenttben ebben az elemben nem egy, hanem
ktdimenzis adatszerkezeteket jelentnk majd meg. Mr
18. bra: Tblzatok meg-
csak a nvr s az ellenttprja van htra a nagy jelentsre alkalmas elemek
bemutatkozsbl. Szrjnk a tree (fa) szra!
A tblkkal ebben a knyvben nem foglalkozunk. k
az igazi nagygyk: akrhny dimenzis adatszerkezetek
megjelentsre jk, amennyiben minden adat elhelyezhet
egy hierarchiban, miden adatnak megadhat a szleleme.
A szl-gyerek kapcsolatok mentn fv alakthat az
adatszerkezet, gy oldjk meg a sokdimenzis struktra
19. bra: Bonyolultabb
ktdimenzis kijelzn val megjelentst. adatszerkezetek megjelentsre
alkalmas elemek
10.2. jratervezs
A kvetkez nhny fejezetben a programunkat jszervel az elejrl kezdve jrarjuk,
aminek lssuk be taln legfbb ideje volt mr. Persze ilyesmit profi Qt-programoz23 nem
tesz na de profi Qt-programoz nem is olvasgat Qt-strandknyveket. Mondhatnnk, hogy
gondolkodhattunk volna mr korbban is, meg hogy gy kellett volna mr a legelejtl megrni
igen m, de a knyv elejn lnyegesen kevesebbet tudtunk a Qt-rl, mint most. Taln mris
sejtjk, hogy a mostani vltozat sem lesz vgleges, de megnyugtatunk mindenkit: ha ezzel
a vltozattal is kszen vagyunk, mr csak egy markns talakts marad htra, s arra mr
csak a knyv vge fel kertnk sor. s mg az a vltozat is messze ll majd a tkletestl. Az
a helyzet, hogy egy program ugye ritkn van ksz a fejlesztsi folyamat sorn legfeljebb a
kiadsra rett llapotig juthatunk el, a Hell vilg programok bonyolultsgt meghalad
projekteken mindig van mit javtani. gyhogy e felett ne is bslakodjunk, fogadjuk el, hogy gy
van s ksz.

10.3. Mit tartunk meg az elz vltozatbl?


Nem sok mindent. Kezdjnk j projektet, majd msoljuk t a projekt mappjba az about
s az icons mappinkat igen, tartalommal egytt. Jhet mg az aboutwindow.cpp, az

23 Meg ugye semmilyen ms nyelven programoz profi programoz sem tesz ilyet. Kivve, ha a
programoz ugyan profi, de nem azon a nyelven, amelyiken pp programot r.

70
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

aboutwindow.h , aboutwindow.ui s a ToDoList.qrc. Vegyk is fel ket a Qt Creator-ban


is a projekt fjljai kz (nem elg, ha benne vannak a mappban), valamint helyezzk el a
fordtsfjl (-fjlok) sort a projektfjlban ha elfelejtettk mr a mdjt, lapozzunk vissza.
Maguk a fordtsfjlok nem kellenek, majd ltrehozzuk ket jra.

10.4. A fablak kialaktsa


A fablak kpnek felhasznlsval alaktsuk a magunkt is ilyenre. Albb kvetkeznek az
egyes objektumok nevei is, amelyeket azrt rdemes gy belltanunk, mert a programunk is ezt
hasznlja majd.
A gombok (QPushButton osztly)
objektumnevei (zrjelben a feliratok egy-
egy lehetsges magyar fordtsa):
buttonDelete
zz (feladat trlse)
buttonUndo
zz (utols trls visszavonsa)
buttonNewAfter
zz (j a kijellt utn)
buttonNewBefore
zz (j a kijellt eltt)
buttonUpSelected
zz (felfel a kijelltet)
zzbuttonDownSelected (lefel a kijelltet)
A kzpen lv nagy fehr semmi
20. bra: A fablak gombja
egy QTableView, aminek a fantziads
tableView nevet adtuk. A File menben lv QAction-osztly objektumok neve s felirata
(zrjelben a magyar fordts):
actionNew,
zz New (j)
actionOpen,
zz Open (Megnyits)
actionSave,
zz Save (Ments)
actionSave_as,
zz Save as... (Ments msknt)
Az Others (Egyebek) menben lv kt QAction:
actionSettings,
zz Settings (Belltsok)
actionAbout,
zz About (Nvjegy)
Hozzuk ltre az Others men QAction-jei szmra a triggered() signal-hoz ktd slot-
ot. Ahol van ikon, rendeljk a QAction-hz, majd hzzuk a QAction-t az eszkztrra.
A msodik fln helyezznk el egy msik QListView-t, a neve legyen searchView. Szksg
lesz mg egy QLineEdit-re (searchEdit nven), meg egy QLabel-re. Ez utbbi szvege legyen
list tasks in category: (a megadott kategria feladatainak listja:), s adjuk meg buddy-
jul a searchEdit-et.
ri kedvnknek megfelelen definiljunk gyorsbillentyket az objektumokhoz. A
QTabWidget els, task list felirat fle legyen eltrben, s a tab-sorrendben legyen a
listView az els szm objektum.
A fablaknak lltsuk be a cmt, s adjuk meg az alkalmazs ikonjt.
A mainwindow.cpp fejlcei kztt helyezzk el az #include "aboutwindow.h" sort, majd
az on_actionAbout_triggered() slot trzst alaktsuk a kvetkezkpp:

71
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

AboutWindow aboutWindow;
aboutWindow.exec();

Ezzel a mozdulattal a legeslegfontosabb rsze mr mkdik is a programunknak. Ha nagyon


elvesztnk a tennivalban, a knyv webhelyrl le tudjuk tlteni a mostani llapotnak
megfelel archvumot. Aki elg szemfles, annak taln feltnt, hogy nem alaktottunk ki flet
a grafikonunknak nos, nem is fogunk.

10.5. A modell, meg az osztly, ami kezeli


Ksztsk el a ModelManager osztlyt, mint a QObject osztly leszrmazottjt. Ezt az
osztlyt (illetve a belle pldnyostott modelManager objektumot) fogjuk megbzni majd
mindennel, ami a modellnk manipulcijval kapcsolatos. Egyelre azonban mg nincs
modellnk, ksztsnk ht.
A Qt-ban minden modellek se a QAbstractItemModel osztly. Ez, mint a neve is mutatja,
elgg absztrakt, magyarul elvont sosztly, telis-tele virtulis tagfggvnyekkel, azaz csak
gy tudnnk hasznlni, ha elszr is elksztennk egy alosztlyt, amiben aztn vgre
megvalstjuk a tonnnyi tagfggvnyt. No, ezt egyelre nem tesszk, mert akkor semmi
idnk nem maradna a hrcsgre, s mg a vgn az elhanyagoltsg oda vezetne, hogy srlne
szegnyke lelki fejldse.
gy dntnk teht a magunk blcsessgben, hogy a nagy s egyik leszrmazottjt, a kiss
bonyolultra sikeredett, m azonnal hasznlhat QStandardItem osztlyt fogjuk hasznlni
nemes cljaink nevezetesen, hogy ne neknk kelljen buherlni az adatstruktrnak megfelel
nzetfrisstsekkel elrsre.
A QStandardItemModel osztly nevnek els kt szavbl az derl ki, hogy ez egy olyan
modell, amelynek elemei QStandardItem osztlyba tartoz objektumok. A dokumentci
szerint az ilyen elemek rendszerint szveget, ikont, vagy jellngyzetet tartalmaznak, s
tnyleg: mi is pp szveget kszlnk beljk tlteni: egy-egy teendt, illetve kategrit.
Radsul, gy a dokumentci, az elemek ki-be kapcsolhatk, szerkeszthetk, kijellhetk, s
egyltaln, mindenfle jsgra alkalmasak. Remek.
Sztnzve az osztly konstruktorai kztt (hangynyit tl vannak terhelve, de ezt itt, a Qt-
ban lassan megszokja az ember) tallunk olyat, amelyikkel sor s oszlopszm megadsval
pldnyosthatjuk az objektumunkat. Ha most arra gondolnnk, hogy jesszompipi, ht honnan
tudjam n elre, hogy hny sor kell, akkor az appendRow() s a removeRow() tagfggvnyre
vetve szemnk fnyt, szvnk dobogsa kicsi csitulhat.
Mr most elruljuk, hogy nem csak a modelManager objektum fog trni a modellben,
hanem majd megint runk fileOperator osztlyt is. gy a modellt rdemes lesz a heap-en
elhelyezni, s a mutatjt eltenni magunknak.
Ennyi elmleti megfontols utn vgre rhatnnk pr sor kdot, nem?
A modelmanager.h fjl fejlcei kztt helyezzk el a QStandardItemModel s a
QStandardItem osztly hasznlatt lehetv tev fejlceket, majd ksztsnk egy
toDoListModel nev, QStandardItemModel osztly objektumra mutat publikus mutatt.
A ModelManager osztly konstruktorban pedig pldnyostsunk magunknak objektumot:
toDoListModel = new QStandardItemModel(0, 2, this);

72
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

Ezek szerint a ropogs j modellnk 0 sor, 2 oszlop, s nem felejtkeznk el az szl


belltsrl sem. Hogy mirt nulla sora van? Nos, azrt, mert
menet
zz kzben lesz, hogy a modellnket majd kirtennk (olyankor, amikor j listt
kezdnk, meg a ksz listk betltst megelzen),
a
zz modell rtsre a clear() tagfggvny hasznlatos,
a
zz clear() tagfggvny a sorok szmt nullra cskkenti (st, mg a fejlceket is trli, errl
mindjrt lesz sz),
azaz
zz minden trls utn j sort kell majd hozzadnunk a modellhez, amibe a drga
felhasznl az els feladatot berhatja,
radsul
zz a fejlcet is vissza kell lltani,
azaz
zz j sok ismtld feladat lesz, amit nll tagfggvnyre bzunk,
s
zz a tagfggvnynek ne kelljen mr gondolkodni, hogy most akkor van-e mr sor a
modellben, vagy nincs.
Ht, ezrt.
Ksztsk is el gyorsan a tagfggvnyt, ami mindezt elvgzi:
void ModelManager::emptyModel()
{
toDoListModel>clear();
QList<QStandardItem* > newRow;
toDoListModel>appendRow(newRow);
toDoListModel>setHorizontalHeaderLabels(QStringList() << tr("job")
<< tr("category"));
}

A clear() tagfggvnyt megbeszltk. Az appendRow() tagfggvny mutatlistt vr, gy


elszr el kell ksztennk ezt a listt, QList<QStandardItem* > osztly objektumknt (ne
feledjk a QList osztly hasznlatt lehetv tev fejlc felvtelt). grtk, hogy a fejlcrl is
lesz sz, ht tessk: a tagfggvny utols sora vgzi a vzszintes fejlc belltst. Fggleges
fejlcet nem adunk meg, gy a tableView objektum majd szl egyet.
A ksz tagfggvny hvst helyezzk el az osztly konstruktornak trzsben, a modellt
pldnyost sor al.
Egyelre elvgeztnk mindent, amit ebben az osztlyban szerettnk volna, gyhogy
visszaslattyoghatunk a fablakba, ahol pldnyosthatjuk az osztlyt, termszetesen a heap-
en. Az elz fejezet vge fel mr lttuk, hogy a fablak konstruktora ersen elhzott, esetleg
rdemes volna taln a feladatait kln tagfggvnyben megfogalmazni aki szmra ez javtja
a program ttekinthetsgt, annak mindenkpp.
void MainWindow::preparationAtStartUp()
{
modelManager = new ModelManager(this);
ui>tableView>setModel(modelManager>toDoListModel);
}

A tagfggvny utols sorban megadjuk a tableView objektum modelljt. A fablak


konstruktorbl hvjuk ezt a fggvnyt.

73
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

Az alkalmazs futtatst kveten ki is


prblhatjuk a nzetet: mris tudunk bele
rni.
Ha gy rezzk, hogy a fggleges
fejlc a szmozs nem olyan fontos,
akkor a felletszerkesztben kapcsoljuk
ki: keressk meg a Property Editor-ban a
verticalHeaderVisible tulajdonsgot,
s vegyk ki mellle a pipt.
Aki elveszett volna, termszetesen megint
megtallja a forrsok jelenlegi llapott a
knyv webhelyn. 21. bra: Modellt kapott a nzetnk

10.6. Knyelem mindenekfelett


A szp j ToDoList most pp nem is annyira szp, mert a modell nem tlti ki a nzetet, sem
hosszban, sem szltben. A hosszbanrl igazn nem tehetnk: kevs benne a feladat, azaz a
sor. A szltvel viszont kne kezdeni valamit, mert oszlopbl tbb sosem lesz mr, ezt a kettt
kell beosztani. Szerencsre ez nem problma, egy egsz utastssal megoldhat persze a Qt-tl
nem is vrtunk mst, ugye? Akkor ez most a modell, vagy a nzet belltsa lesz? Ne feledjk,
a modell azt sem tudja, hogy neki van nzete, arrl meg aztn pp fogalma nincs, hogy a
nzet miknt jelenti meg az adatokat. E logika alapjn a QTableView osztly tagfggvnyei
kztt kell sztnzni. Meg is talljuk, amit kerestnk, s a nagy keresgets vgn a fablak
preparationAtStartUp() tagfggvnynek trzsben utols sorknt elhelyezzk a

ui>tableView>horizontalHeader()>setSectionResizeMode
(QHeaderView::Stretch);

sort. A kt kettspont meg a krlttk lv kt sz ismt egy enum, azaz nevestett konstans,
aminek rtke valjban 1. Az ilyen enum-ok lehetsges rtkeinek listjt legokosabb, ha a
kettspont eltti osztly dokumentcijban keressk meg.
Ha mr ilyen szpen kinyjtottuk a fejlcet, tegyk ki a hrcsgt kicsit a billentyzetre,
mertsnk inspircit abbl, ahogy a lbacskival tapodva szebbnl szebb karakterlncokat
jelent meg a kpernyn.
Mikor kellen inspirldtunk, megvilgosodunk: nincs j feladat gomb. Persze van kt
msik, amivel vgl is majd, ha mr leprogramoztuk lehet j feladatot felvenni, de a
gombok nyomkodsa tnkretenn az inspircit, s ugye nem mindenkinek van hrcsge.
Mekkora j volna, ha a feladat felvitelvel automatikusan kapnnk egy j sort.
j sort ugye a modellben kell elhelyezni, de ht honnan jvnk r, hogy mikor kell odatenni
a sort? Ez valami signal lesz, gyhogy lapozzuk fel a QStandardItemModel signal-jait.
Hogyaszongya: itemChanged(QStandardItem*), s ennyi. Ha megnzzk az sosztlytl
rklt signal-okat, akkor sem javul a helyzet. Szval itt vagyunk, egy rva signal-lal,
amibl ugye a megvltozott elem mutatja derl ki. Neknk meg mondjuk nem volna pp
htrnyunkra, ha tudnnk, hogy hnyadik sorban vltozott meg az elem, mert ugye, ha nem az
utolsban, akkor arrl van sz, hogy a felhasznl valamelyik rgebbi feladaton urambocs
kategrin vltoztatott, s akkor nem kell res sort biggyesztennk a modell vgre.

74
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

Kicsit felderl az arcunk, ha megnzzk, hogy mit lehet kezdeni a visszakapott mutatval.
A QStandardItem osztly dokumentcijban van row() s column() tagfggvny,
azaz a modell elemei tudjk magukrl, hogy melyik sorban s oszlopban vannak. Ht
akkor szret! Oldalogjunk t a ModelManager osztlyunkba s valstsuk meg az
itemChanged(QStandardItem*) signal-t elkap privt slot-ot:

void ModelManager::modelItemChanged(QStandardItem *changedItem)


{
if((changedItem>row() == toDoListModel>rowCount()-1) &&
(changedItem>column() == 0) && (changedItem>text() != "")){
QList<QStandardItem* > newRow;
toDoListModel>appendRow(newRow);
}
}

Azaz amennyiben az utols sorban vltozott az elem, s az els oszlopban vltozott az elem
(mert ha a drga felhasznl a kategria megadsval kezden, akkor nem adunk neki j sort),
s a vltozs vgeredmnye nem res teend (mert esetleg meggondolta magt, s mgsem
akar j sort), akkor kap j sort a modell. A ModelManager osztly konstruktorban adjuk ki a
megfelel connect utastst:
connect(toDoListModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(modelItemChanged(QStandardItem*)));

s mr hasznlhatjuk is alkalmazsunk legjabb kpessgt. Gondoljuk csak t, mi is


trtnik: a modell szl a ModellManager osztly objektumnak, hogy megvltozott az egyik
elem, mire az megkri a modellt, hogy ugyan vegye mr fl utols sorknt a frissen ellltott,
egybirnt res elemlistt. A grafikus rsz, azaz a nzet, az egszbl annyit vesz szre, hogy
valamilyen furcsa okbl hirtelen eggyel tbb sor lett, amit szolgaian meg is jelent.
A forrsok jelenlegi llapota megint csak letlthet a knyv webhelyrl, de mostantl ezt
nem ismtelgetjk tbb, nem baj?

10.7. Bevetjk a QItemSelectionModell osztlyt


Itt az ideje megvalstani a jobb oldali gombsorhoz, pontosabban a gombok clicked() signal-
jaihoz rendelt slot-okat. tgondolva a feladatot, elfilzgatunk azon, hogy a kijellt sor trlsrl
van sz, ugyebr. De honnan a vizesvdrbl derl ki, hogy melyik sor van kijellve? Tudja
a modell? Dehogy, az mg azt sem tudja hogy ltszanak az adatai. Tudja a nzet? Ht, akr
tudhatn is, de nem ilyen egyszer a helyzet.
A Qt szerint mindezt gy rdemes megoldani, hogy kln QItemSelectionModell
(azaz kijellsmodell) osztly objektumban tartjuk nyilvn a kijellseket. Ez az objektum
annyira szorosan ktdik a modellhez, hogy mr a konstruktor paramtereknt meg kell
adnunk a forrsmodellt. Ebben az objektumban belltjuk, hogy mi minden legyen kijellve.
A megjelents onnantl zajlik, hogy a nzetnek is szltunk a kijellsmodell ltezsrl. Azaz
elvileg megint van r mdunk, hogy egy modellnek egyszerre tbbfle kijellse is ljen, s a
nzetben ezek kzl azt hasznljuk, amelyiket pp alkalmasnak tartjuk. Mi most megelgsznk
egy kijellsmodellel, amit a ModellManager osztly belsejben keltnk letre. Maghoz a
modellhez hasonlan a kijellsmodell is a heap-re kltzik, s nyilvnoss tesszk a mutatjt,
mert a fablakon bell ltez nzetnek kell tudni belle olvasnia st, rnia is bele, mert ugye

75
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

ott fogjuk majd kijellgetni a szerkeszteni, trlni, mozgatni vgyott sorunkat.


A modelmanager.h fejlcei kztt helyezzk el a <QItemSelectionModel>-t, hozzuk ltre
a mutatt *toDoSelectionModel nven, majd a ModelManager osztly konstruktorban a
modellt pldnyost sort kveten adjuk ki a
toDoSelectionModel = new QItemSelectionModel(toDoListModel, this);

utastst.
A fablak preparationAtStartUp() tagfggvnynek trzsben negyedik sorknt
mutassuk be a kijellsmodellt a nzetnek:
ui>tableView>setSelectionModel(modelManager>toDoSelectionModel);

Ha most lefordtjuk s futtatjuk a programunkat, akkor az g egy vilgon semmilyen


vltozst nem ltunk benne, de mi mr tudjuk, hogy a modelManager objektum belsejben
is minden pillanatban ki tudjuk derteni, hogy a nzetnek pp melyik cellja van kijellve.
S ha mr itt tartunk, a Property Editor-ban a tableView tulajdonsgai kztt lltsuk be
a kijells mdjt cellnkntire, azaz a selectionMode tulajdonsg rtkl adjunk meg
singleSelection-t. Kacrkodhatunk a gondolattal, hogy a selectionBehavior (kijells
viselkedse) tulajdonsg rtkt selectRows-ra (soronknti kijells) lltjuk, de az a helyzet,
hogy a kijellst mi arra is hasznlni akarjuk, hogy a felhasznlnak megmutassuk, melyik
cellba fog rni, ha gpelni kezd. gyhogy inkbb hagyjuk selectItems-en (elemenknti
kijells). Utolsknt mg annyit tegynk meg, hogy a tabKeyNavigation melll is kivesszk
a pipt, s gy lehetv tesszk, hogy a tab-sorrend rvnyesljn, s a tabultor nyomkodsval
is el lehessen jutni a nyomgombokig.
Aprop, nyomgombok! Ht pp azrt kezdtnk bele a kijellsmodell hasznlatba, hogy a
nyomgombok vgre mkdni tudjanak. Mg mieltt tnyleg hozzfognnk, a ModelManager
osztly konstruktorban a modell inicializlst vgz emptyModel(); sor alatt helyezzk el,
hogy
toDoSelectionModel>setCurrentIndex(toDoListModel>index(0,0),QItem
SelectionModel::SelectCurrent);

Azaz jelenlegi (nem kijellt, csak


kivlasztott) indexknt lltsuk be a 0.
sor 0. oszlopnak elemt, de gy, hogy
a kivlasztssal prhuzamosan azrt
kapjon kijellst is. A kt kvetkez bra
sszehasonltsval kpet kapunk a dolog
rtelmrl.
gy azrt jobban hvogat, hogy rj mr
ide valamit, lcci-lcci-lcci!

22. bra: A program indulsa az els cella k


ijellse nlkl

76
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

Akkor vgre a gombok. Az ltaluk


emittlt signal-okat kzvetlenl a
modelManager objektum slot-jaihoz ktjk
majd, gy ezeket a slot-okat publikusnak
kell deklarlnunk. Lssuk a feladat trlst
vgz slot-ot:

23. bra: A program indulsa az els cella


kijellsvel
void ModelManager::deleteSelectedTask()
{
int row = toDoSelectionModel>currentIndex().row();
if(row >= 0){
toDoListModel>removeRow(row);
if(row >= toDoListModel>rowCount()-1)
toDoSelectionModel>setCurrentIndex(toDoListModel->index
(row-1,0),QItemSelectionModel::SelectCurrent);
else
toDoSelectionModel>setCurrentIndex(toDoListModel>index
(row,0),QItemSelectionModel::SelectCurrent);
}
}

A kijellsmodellbl megtudjuk, hogy melyik sor van kijellve. Azrt kell vizsglnunk, hogy
a sor szma legalbb nulla-e, mert kis gyeskedssel pillanatok alatt -1 rtkv tudjuk tenni
majd, pusztn a gomb nyomogatsval. Az esetsztvlaszts els lehetsge akkor fut majd le,
ha az utols sort trltk, minden ms esetben a msodik, az else utni lehetsg valsul meg.
Mindkt esetben kijelljk az aktulis sort, hogy a felhasznlnak nagyon nyilvnval legyen,
hova gpel majd.
Esznkbe jut a connect utasts is, amit a fablak preparationAtStartUp()
tagfggvnynek trzsben helyeznk el, clszeren olyan rszen, ahol mr ltezik mindkt
objektum: a nyomgomb is, meg a modelManager is.
connect(ui>buttonDelete, SIGNAL(clicked()),
modelManager, SLOT(deleteSelectedTask()));

A trls visszavonst lehetv tev gombot szemrmesen tugorjuk, s az j sorokat beszr


gombok slot-jainak megvalstsval folytatjuk.

77
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

void ModelManager::newTaskAfterSelected()
{
int row = toDoSelectionModel>currentIndex().row();
QList<QStandardItem* > newRow;
toDoListModel>insertRow(row+1, newRow);
toDoSelectionModel>clearSelection();
toDoSelectionModel>setCurrentIndex(toDoListModel>index(row+1,0),
QItemSelectionModel::SelectCurrent);
}

void ModelManager::newTaskBeforeSelected()
{
int row = toDoSelectionModel>currentIndex().row();
QList<QStandardItem* > newRow;
toDoListModel>insertRow(row, newRow);
toDoSelectionModel>clearSelection();
toDoSelectionModel>setCurrentIndex(toDoListModel>index(row,0),
QItemSelectionModel::SelectCurrent);
}

A kt slot nem is gondoln az ember igen hasonlra sikerlt. Mindssze kt helyen


klnbznek, mgpedig a tekintetben, hogy az aktulis sor helyre szrnak-e be sort, a tbbit
eggyel elbbre lptetve (ezt a felhasznl majd gy ltja, hogy az aktulis sor el kerlt az
j sor), vagy az aktulis sort kveten. Az insertRow() tagfggvny nagyon hasonlan
mkdik a mr ismert appendRow()-hoz, attl a nansznyi klnbsgtl eltekintve, hogy a
beszrskor meg kell mondanunk azt is, hogy hova krjk a sort, mg a hozzfzs esetben
erre nyilvnval okbl nincs szksg. A kijells elvgzse eltt trljk az rvnyben lvt,
hiszen nem akarjuk hogy a rgi s az j kijells is ltszdjk. A trls gomb esetben erre azrt
nem volt szksg, mert az aktulis sor, s vele a kijells is trldtt.
Elhelyezzk a megfelel connect utastsokat is:
connect(ui>buttonNewAfter, SIGNAL(clicked()),
modelManager, SLOT(newTaskAfterSelected()));
connect(ui>buttonNewBefore, SIGNAL(clicked()),
modelManager, SLOT(newTaskBeforeSelected()));

Akkor a kvetkez kt gomb, megint egyben trgyalva:


void ModelManager::moveUpSelectedTask()
{
int row = toDoSelectionModel>currentIndex().row();
if(row > 0){
toDoListModel>insertRow(row-1,toDoListModel>takeRow(row));
toDoSelectionModel>setCurrentIndex(toDoListModel>index(row-1,0),
QItemSelectionModel::SelectCurrent);
}
}

78
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

void ModelManager::moveDownSelectedTask()
{
int row = toDoSelectionModel>currentIndex().row();
if(row < toDoListModel>rowCount()-1){
toDoListModel>insertRow(row+1,toDoListModel>takeRow(row));
toDoSelectionModel>setCurrentIndex(toDoListModel>index(row+1,0),
QItemSelectionModel::SelectCurrent);
}
}

A kezdet most is azonos: megtudjuk, hogy hanyadik sorban kricl a felhasznl. A


felttelvizsglat mr eltr. Amikor felfel vinnnk valamit, akkor az a krds, hogy van-e mg
korbbi sor, s korbbi sor akkor van, ha a mostani sor sorszma nullnl nagyobb. Amikor
lefel vinnnk a sort, akkor a krds gy szl, hogy nem vagyunk-e mris mindennek a
vgn. Ha a vizsglt felttel teljesl, akkor belekezdnk a sor mozgatsba. A takeRow() a
takeItem() tagfggvny pr fejezettel korbbi elfordulsa miatt elvileg ismers akkor
zavarnak talltuk a nevet, de mostanra megbartkoztunk vele. lesz az a kartrs, aki kiveszi
a sort a helyrl, visszaadja a mutatjt, de egybknt a sort magt nem kukzza le. A mltkor,
a takeItem() esetben ezen bosszankodtunk, mert pp nagy volt a a kukzhatnkunk. Most
azonban okosan hasznljuk a dolgot, s a kivett sort mg azon melegben vissza is tesszk az j
helyre. Utols mozdulatknt a kijellsmodellt is megfelelen mdostjuk.
Ktelez fordulatknt lssuk a megfelel connect utastsokat (ugye nem feledtk, hogy a
fablak fjljba kerlnek?):
connect(ui>buttonUpSelected, SIGNAL(clicked()),
modelManager, SLOT(moveUpSelectedTask()));
connect(ui>buttonDownSelected, SIGNAL(clicked()),
modelManager, SLOT(moveDownSelectedTask()));

Annak rmre, hogy ilyen szpen elkszltnk a gombok thatodval, visszatrhetnk a


kihagyott undo last delete, azaz az utols trls visszavonsa felirathoz.
Elrebocstannk, illetleg flhvjuk a figyelmet arra, hogy nem utols mveletet rtunk,
hanem utols trlst. A visszavons teljes rtk megvalstshoz nagyon sokfle dolgot
kellene feljegyezni, pldul azt, hogy hova kerlt be egy j sor, hnyszor mozgattunk felfel-
lefel egy ksz sort, radsul mg azt is, hogy mirl rtuk t a cellt olyanra, amilyen most. Erre
a feladatra meg egy strandknyvben nem szoktak vllalkozni.
Mi csak annyit tesznk, hogy ksztnk egy vermet (angolul stack), ha tetszik, LIFO-t. A
LIFO annyit tesz, hogy Last In, First Out, azaz ami utoljra ment be, az jn ki elsnek. Vermet
az ember nem sval-lapttal kszt a Qt-ban (kis hjn olyan terletre bukkantunk, ahol mg
a hrcsg is lepiplt volna bennnket), hanem pldnyost magnak a QStack<T> osztlybl.
A T betbl persze megint tudjuk, hogy ez egy sablonosztly, azaz meg kell mondanunk,
hogy miket, milyen tpus objektumokat trolunk a belle ksztett pldnyban. Mi a magunk
blcsessgben gy dntnk, hogy ha mr gyis adatprokrl, kt QString-rl van sz, akkor
mirt ne hasznlnnk a QPair osztlyt? (Trolhatnnk a kivett sorok mutatit, de akkor
megint QSharedPointer-ekkel kne szszlni, ahhoz meg mr ks van.)
gyhogy a modellmanager.h llomny elejre vegyk fel a <QStack> s a <QPair> fejlcet,
majd privt objektumknt deklarljuk a

79
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...

QStack<QPair> undo;

nevt.
Az undo objektum tltgetst a trlsrt felels slot-ban vgezzk majd, mgpedig gy, hogy
a sor trlst megelzen (teht a toDoListModel->removeRow(row); sor eltt) kiadjuk az
undo.push(QPair<QString, QString>(toDoListModel>index(row,0).
data().toString(), toDoListModel->index(row,1).data().toString()));

utastst.
Szp hossz, igaz?
A visszavons megvalstsa nem jelent mst, mint a verem legfels elemnek kivtelt, s
a benne lv kt karakterlnc alapjn kpzett objektum elhelyezst a modellben. Minthogy
nem troljuk, honnan vettk ki nem is beszlve arrl, hogy azta ssze-vissza mozgathattuk,
szerkeszthettk az elemeinket, s jakat is szrhattunk be , a modell legvgre biggyesztjk a
rgi-j sort.
void ModelManager::undoLastDelete()
{
if(!undo.isEmpty()){
QList<QStandardItem* > newRow;
toDoListModel>appendRow(newRow);
toDoListModel>setData(toDoListModel>index(toDoListModel
>rowCount()-1, 0), undo.top().first);
toDoListModel>setData(toDoListModel>index(toDoListModel
>rowCount()-1, 1), undo.pop().second);
}
}

A setData() tagfggvny els paramtere azt mutatja meg, hogy hol kell belltania a
msodik paramterben megadott adatot. A msodik paramterben lthat QStack::top()
tagfggvny a verem legfels elemre mutat hivatkozst ad vissza, s nem tvoltja el az elemet
a verembl. A legfels elem trtnetesen egy QPair<QString, QString> osztly objektum
(igen, a tbbi is), aminek hvhat a first() tagfggvnye. A msodik setData()-hvs sorn
mr ms tagfggvnyt hasznljuk a QStack osztlynak: egyszer top(), msszor pop(). A
pop() igen hasonl a top()-hoz, de ki is veszi az elemet a verembl. Ha a slot mkdst
megrtettk, helyezzk el a megfelel connect utastst is:
connect(ui>buttonUndo, SIGNAL(clicked()),
modelManager, SLOT(undoLastDelete()));

Futtassuk a programunkat, s hogy az a sistergs-mennydrgs! A modell itemChanged


signal-jhoz kttt slot most is teszi a dolgt, s kpes beszrogatni neknk j sorokat.
Nem lehetne megmondani neki, hogy kicsit hagyja abba? De. Ltezik a connect prja, a
disconnect, pp csak mg sosem hasznltuk, de ht ugye mindig van egy els alkalom.
Esetnkben az els alkalom nem az utols, mert mondjuk a fjl betltsnl hasonl
problmval kerlnk majd szembe. gy aztn runk is egy j kis tagfggvnyt, ami a
paramtere rtktl fggen ki-be kapcsolgatja a problmt okoz signal s slot kapcsolatot:

80
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv

void ModelManager::connectItemChangedSignalToSlot(bool needed)


{
if(needed)
connect(toDoListModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(modelItemChanged(QStandardItem*)));
else
disconnect(toDoListModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(modelItemChanged(QStandardItem*)));
}

A fenti setData()-sorok eltt kikapcsoljuk:


connectItemChangedSignalToSlot(false);

utnuk meg bekapcsoljuk:


connectItemChangedSignalToSlot(true);

A trls visszavonsa remekl mkdik. Volna mg rtelme annak is, hogy a szp j ki-
be kapcsolgat tagfggvnynek mr a ModelManager osztly konstruktorban is hasznt
vegyk, nevezetesen az ottani connect utastst cserlhetjk le e tagfggvny hvsra. A
hrcsg hrom centi rpa fejben elrulja, hogy melyik paramtert kell hvskor megadnunk.

81
Qt strandknyv 11. Adatments s -visszatlts...

11. Adatments s visszatlts modell


hasznlatakor

Az adatments s az adatok visszatltse els rnzsre nem klnbzik alapveten attl a


vltozattl, amit a kilencedik fejezet vgig hasznltunk. Miknt jrtunk el addig? Volt egy
listnk, ami task osztly objektumokat pontosabban task osztly objektumokra mutat
mutatkat mutatit trolt. Ezt a listt nylazta vgig a fileOperator objektum mentskor,
illetve ezt tlttte fel j objektumokkal a mentett fjl megnyitsakor. Lnyegben akkor is
volt egy adatszerkezetnk, ami trolta az adatainkat s most is van egy: a modell. A kt eset
kztt az a nagy klnbsg, hogy ezttal nem a mi dolgunk az adatszerkezet ellltsa, csak
annyi a teendnk, hogy a mr ksz modellben engednnk kell trni a FileOperator osztlyt.
Szerencsre a modell, illetve a r mutat mutat, br a modelManager objektumunkban l,
publikus, azaz a fileOperator objektum tud majd turklni benne.
gy aztn kevesebb bajunk van az adataink integritsnak megrzsvel, cserbe meg tudunk
valstani pr knyelmi funkcit.

11.1. A Belltsok ablak


Az utols mentett fjlt persze ezttal is szeretnnk majd visszatlteni, meg j volna
megvalstani az automatikus mentst ismt, azaz mindenkpp szksgnk lesz a belltsok
trolsra. Ha mg emlksznk, annak rdekben, hogy a belltsokat kezel, QSettings
osztly objektumokat a programunk klnfle terletein knnyen tudjuk pldnyostani,
rdemes volt pr adatot megadni az alkalmazsunkrl. Kezdjk ht azzal a munkt, hogy ezt
megtesszk. Els dolgunk a <QCoreApplication> fejlc felvtele a fablakban. Ezt kvetheti
a kvetkez hrom sor elhelyezse a preparationAtStartUp() tagfggvny trzsnek elejn:
QCoreApplication::setOrganizationName("Sufni & Co.");
QCoreApplication::setOrganizationDomain("example.com");
QCoreApplication::setApplicationName("ToDoList");

Ezt kveten alaktsuk ki a SettingsWindow osztlyt. Az osztly sosztlya a QDialog.


A felhasznli felletet alaktsuk ilyenre:
Az automatikus mentsen fell
mg egy belltst trolunk: kvnsg
esetn mgiscsak megjelenthetv
tesszk a fablak tableView elemnek
sorszmozst.
Az elemek nevei (alighanem kitalljuk,
hogy melyik melyik):
autoSaveCheckBox
zz

autoSaveSpinBox
zz

numberedRowsCheckBox
zz

buttonOk
zz
24. bra: A Settings ablak
buttonCancel
zz

82
11. Adatments s -visszatlts... Qt strandknyv

Az ablakot modlis ablakknt jelentjk majd meg, s ha a felhasznl elfogadta a


mdostsokat magyarul az OK-gombot nyomta meg , akkor trolunk is mindent a
QSettings osztly hasznlatval. Az ablak modlis mivolta miatt a fileOperator objektum
rr a mr mentett vltoztatsokat hasznlni, csak szlnunk kell neki, hogy valami vltozott. A
fablaknak meg a sorszmozs miatt kell majd szlnunk.
A settingswindow.h fjlban deklarlunk egy settings nev objektumot a
hasznlatt lehetv tev <QSettings> fejlc felvtelt se felejtsk el megejteni. Az osztly
konstruktorban a kvetkez hrom sorral az eltrolt belltsoknak megfelelen lltjuk be a
belltsok ablak elemeit:
ui>autoSaveCheckBox>setChecked(settings.value("Files/
AutoSaveEnabled", "false").toBool());
ui>autoSaveSpinBox>setValue(settings.value("Files/
AutoSaveMinutes", 0).toInt());
ui>numberedRowsCheckBox>setChecked(settings.value("UI/
NumberedRows", "false").toBool());

Ugye emlksznk mg, hogy a QSettings::value() tagfggvny a msodik, elhagyhat


paramtervel tette lehetv, hogy olyankor is megadjunk vele rtket, ha mg nincs trolva a
kiolvasni vgyott bellts?
Szksg lesz mg egy privt slot-ra, amelyik az autoSaveSpinBox-ot kapcsolgatja ki-be:
void SettingsWindow::on_autoSaveCheckBox_stateChanged(int arg1)
{
if(arg1 == Qt::Unchecked)
ui>autoSaveSpinBox>setEnabled(false);
else if(arg1 == Qt::Checked)
ui>autoSaveSpinBox>setEnabled(true);
}

Vgl egy utolsra, amelyik az OK gomb lenyomsa esetn lendl akciba:


void SettingsWindow::on_buttonOk_clicked()
{
settings>setValue("Files/AutoSaveEnabled", ui>autoSaveCheckBox>
isChecked());
settings>setValue("Files/AutoSaveMinutes", ui>autoSaveSpinBox>
value());
settings>setValue("UI/NumberedRows", ui>numberedRowsCheckBox>
isChecked());
this>done(QDialog::Accepted);
}

Figyeljk meg, hogy a done() tagfggvny paramtereknt adjuk tudtra az ablakot


megnyit objektumnak esetnkben a fablaknak , hogy vgl is a felhasznl elfogadta a
belltsokat.
Az utols kt slot a neve alapjn automatikusan ktve lesz a megfelel signal-hoz, vagyis ezt a
gondot elfelejthetjk. A SettingsWindow osztly elkszlt.
Itt van ht az ideje, hogy az elz fejezetben kialaktott MainWindow::on_
actionSettings_triggered() slot trzst is megrjuk, de eltte mg a fablak valamelyik
fjljban vegyk hasznlatba a settingswindow.h fejlcet. A slot trzse nem bonyolult, cserbe
viszont rm egyszer:

83
Qt strandknyv 11. Adatments s -visszatlts...

SettingsWindow settingsWindow;
if(settingsWindow.exec() == QDialog::Accepted)
restoreApplicationState();

Azaz pldnyostunk magunknak egy belltsok-ablakot, mghozz a stack-en,


azaz mihelyt kilpnk belle s vgigfut a slot, meg is sznik ltezni az ablak. Ha a
felhasznl a belltsok elfogadsval zrta be az ablakot, akkor rvnyre juttatjuk
ket a kiss (szval: igencsak) nagykpen elnevezett restoreApplicationState(),
azaz alkalmazsllapotnakVisszalltsa privt tagfggvnnyel, amit egybknt a
preparationAtStartUp() tagfggvny trzsnek vgn is hvni kell. A nv nyilvn
nem volna ennyire nagykp, ha belltsok szzait trolnnk, de ugye eddig pont hrom
belltsunk van, s ezek kzl ebben az osztlyban azaz a fablakban csak egy rdekes.
No, sebaj. Maga a tagfggvny a kvetkez formt lti:
void MainWindow::restoreApplicationState()
{
QSettings settings;
if(!settings.value("UI/NumberedRows", "false").toBool())
ui>tableView>verticalHeader()>hide();
else
ui>tableView>verticalHeader()>show();
fileOperator>adjustAutoSave();
}

A fileOperator objektum mg sehol nincs persze, st, az osztlya sem, azaz, ha ki


szeretnnk prblni, hogy eddig minden rendben-e, akkor a tagfggvny trzsnek utols sort
alaktsuk megjegyzss.

11.2. A FileOperator osztly reinkarncija


Vgre hozzkezdnk ahhoz, ami a fejezet valdi tmja. Adjunk a projekthez j osztly,
FileOperator nven, s ezttal ne a QObject, hanem a QWidget legyen az sosztly, mert,
ahogy arra bizonyra emlksznk, a fjl-vlaszt ablakok snek mindenkpp QWidget
osztlynak kell lennie. Az osztlyt a fablakbl fogjuk pldnyostani, s a konstruktornak
egyik paramtereknt adjuk t a modellnk mutatjt. Azaz az osztly konstruktora ilyesmi
lesz:
FileOperator::FileOperator(QStandardItemModel *toDoListModel, QWidget
*parent) :
QWidget(parent)
{
model = toDoListModel;
timer = new QTimer(this);
}

A timer objektum termszetesen az automatikus ments miatt kell majd. Aprop,


automatikus ments: rjuk mr meg gyorsan azt a frnya FileOperator::adjustAutoSave()
fggvnyt, aminek a hvst az imnt szgyenszemre megjegyzss kellett alaktanunk. A
tagfggvnynek nem lesz ms dolga, mint a belltsok beolvassa, s a timer objektum
megfelel felparamterezse. Az automatikus ments kivitelezse egy msik tagfggvny
feladata lesz majd, ami radsul nem csak tagfggvny, de slot is lesz, gy hozz tudjuk ktni a
timer objektum ki(el?)slshez. me a tagfggvnynk:

84
11. Adatments s -visszatlts... Qt strandknyv

void FileOperator::adjustAutoSave()
{
if(settings.value("Files/AutoSaveEnabled", "false").toBool())
timer>start(settings.value("Files/AutoSaveMinutes").toInt()
*60000);
else
timer>stop();
}

Vgezzk el a FileOperator osztly pldnyostst a fablakban. Olyan helyen kell


megejtennk, ahol mr van modell, de mg a restoreApplicationState() tagfggvny
hvsa eltt, mert klnben nem indul be az automatikus ments.
Ha eddig minden klappol, nekikezdnk a mentst vgz tagfggvny elksztsnek. Ezttal
a fjlnevet paramterknt kapja meg. A fjl megnyitsa s hasznlatba vtele a mr megismert
mdon trtnik, s a modell mentse kzben figyelnk arra, hogy res teendj feladatot
azaz az olyan sorokat, amelyek els oszlopa res nem mentnk. Sikeres ments esetn a
belltsokban troljuk a fjlnevet. A hibazeneteket ezttal kln tagfggvnyben helyezzk
el, hiszen mentskor s megnyitskor nagyon hasonlan magyarztuk el a felhasznlnak,
hogy baj trtnt. A hibajelzst vgz tagfggvny paramterknt kapja meg, hogy mikor is
(mentskor vagy megnyitskor) trtnt baleset. A mentst vgz privt tagfggvny vgl
ilyenre sikeredett:
bool FileOperator::performFileSaveOperation(QString fileName)
{
QFile file(fileName);
bool success = false;
if (file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
QTextStream out(&file);
for(int i = 0; i < model>rowCount(); i++){
QString job = model>index(i,0).data().toString().
simplified();
if(!job.isEmpty())
out << job << "|" << model>index(i,1).data().
toString() << endl;
}
if(!file.error())
success = true;
}
if(success){
settings.setValue("Files/LastFileName", fileName);
}
else{
fileOperationErrorMessage(tr("Save"));
}
return success; //can be "false" too
}

Gyorsan megrjuk a fileOperationErrorMessage() privt tagfggvnyt is:

85
Qt strandknyv 11. Adatments s -visszatlts...

oid FileOperator::fileOperationErrorMessage(QString operation)


{
QMessageBox mb;
mb.setIcon(QMessageBox::Critical);
mb.setText(operation + " " + tr("not succeeded."));
mb.setInformativeText(tr("Try and do something wise."));
mb.exec();
}

Az elz alkalommal gy gondolkodtunk, hogy a felhasznl alighanem az elz fjl mell


menten az jat is, ha meg nem volt elz fjl, akkor valahova a sajt ckmkja kz. Radsul
azt is feltteleztk, hogy hasonl gondolatmenet fut t az agyn a mentett fjl megnyitsakor
is: htha az elz melll nyitna meg msikat, mert mondjuk az imnt mellkattintott. gy aztn
mindkt alkalommal ilyen megfontolsbl kiindulva lltottuk el azt az elrsi tvonalat,
amelyet felhasznlva megnyitottuk a fjlvlaszt prbeszdablakot. Ha viszont ktszer is kellett,
ugye van rtelme kln tagfggvnybe pakolni a fejtegetst? A nagyrabecslt Olvas gesztust
blintsknt rtelmezzk, s mr itt is az alaprtelmezett elrsi tvonalat javasl privt
tagfggvny:
QString FileOperator::suggestDefaultPath()
{
QString path = QDir::homePath();
QString lastFileName = settings.value("Files/LastFileName", "").
toString();
if(lastFileName != ""){
QFileInfo info(lastFileName);
path = info.absolutePath();
}
return path;
}

Minden kszen ll ahhoz, hogy megrjuk azt a tagfggvnyt is, amelyik elbb beszerzi a
szksges fjlnevet, s aztn ennek ismeretben vgezteti el a mentst. A mltkor jl bevlt
mdszer szerint, ha a felhasznl nem kegyeskedett megadni a fjl kiterjesztst, akkor majd mi
megadjuk. me:
bool FileOperator::fileSave()
{
QString newFileName = QFileDialog::getSaveFileName(
this,
tr("Save as..."),
suggestDefaultPath(),
tr("ToDoList files (*.tdolst);;text files (*.txt);;all
files (*)")
);
if(!newFileName.isEmpty()){
QFileInfo info(newFileName);
QString ext = info.suffix();
if(ext != "tdolst" && ext != "txt")
newFileName += ".tdolst";
return performFileSaveOperation(newFileName);
}
return false;
}

86
11. Adatments s -visszatlts... Qt strandknyv

Trjk kicsit a fejnket. Ha a felhasznl Ctrl+S billentykombincit nyom,


s van fjlnv (ki tudjuk olvasni a belltsokbl), akkor csukls nlkl hvjuk a
performFileSaveOpration() tagfggvnyt. Ha nincs fjln, akkor a fileSave()
tagfggvnyt hvjuk, megtudjuk a fjlnevet s ennek ismeretben megint csak a
performFileSaveOpration()-ra lcsljk a melt. Ha a felhasznl a Save as... menpontot
vlasztan, akkor tekintet nlkl arra, hogy van-e fjlnevnk vagy nincs, mindenkpp jat
kell krnnk, hiszen a Save as... menpontnak pont ez a lnyege. Vgiggondoltunk minden
lehetsget? Taln. Akkor megrhatjuk a kt QAction osztly objektum (mrmint a Save s a
Save as...) slot-jt. Hol volna rdemes? A fablakban egyszerbb, a FileOperator osztlyban
elegnsabb. Mi elegnsak lesznk, s publikus slot-knt a FileOperator-ban helyezzk el a
kt kvetkez szsszenetet. A fggvnyek elnevezse nagyon hasonl ahhoz, amit automatikus
ltrehozsukkal kapnnk, de szndkosan nem teljesen ugyanolyan, ugyanis a moc (a Meta
Object Compiler) azonnal hbrgne, hogy ltja , hogy itt a slot, de hol a signal? (Mi persze
tudjuk, hogy egy msik osztlyban, de a moc-nak nincs m annyi esze, hogy megkrdezze
tlnk.) s akkor most mihez kssn, meg micsodt? Mi meg nem brjuk, ha valaki hbrg, gy
inkbb picit vltoztatunk a neveken.
void FileOperator::onActionSaveTriggered()
{
QString fileName = settings.value("Files/LastFileName", "").
toString();
if(!fileName.isEmpty())
performFileSaveOperation(fileName);
else
fileSave();
}

void FileOperator::onActionSaveAsTriggered()
{
fileSave();
}

Mr csak az a dolgunk velk, hogy a fablakban kiadjuk a megfelel connect utastsokat:


connect(ui>actionSave, SIGNAL(triggered()),
fileOperator, SLOT(onActionSaveTriggered()));
connect(ui>actionSave_as, SIGNAL(triggered()),
fileOperator, SLOT(onActionSaveAsTriggered()));

Termszetesen nincs sok teteje a dolognak, ha mg a fileOperator pldnyostsa el rjuk


a sorokat, de a pldnyostst kveten azonnal jhetnek. Elgondolkodhatunk azon is, hogy
kzvetlenl a fileSave() tagfggvnyt tesszk slot-t, s azt ktjk a Save as... menhz. Nem
sok ellenrv van ellene, taln az, hogy egyesek szmra a jelenlegi forma ttekinthetbb. Ez
viszont egy igen-igen fontos rv, mindenkit meggyzhet, kivve esetleg azokat, akik szerint
a msik forma ttekinthetbb. A hrcsg mg nem nyilvntotta ki vlemnyt az gyben,
gyhogy a meccs mg nem lefutott.
Megnyits kvetkezik, pedig ez a knyv nem is a sakkrl szl. A megnyitst vgz
tagfggvnyek termszetesen nagyon hasonltanak majd a mentst vgzkhz, s ugyangy
prban jrnak. Itt kvetkezik mind a kett:

87
Qt strandknyv 11. Adatments s -visszatlts...

bool FileOperator::fileOpen()
{
QString newFileName = QFileDialog::getOpenFileName(
this,
tr("Open file..."),
suggestDefaultPath(),
tr("ToDoList files (*.tdolst);;text files(*.txt);;all
files (*)")
);
if(!newFileName.isEmpty())
return performFileOpenOperation(newFileName);
return false;
}

bool FileOperator::performFileOpenOperation(QString fileName)


{
model>clear();
model>setHorizontalHeaderLabels(QStringList() << tr("job") <<
tr("category"));
QFile file(fileName);
bool success = false;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
while (!in.atEnd()){
QStringList sl = in.readLine().split("|");
QStandardItem *item = new QStandardItem(sl.at(0));
model>setItem(model>rowCount(), 0, item);
item = new QStandardItem(sl.at(1));
model>setItem(model>rowCount()-1, 1, item);
}
if(!file.error())
success = true;
}
if(success){
settings.setValue("Files/LastFileName", fileName);
QList<QStandardItem* > newRow;
model>appendRow(newRow);
}
else{
fileOperationErrorMessage(tr("Open"));
}
return success; //can be "false" too
}

A performFileOpenOperation() tagfggvnyt a fileOpen() tagfggvny hvja, s mg


ki? Mert, ha senki, akkor minek kln tagfggvny? Nos, a msik hely, ahonnan hvni fogjk,
a program indtsakori automatikus fjlbetlts lesz. gyhogy van ltjogosultsga a dolognak.
Viszont a fileOpen() tagfggvny hvsa krl van mg egy icuri kis problma.
Alighanem esznkbe tlik mg, hogy a knyelem min ModelManager osztly berkeiben
megrtuk azt a slot-ot, amelyik az utols sor els oszlopban lv elem megvltozsakor azonnal
ltrehoz egy res sort. Hamarosan bajba is keveredtnk vele, olyankor is el-elkerlt egy-egy
res sor, amikor az legkevsb sem szolglta hajainkat. Meg is rtuk a nagy ki-be kapcsolgat
connectItemChangedSignalToSlot() tagfggvnyt, amelyrl mr akkor elrevettettk,

88
11. Adatments s -visszatlts... Qt strandknyv

hogy a fjlbetltskor mg j hasznt vesszk majd. Nos, ez az id mrmint a fjlbetlts, meg


a problma eljvetele itt van. Szeretnnk hvni a tagfggvnyt, de ugye a fileOperator
objektum nem ltja. Knytelenek lesznk szgyenszemre ezt az egy slot-ot a fablakban
megvalstani? Azt mr nem!
gy mdostjuk a FileOperator osztly konstruktort, hogy ne csak a modell, hanem az
egsz modelManager objektum mutatjt vegye t. me:
FileOperator::FileOperator(ModelManager *modelManager, QWidget
*parent) :
QWidget(parent)
{
this>modelManager = modelManager;
model = modelManager>toDoListModel;
timer = new QTimer(this);
}

A trzs els sora azt teszi lehetv, hogy a FileOperator osztlyon bell is
modelManager nven hivatkozhassunk az objektumunkra. Persze t kell alaktanunk
a fileOperator objektumot pldnyost sort is a fablak forrsban, st, a
ModelManager::connectItemChangedSignalToSlot() tagfggvnyt is publikuss kell
tennnk, merthogy eddig privt volt, a szentem. Vgre megrhat a megnyitst vgz slot:
void FileOperator::onActionOpenTriggered()
{
modelManager>connectItemChangedSignalToSlot(false);
fileOpen();
modelManager>connectItemChangedSignalToSlot(true);
}

s, igen, most sem hagyjuk el a connect utastst a fablakbl:


connect(ui>actionOpen, SIGNAL(triggered()),
fileOperator, SLOT(onActionOpenTriggered()));

Akkor ez is megvan. Bueno.

11.3. Utols lehetsg a mentsre a QMessageBox osztly tovbbi


lehetsgei s a Q_PROPERTY makr
Most, amikor az j fjl ha tetszik, j lista kezdshez szksges tagfggvny
megvalstsn kezdnk ggyizni, esznkbe jut, hogy milyen mrges arcot vg majd a
felhasznl, ha csak vletlenl kattint az j ikonra, s trldik az egsz addigi munkja.
Persze mi tudjuk, hogy lesz a hibs, mirt nem lltott be automatikus mentst?! Ami mg
nem is mkdik. Mrmint a bellts mkdik, a ments nem.
Szval, csak szlni kne neki, hogy mentse mr, ami menthet. Persze vaklrmzni sem
akarunk, mert mi a helyzet, ha pp az elz kattintsval mentette? J volna nyilvntartani
valahogy, hogy volt-e vltozs az utols ments ta. Hol is kellene ezt nyilvntartani?
Legyen mondjuk a modelManager objektumnak egy tulajdonsga, legalbb megtanuljuk,
hogy miknt kszt az ember amolyan igazi Qt-os tulajdonsgot24. A tulajdonsg neve az
lesz, hogy toDoListModelChanged, de csak azrt ilyen rviden, mert rezzk, hogy a

24 Olvasnival: http://qt-project.org/doc/qt-5.1/qtcore/properties.html

89
Qt strandknyv 11. Adatments s -visszatlts...

toDoListModelChangedSinceLastSave tlzs lenne.


A modelmanager.h fjlban a Q_OBJECT makr al helyezzk el az albbi sort:
Q_PROPERTY(bool toDoListModelChanged READ toDoListModelChanged
WRITE setToDoListModelChanged NOTIFY toDoListModelChangedChanged)

Ebbl aztn a Qt vilgban tudni lehet, hogy ennek az osztlynak van egy fent emltett nev,
bool tpus tulajdonsga, aminek a kiolvasfggvnye (angolul: getter) is azonos nvvel br, a
belltfggvnynek (angolul: setter) neve a WRITE sz utn van megadva, s amennyiben a
tulajdonsg megvltozna, az osztlybl pldnyostott objektumok a NOTIFY sz utn megadott
signal-t emittljk (minden kedves Olvastl elnzst krnk a nmileg zavarra sikeredett
signal nv miatt).
Persze a makr nem a teljes igazsgot hirdeti, amennyiben a privt tagvltoz neve valjban
m_toDoListModelChanged mr emltettk, hogy ezt gy szoks, s hogy az m valjban a
member, azaz tag sz rvidtse.
A tulajdonsg persze nincs azzal teljesen ksz, hogy mindezt elmondtuk rla. Deklarlnunk
kell mg a
void toDoListModelChangedChanged(bool);

signal-t, illetve deklarlsra s megvalstsra szorul mg a kiolvas- s belltfggvny is.


me:
bool ModelManager::toDoListModelChanged() const
{
return m_toDoListModelChanged;
}

void ModelManager::setToDoListModelChanged(bool toDoListModelChanged)


{
m_toDoListModelChanged = toDoListModelChanged;
emit toDoListModelChangedChanged(toDoListModelChanged);
}

Most viszont tnyleg elkszltnk vele, mr csak hasznlatba kell vennnk. Elszr
is, a FileOperator osztlyban jelezzk, ha az gykdsnknek ksznheten a
modell azonos llapotv vlt a fjllal. Ez mind a sikeres mentst, mint a sikeres
megnyitst kveten elmondhat, azaz ahol a performFileSaveOperation(), illetve
performFileOpenOperation() tagfggvnyben a fggvny vge fel a belltsok kz
mentjk a fjlnevet, bellthatjuk ennek a tulajdonsgnak az rtkt is, mgpedig a
modelManager>setToDoListModelChanged(false);

utastssal. Magban a modelManager objektumban viszont jformn minden


tnykedsnkkel elrontjuk a fjl s a modell szp konzisztens llapott, azaz
setToDoListModelChanged(true);

utastst kell elhelyeznnk a modelItemChanged(), a deleteSelectedTask(), az


undoLastDelete(), a newTaskAfterSelected(), a newTaskBeforeSelected(), a
moveUpSelectedTask() s a moveDownSelectedTask() tagfggvnyben. Azonban az
emptyModel() tagfggvnyben pp ellenkez a helyzet: itt false rtket kell megadnunk.

90
11. Adatments s -visszatlts... Qt strandknyv

A tulajdonsg megvltozsakor mindig emittlunk signal-t is, amihez a fablakban runk egy
j kis publikus slot-ot:
void MainWindow::showIfModelChangedSinceLastSave(bool changed)
{
QSettings settings;
QString fileName = settings.value("Files/LastFileName",
tr("unsaved")).toString();
if(changed)
this>setWindowTitle(QCoreApplication::applicationName() + " *
"
+ fileName);
else
this>setWindowTitle(QCoreApplication::applicationName() + " "
+ fileName);
}

A slot igyekszik megtudni a jelenlegi fjl nevt, ha nem megy neki, akkor jelzi, hogy a fjl
mg nincs mentve. Ha a legutols fjlmvelet ta van vltozs, akkor csillagot is r a cmsorba,
mskor viszont nem. Ne feledjk sszektni a signal-lal:
connect(modelManager, SIGNAL(toDoListModelChangedChanged(bool)),
this, SLOT(showIfModelChangedSinceLastSave(bool)));

Most, hogy vgre minden pillanatban tisztban vagyunk vele, hogy kell-e menteni,
megrhatjuk az j listt kezd tagfggvnyt, vagy legalbb komolyabban tgondolhatjuk, mit
is kellene tudnia. Elszr is, megnzi, hogy kellene-e menteni, s ha igen, akkor megkrdezi a
felhasznlt, hogy is gy gondolja-e. Ha a felhasznl azt mondja, hogy mentene, akkor kap
r lehetsget. Ha azt mondja, hogy nem mentene, akkor kap j, res feladatlistt. Ha pedig azt
lltja, hogy meggondolta magt, akkor nem erltetjk mi sem a dolgot, s meghagyjuk neki a
rgi listjt.
gy addott, hogy mgsem rjuk meg mg a slot-unkat, elbb megrjuk azt a tagfggvnyt,
amelyik majd figyelmezteti a felhasznlt:

91
Qt strandknyv 11. Adatments s -visszatlts...

bool FileOperator::lastPossibiltyToSaveFile() {
if(modelManager>toDoListModelChanged()){
QMessageBox mb;
mb.setIcon(QMessageBox::Warning);
mb.setText(tr("Your ToDoList has changed since the last
save."));
mb.setInformativeText(tr("Grab this last possibility to save
it!"));
mb.setStandardButtons(QMessageBox::Save | QMessageBox::Discard |
QMessageBox::Cancel);
mb.setDefaultButton(QMessageBox::Save);
int ret = mb.exec();
switch (ret) {
case QMessageBox::Save:
onActionSaveTriggered();
return true;
break;
case QMessageBox::Discard:
return true;
break;
case QMessageBox::Cancel:
return false;
break;
}
}
return true;
}

Az ablakunk egy zenetablak, s ezttal a szabvny gombok kzl vlogatunk. Megadjuk azt
is, hogy alaprtelmezs szerint a felhasznl igenis menteni akar, gy legalbb nem lesz gond,
ha vaktban nyomkodja az Enter-t. A tagfggvny visszatrsi rtke dnti el, hogy kap-e res
listt a felhasznl. Ha nem is kell menteni, akkor true rtkkel trnk vissza, azaz kap. Ha
kell menteni, akkor hvjuk ugyanazt a slot-ot, amit a Ctrl+S billentykombinci lenyomsa
esetn is hvnnk, s ahol vagy azonnal megtrtnik a ments, vagy elbb fjlnevet krnk, s
azutn igyeksznk menteni. Mindenesetre a vgn csak megkapja a felhasznl az j listjt.
Ha a felhasznlt nem rdekli az adatainak elvesztse, vagy pp ezt akarta, akkor is kap j
listt. Egyedl akkor nem kap jat, ha a Mgse gombra kattint.
Vgre a slot-unk kvetkezik:
void FileOperator::onActionNewTriggered()
{
if(lastPossibiltyToSaveFile()){
modelManager>emptyModel();
}
}

Eltvoltjuk a bejegyzsek kzl a fjlnvre vonatkozt, s krnk egy j modellt. Persze csak
akkor, ha a felhasznl meg nem gondolta magt. Az emptyModel() tagfggvny mg privt,
tegyk gyorsan publikuss, s ha mr gy is arra jrunk, helyezznk el benne egy
settings.remove("Files/LastFileName");

sort, klnben nha-nha fellrunk olyasmit is, amit nem akarunk.

92
11. Adatments s -visszatlts... Qt strandknyv

Most pedig futtassuk a programunkat, s rvendezznk, igazn remek lett. Eltekintve attl,
hogy megnyitskor mg simn rnyitja a mentetlen listra a mentettet. rdemes volna ezt is
rendbekapnunk, fleg, hogy semmi tbbet nem kell tenni, mint az onActionOpenTriggered()
slot trzst krbevenni egy if(lastPossibiltyToSaveFile()){} felttellel.

11.4. Automatikus ments s megnyits


Az automatkus ments mr elg rgen flksz llapotban leledzik. A FileOperator osztly
ltrehozst kveten azonnal megrtuk az adjustAutoSave() tagfggvnyt, amit a fablak
restoreApplicationState() tagfggvnye hv, rszint a program indulsakor, rszint
a belltsablak belltsainak elfogadsakor. Az adjustAutoSave() fggvny elindtja
a visszaszmllst, csak akkor mg nem ktttnk semmit a visszaszmlls vgt jelz
QTimer::timeout() signal-hoz, mert nem volt mit. Most azonban hip-hopp ksz vagyunk a
megfelel slot-tal:
void FileOperator::autoFileSave()
{
performFileSaveOperation(settings.value("Files/LastFileName", "").
toString());
}

Mi a helyzet akkor, ha be van lltva automatikus ments, de nincs fjlnv? Nos, ez egy j
problmt vet fel, de ezt most gyorsan elvarrjuk: a felhasznl majd kap hibazenetet, hogy
nem ment a ments, s majd gondolkodik, hogy ugyan milyen mentsrl van sz. Az osztly
konstruktorban mg elhelyezzk a
connect(timer, SIGNAL(timeout()),
this, SLOT(autoFileSave()));

utastst, s az automatikus ments letre kel. Lssuk a program indulsakori automatikus


betltst. Ezzel rsen kell lennnk, mert a ModelMaganer::emptyModel() fggvny, amit
mr az osztly konstruktora is hv, trli az utols mentett fjl nevt. gyhogy sutyiban
eltesszk, mg az osztly pldnyostst megelzen, egy
QSettings settings;
QString lastFileName = settings.value("Files/LastFileName", "").
toString();

utastsprral eltesszk magunknak, a fileOperator objektum ltrejtte utn meg a


if(!lastFileName.isEmpty())
fileOperator->autoFileOpen(lastFileName);

utastssal kezdemnyezzk a fjl megnyitst. A hvott tagfggvny trzse teljes hrom sor:
void FileOperator::autoFileOpen(QString fileName)
{
modelManager>connectItemChangedSignalToSlot(false);
performFileOpenOperation(fileName);
modelManager>connectItemChangedSignalToSlot(true);
}

s ezzel ksz is vagyunk. Alighanem megrdemlnk egy lakomt. A hrcsg lhet a fhelyre
de csak azrt, mert ott jl ltszik, s nem kell flnnk, hogy beszalad valamilyen sarokba,
ahonnan soha tbb nem tudjuk elkotorni.

93
Qt strandknyv 12. A modell, a deleglt, meg a proxymodell

12. A modell, a deleglt, meg a proxymodell

Ebben a fejezetben elszr azzal fogunk foglalatoskodni, hogy sajt delegltat runk a
modellnkhz. Persze mg nem felttlen tudjuk, mi az a deleglt, de majd idvel fny derl
r. Ha megvagyunk vele, ismt beizztjuk az automatikus kiegsztst, ami ugyan nem lesz
tkletes, viszont elkpeszten kevs munkval jr, s a ksrletez kedv Olvas eltt nyitva
marad a lehetsg a tkletestsre. Vgl elksztjk a msodik fln a keresst is.

12.1. Deleglt megvalstsa a QStyledItemDelegate osztly


hasznlatval
Azt grtk, hogy idvel kiderl, mi az a deleglt. Most jtt el az id. A deleglt sz eredeti
rtelmben ugye olyasvalakit jell, akire valamilyen feladatot r lehet szni, s az illet a
nevnkben eljrva intzkedhet. A QStyledItemDelegate osztly objektumokra a modell
elemeinek megjelentst, szerkesztst szza r az ember, mr olyankor is, amikor nem
is sejti. A mi modellnk delegltjai jelen pillanatban QLineEdit osztly objektumok.
Amikor szerkeszteni kezdnk egy elemet, az elem rtkt tadjuk egy QLineEdit osztly
objektumnak, amely a szerkeszts befejeztvel visszahelyezi a megvltozott elemet a modellbe
ezt az esemnyt teszi kzhrr a QStandardItemModel::itemChanged() signal.
Ha az alapbelltsokkal nem vagyunk maximlisan megelgedve, akkor alosztlyt kpznk
a QStyledItemDelegate osztlybl, s megadjuk, hogy a nzetnk ezt hasznlja inkbb.
Ebben az alfejezetben mi is pontosan ezt tesszk majd. Fogjunk is hozz!
Els feladatunk az j osztly elksztse. Adjunk teht j osztlyt a projektnkhz, mgpedig
Delegate nven. Az osztly sosztlyul adjuk meg a QStyledItemDelegate rtket, a Type
Information sornl pedig lltsuk be, hogy inherits QObject. A friss osztlyunkban az sosztly
ngy virtulis publikus tagfggvnyt kell megvalstanunk, gymint:
QWidget*
zz createEditor(QWidget *parent, const QStyleOptionViewItem
& option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
zz
void setModelData(QWidget *editor, QAbstractItemModel *model, const
zz
QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const
zz
QStyleOptionViewItem &option, const QModelIndex &index) const;

Amikor elkezdjk a deklarcikat a delegate.h fjlban, a Qt Creator szerkesztje elbb-


utbb felismeri a nevet, s automatikusan fel fogja ajnlani neknk a paramterlistt. Okos,
nem?
Amikor szerkeszteni kezdjk a modell egy elemt, a nzet a createEditor() tagfggvny
hvsval pldnyost magnak egy brmilyen QWidget-leszrmazott objektumot. Az
updateEditorGeometry() tagfggvnynek az a dolga, hogy a j helyen jelentse meg a
ksz szerkesztt. Ha megvan a szerkeszt, a nzet a setEditorData() hvsval adja t neki
a szerkesztenivalt. A szerkeszts befejeztvel a setModelData() fggvny helyezi vissza a
megvltozott elemet az eredeti helyre.
Valstsuk meg ezeket a tagfggvnyeket. A createEditor() tagfggvnyben annyi a
dolgunk, hogy ltrehozunk egy QLineEdit osztly objektumot, s visszaadjuk a mutatjt.

94
12. A modell, a deleglt, meg a proxymodell Qt strandknyv

me a fggvny trzse:
Q_UNUSED(option);
Q_UNUSED(index);
QLineEdit *editor = new QLineEdit(parent);
return editor;

A Q_UNUSED makrt hasznltuk mr arra j, hogy a fordt ne mltatlankodjon a


paramterlistban megtallhat, mde ltalunk nem hasznlt vltozk miatt.
A setEditorData() trzse az albbi formt lti:
QString value = index.model()>data(index, Qt::EditRole).toString();
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(value);

Elszr is kiharapjuk a megfelel cella tartalmt, s elhelyezzk a value vltozban. s,


hogy mi az a Qt::EditRole? Igen, egy enum, de mirt kell? gy ll a dolog, hogy a modell egy-
egy eleme nem csak azt az adatot tartalmazza, amit els rnzsre gondol az ember. Hanem
rengeteget mg, pldul az eltr s a httr sznt, a bettpust, meg hogy a szveg merre
van rendezve25. Ezek a Qt nevezktana szerint role-ok, azaz szerepek. Mi a fenti utastssal az
adatok kzl azt a role-t krjk el, amelyik a szerkesztben val munkra alkalmas.
Sajnos itt nem hasznlhatjuk a j kis editor nevet, mert sszegabalyodik a
paramterlistban lvvel, gy a lineEdit-re fanyalodunk. A paramterlistban tadott
szerkeszt mindig QWidget tpus, mi ennek egy alosztlyt hasznljuk, de hogy melyiket,
azt a trzs msodik sora eltt csak mi tudjuk, a Qt nem. A static_cast arra val, hogy
elmondjuk a Qt-nak is, hogy ez a QWidget valjban s egsz konkrtan egy QLineEdit. s
mirt olyan fontos szmunkra, hogy a Qt is pontosan tisztban legyen a lineEdit objektum
osztlyval? Azrt, mert a kvetkez sorban olyan tagfggvnyt hvunk, amely nincs m
minden mezei QWidget-ben, de a QLineEdit osztly objektumokban van.
Ott tartunk, hogy vadul szerkesztgethetjk az elemnket. Ha pedig befejeztk a szerkesztst,
visszaadhatjuk a modellnek, hadd rljn. Ezt a feladatot vgzi a setModelData tagfggvny.
Ime a fggvny trzse:
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
model->setData(index, lineEdit->text());

A negyedik tagfggvnyt kicsit mg implementlatlanul hagyjuk, pontosabban, minthogy


mr deklarltuk, megrjuk, de res trzzsel. Enlkl is elfut majd a delegltunk, de kicsit furn: a
szerkesztablak rossz helyen jelenik majd meg. Legalbb ltjuk, hogy mirt rdemes dolgoznunk. A
sok nem hasznlt vltoz miatt majd kapunk persze fl kil warningot, de kibrjuk, ha meg nem, ott
a Q_UNUSED.
Mieltt az osztly pldnyostsba fognnk, mg el kell helyeznnk az osztly valamelyik
fjljban a <QLineEdit> fejlcet. Ha megvan, oldalogjunk t a fablakba. Deklarljuk a
Delegate osztly objektumra mutat delegate mutatt, majd a preparationAtStartUp()
tagfggvnyben pldnyostsunk neki objektumot, s mutassuk is be az j delegltat a nzetnek:
delegate = new Delegate(this);
ui->tableView->setItemDelegate(delegate);

A program fordul, a deleglt mkdik. Igaz, kicsit idtlen, hogy mindig a nzet bal fels
25 Itt a tbbi is: http://qt-project.org/doc/qt-5.1/qtcore/qt.html#ItemDataRole-enum

95
Qt strandknyv 12. A modell, a deleglt, meg a proxymodell

sarban van, de ht ugye, aki nem valstja meg az updateEditorGeometry() tagfggvnyt,


annak nem jr jobb. Akkor ht valstsuk meg. Kt teljes sor a trzse:
Q_UNUSED(index)
editor>setGeometry(option.rect);
Ha most futtatjuk a programunkat, klappol minden. J sokat dolgoztunk, hogy vgre legyen
egy sajt delegltunk, ami ugyanazt tudja, mint az alaprtelmezett. Ht normlisak vagyunk?!
Persze volt mindennek rtelme, hiszen gy mr testre szabhat a delegltunk. Kezdetnek
oldjuk meg, hogy a kt oszlopban ms-ms legyen a szerkeszt httrszne. Ezt gy tudjuk
elrni, hogy az objektum pldnyostst kveten megvizsgljuk, hogy az adott index elem
melyik oszlopban van, s ms-ms rtket adunk meg a 0, illetve az 1 sorszmhoz. Szrjuk be
ezt a pr sort a return editor utasts el:
if(index.column() == 0)
editor>setStyleSheet("QLineEdit {background: yellow;}");
else
editor->setStyleSheet("QLineEdit {background: lightgreen;}");

Tvoltsuk el mg a Q_UNUSED(index); sort, s gynyrkdjnk a sznkavalkdban.


Nekifoghatunk az automatikus kiegszts megvalstsnak. A kiegsztst egy QCompleter
osztly objektum vgzi. Hasznltunk mr ilyet, taln emlksznk is r, hogy egy modellt
is meg lehetett adni neki forrsul. Ht most pp ezt tesszk majd: megadjuk neki magt a
toDoListModel-t, annak is a msodik oszlopt. Persze a Delegate osztly nem ismeri
a ModelManager osztlyt, gy a modellrl sem tud. Egyelre. Alaktsuk t a Delegate
osztly konstruktort gy, hogy paramterknt krje el a modellre mutat mutatt. Ha mr
nekifogunk, gyorsan pldnyosthatunk a konstruktorban QCompleter osztly objektumot is,
meg be is lltjuk neki a modell msodik oszlopt:
Delegate::Delegate(QStandardItemModel *model, QObject *parent) :
QStyledItemDelegate(parent)
{
this>model = model;
completer = new QCompleter(model, this);
completer>setCompletionColumn(1);
completer>setCaseSensitivity(Qt::CaseInsensitive);
completer>setModelSorting(QCompleter::CaseInsensitivelySortedModel);
}

Belltjuk mg a kis-nagybetkre val rzketlensget, illetve azt, hogy a modellbl kapott


adatokat rendezze is a completer objektum. Ne felejtkezznk el a model s a completer
mutat deklarlsrl, illetve a <QStandardItemModel> s a <QCompleter> fejlc
felvtelrl. Vltoztatnunk kell a fablakban a delegate objektumot pldnyost soron is,
alaktsuk ilyenn:
delegate = new Delegate(modelManager>toDoListModel, this);

Van mr szp completer objektumunk, pp csak annyi vele a gond, hogy senki nem hasznlja.
A Delegate osztly createEditor() tagfggvnyben kell az else gon elhelyezni mg az
editor>setCompleter(completer);

utastst. Megy a kiegszts, br az els rmnket hamar elrontja, hogy ha mr tbbszr


van megadva egy kategria, akkor tbbszr is felajnlja a completer. gy jrtunk.

96
12. A modell, a deleglt, meg a proxymodell Qt strandknyv

12.2. A keresfl s a QSortFilterProxyModel osztly


Vajh mi lehet az a proxymodell? Olyan modell, amelyik automatikusan vltozik, kapcsolatot
tart fenn az eredeti modellel ez lesz a proxymodell forrsmodellje , de az eredeti modellnek
nem minden elemt tartja meg, vagy mskpp mondja el ket. A proxymodellek a Qt-ban a
QAbstractProxyModel osztly leszrmazottai, de ez az osztly, mint a neve is mutatja, elvont,
azaz rengeteg fggvnyt meg kell valstani ahhoz, hogy hasznlhassuk. A lehetsgek vg-
telenek: megoldhat pldul, hogy egy szmokat tartalmaz modell proxymodellje szavakknt
mondja el neknk az eredeti modell szmait, vagy csak a pratlan szmokat tartalmazza.
A dolog azonban macers, s egy strandknyvben nem fogunk bele ilyesmibe. Szerencsre
arra, amire neknk kellene a proxymodell, nevezetesen, hogy csak bizonyos keressi
feltteleknek megfelel sorokat tartson meg az eredeti modell tartalmbl, mr van beptett
leszrmazott osztly, a QSortFilterProxyModel. Rendez s fleg: szr, ht ez kell neknk!
A proxymodellt is a ModelManager osztlyban helyezzk el. Adjuk meg a fejlcek kztt a
<QSortFilterProxyModel>-t, majd deklarlunk publikus mutatt searchModel nven. Az osz-
tly konstruktorban pldnyostsunk hozz objektumot s ejtsk meg a szksges belltsokat:
searchModel = new QSortFilterProxyModel(this);
searchModel>setSourceModel(toDoListModel);
searchModel>setFilterKeyColumn(1);
searchModel->setFilterCaseSensitivity(Qt::CaseInsensitive);

Felhvnnk a figyelmet a harmadik sorra: itt dl el, hogy a keresst a kategrik oszlopban
vgzi majd a modell, amely immron teljesen mkdskpes, pp csak semmi nem hasznlja.
Kullogjunk t a fablak preparationAtStartUp() tagfggvnybe, s csempsszk bele az
albbi sorokat:
ui>searchView>setModel(modelManager>searchModel);
ui>searchView>horizontalHeader()>setSectionResizeMode(QHeaderView::
Stretch);
connect(ui>searchEdit, SIGNAL(textChanged(QString)),
modelManager>searchModel, SLOT(setFilterFixedString
(QString)));

Az els sorban a keresfln elhelyezett nzet modelljl a proxymodellt adjuk meg. Vegyk
szre, hogy pp gy adjuk meg, mint ha igazi modell lenne. A nzetnek fogalma nincs, hogy
ez a modell nem olyan modell. A msodik sor szerepe az, hogy a modell kitltse a nzetet
ezt a rendes modellnk nzete esetben is gy oldottuk meg. A harmadik sor pedig azrt
felel, hogy a proxymodell csak azokat a sorokat tartalmazhassa, amelyek msodik oszlopban
megtallhat a searchEdit objektumban lv karakterlnc.
Most, hogy megy a keress, nincs kibv: keressk meg a hrcsgs feladatainkat, s lssuk
el kis kedvencnket.
Tartozunk egy vallomssal: ksz a ToDoList modelles vltozata. A jobb keznkkel
megveregethetjk a bal vllunkat balkezesek termszetesen hasznlhatjk ellenkez oldali
fels vgtagjaikat a gyakorlat elvgzsre.
s gy, a tornamutatvny vgre kvetkezzen mg egy megszvlelend j tancs: a vals
letben a programjainkba kemny ellenrzst kell ptennk a bemeneti fjl integritst
illeten. Ha pldul a feladatlistafjlbl kivesszk a teendt a kategritl elvlaszt cs (|)
karaktert, gy elszll a programunk, mint szi viharban a manyagzacsk.

97
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

13. Hlzati szinkronizci: a ToDoList


felhcskje

Ebben a fejezetben a modell-nzet minta lehetsgeit kihasznl ToDoList-vltozatbl


kiindulva olyan alkalmazst ksztnk, amelyik kpes az adatfjljt webszerverre
szinkronizlni. Az elkpzels a kvetkez: ha a felhasznl szeretn, hogy a programja a
feladatlista-fjlt a megadott webszerverre szinkronizlja, akkor a program mentskor
menti
zz a fjlt a helyi adattrolra
feltlti
zz a megadott webszerverre
feltlt
zz mell egy idblyeget is.
A fjl megnyitsakor:
letlti
zz az idblyeget a webszerverrl
megnzi,
zz hogy az idblyeg szerint a webszerveren lv fjl az jabb, vagy a helyi
ha
zz a webszerveren lv az jabb, akkor letlti azt is
megnyitja,
zz amelyiket kell.
A programunk nem fog semmifle hitelestst tartalmazni a webszerver fel. Termszetesen
semmilyen titkosts nem jn szba. Komolyabb problma, hogy a megoldsunk csak akkor
mkdik jl, ha a felhasznl sszes eszkzn be van kapcsolva a hlzati szinkronizci.
Termszetesen semmifle konfliktuskezelst nem valstunk meg. A legnagyobb baj azonban
az, hogy a megoldsunk nem nyjt lehetsget arra, hogy az egyik eszkznkn elkszlt
fjl ltt a msik, jonnan hasznlatba vett eszkznkn szrevegyk. Ha az egyik gpnkn
ltrehoztuk a cucc.tdolst llomnyt s felszinkronizltuk a webszervernkre, a msik
gpnkn csak gy tudjuk majd letlteni, ha ott mr eleve volt egy rgebbi cucc.tdolst.
Hogy mirt lesz ilyen korltozott tuds a programunk? Az a helyzet, hogy a val letben igen
komoly szerveroldali httr szerveroldali alkalmazs kellene ahhoz, hogy megbzhatan
mkdjn egy ilyen szolgltats, s ez csak egy strandknyv.
Ennyi htult mellett a fejezet mg mindig j lesz arra, hogy a hlzatok fel tett kis
kirndulssal sroljuk a Qt hlzatkezels-tengernek felsznt.

13.1. A webszerver elksztse


Hlzati alkalmazs programozsba fogni sajt, klnbejrat hlzat nlkl manapsg
dresgnek szmt, amit nem is fogunk elkvetni. Fleg, hogy a hlzat a mi alkalmazsunk
esetben annyira egyszer, hogy egy gpen elfr a kliens is s a szerver is.
Olyan webszerverre van szksgnk, amely kpes PHP-kdot futtatni. Kivtelt kpeznek azok
az Olvask, akik tltve magukat a szerveroldali rsz meglehets sszetettsgn, vllalkoznak a
hamarosan olvashat, PHP-nyelv kd ms szerveroldali krnyezetbe val tltetsre.
Akr Windows-on, akr Linux-on, akr OS X-en dolgozunk, alighanem a legkzenfekvbb
vlaszts egy Apache 2.x webszerver teleptse. Hogy mirt pont ezt ajnljuk, mikor annyi
ms szp webszerver van? Azrt, mert ez a legelterjedtebb nylt forrs, azaz brki szmra
beszerezhet webszerver. s, lvn a legelterjedtebb, a legjobban dokumentlt is fordulhatunk
a hivatalos forrsokhoz, de a YouTube is segtsgnkre lehet.

98
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv

Mikor az Apache 2.x fenn van, akkor teleptennk kell mell PHP-t is, s az Apache-nak
szlni kell, hogy vegye hasznlatba a PHP-t, s kezelje, futtassa le a .php fjlokat.
A PHP-telepts ellenrzsre egy akarmi.php nev fjlt szoks elhelyezni ott, ahol a
teleptsnk alaprtelmezs szerint a webszerver fjljait trolja (Windows-on ez alaprtelmezs
szerint az Apache telepts alatti htdocs knyvtr, Debian alap Linuxokon, azaz Ubuntu-n is a
/var/www mappa). A fjl tartalma legyen a kvetkez:
<?php
phpinfo();
?>
Ha ezek utn a bngsznkben a http://localhost/akarmi.php cmre naviglunk (a localhost
minden gpen maga a helyi gp, bels, gynevezett visszacsatol, angolosan: loopback hlzati in-
terfsze, IP-cme: 127.0.0.1), akkor a kvetkezhz hasonl kpnek illene fogadni bennnket:
Nzznk mg utna, hol talljuk meg
rendszernkn az Apache naplfjljait
(angolul: log files). Ha a belltfjlok s
a dokumentci alapjn vgkpp nem
talljuk, akkor rdemes fjlkeresssel
prblkoznunk. Keressnk error.log,
illetve access.log nev fjlokat. A
naplfjlok helye Windows rendszeren
alaprtelmezs szerint az Apache teleptsi
mappja alatti logs knyvtr, Debian
alap Linux-okon a /var/www/apache2
mappa. A naplfjlok egyszer szvegfjlok, 25. bra: Ha ilyet ltunk, akkor sikeresen beze-
brmilyen egyszer szvegszerkesztvel meltk a PHP-t
megnzhetk, de a parancssorbl is olvashatk. Windows-on rdemes lehet a more
programocskt hasznlnunk a megfelel mappban:
more < error.log
Linuxokon a naplfjlok folyamatosan frissl tartalmt a tail paranccsal tudjuk gy
figyelemmel ksrni, hogy nem kell a fjlokat jra s jra megnyitni:
tail -f /var/log/apache2/*.log
A fejleszts sorn a naplk vizsglata sok fejfjstl kmlheti meg az embert (azrt e
fajspecifikus kijelents, mert a hrcsgk ltalban nem szoktak naplfjlokat nzni). Innen
fog kiderlni, ha rossz helyre tettk a szerveroldali PHP-kdot, vagy ha azrt nem megy a
fjlfeltlts, mert a PHP-nak nincs engedlye az adott knyvtrba rni.
Pr bekezdssel feljebb utaltunk r, hogy hihetetlenl sszetett s kifinomult PHP-kdot kell
fejlesztennk a fjlfeltlts lehetsgt megteremtend. Ilyen az eleje:
<?php
$targetName = "/var/www/" . basename( $_FILES['uploaded]['name]);
move_uploaded_file($_FILES['uploaded]['tmp_name], $targetName);
?>
s ilyen a vge is, merthogy figyelmetlensgnkben mris vgigolvastuk mind a ngy sort.
A "/var/www" rszt cserljk ki a mi rendszernk megfelel mappjra, majd a fenti ngy sort
ptygjk be egy szvegfjlba persze letlthetjk a knyv webhelyrl is , s mentsk az

99
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

elbbi akarmi.php fjl mell, upload.php nven. Hogy mit csinl ez a ngy sor? Elfogadja
a feltlttt fjlokat, s a megadott helyre menti ket. Ennl hosszabb magyarzatra nem
vllalkozunk, elvgre ez nem PHP-strandknyv.
Nzzk meg, hogy a webszervernket futtat felhasznlnak van-e joga rni a megadott
mappba, s elvileg a szerveroldali belltsokkal megvagyunk. Nem gyzzk hangslyozni,
hogy fejleszts kzben Fl szem, fl fl a figyels26, mrmint a naplfjlok figyels.
Akkor ht vissza a Qt-hoz.

13.2. Kiegsztjk a bellts-ablakot


A grafikus fellettervezben alaktsuk
ilyenre a settingswindow.ui fjl tartalmt:
A kt j elem neve: webCheckBox s
webLineEdit. Igen, tudjuk, hogy van mg
egy QLabel osztly felirat is, de azzal a
programban nem beszlgetnk, ilyetnfor-
mn a neve lnyegtelennek minsttetik.
llttassuk el a webCheckBox slot-jt,
majd egsztsk ki a kdot.
Az osztly konstruktorban beolvasunk
mg kt belltst:
26. bra: Az j bellts-ablak
ui>webCheckBox>setChecked(settings.value("Web/SyncEnabled",
"false").toBool());
ui->webLineEdit->setText(settings.value("Web/Address", "").toString());

Az OK gomb lenyomsakor a tbbivel egytt ezeket is troljuk:


settings>setValue("Web/SyncEnabled", ui>webCheckBox
>isChecked());
settings->setValue("Web/Address", ui->webLineEdit->text());

A webCheckBox slot-ja nagyon hasonl lesz a msikhoz mrmint, ha jl rjuk meg. A slot
trzse a kvetkez:
if(arg1 == Qt::Unchecked)
ui>webLineEdit.setEnabled(false);
else if(arg1 == Qt::Checked)
ui->webLineEdit.setEnabled(true);

Ksz is vagyunk. A prbafuttats sorn troltathatjuk a webcmet a webLineEdit-ben a


localhost szt kell megadnunk, ha a webszerver ugyanazon a gpen zemel, ahol a programunkat
fejlesztjk.

13.3. Feltlts a WebSynchronizer osztllyal a


QNetworkAccessManager s egyb hlzati rmk
Hogy is szeretnnk mi ezt pontosan? Ksztsnk egy WebSynchronizer osztlyt, amelynek
kt publikus tagfggvnye van mrmint a konstruktoron kvl, persze. Az egyik feladata
26 Cipr, Vili a verb, Pannnia Filmstdi, 1989.

100
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv

a fjl lemezre val mentst kveten feltlteni a webszerverre, a msik meg az, hogy
a fjl megnyitsa eltt letlti az idblyeget, s ha az idblyeg szerint a webszerveren
lv feladatlista-fjl az jabb, akkor letlti azt is, s fellrja vele a helyi pldnyt. A
WebSynchronizer osztlyt majd a fileOperator objektumban pldnyostjuk. Mentskor-
betltskor megnzzk a belltsfjlt, s ha kell szinkronizlni, akkor hvjuk a megfelel
tagfggvnyt. (Ismt megjegyezzk, hogy a feladat jelenlegi megvalstsa sok kivetnivalt
hagy maga utn. Ha pldul a letlts sorn flig jn csak le a fjl, azzal is fellrjuk a helyi
pldnyt, s akkor aztn hasznlhat fjl nlkl maradunk, ami az lmosknyv szerint semmi
jt nem jelent. Amikor mindezt majd lesben csinljuk, sokkal gondosabban kell eljrnunk.)
Adjuk ht hozz a WebSynchronizer osztlyt a projekthez. Az se lehet egy mezei QObject,
semmi bonyolultabbra nincs szksgnk. Deklarljuk benne az albbi kt publikus tagfggvnyt:
void syncFileBeforeOpen(QString fileName, QString webAddress);
void syncFileAfterSave(QString fileName, QString webAddress);

Helyezzk el a megvalstsukat egyelre res trzzsel a websynchronizer.cpp fjlban,


s ezt kveten a FileOperator osztly konstruktorban pldnyostsunk magunknak egyet az
j osztlybl, mgpedig webSync nven, a heap-re:
webSync = new WebSynchronizer(this);

Ezt kveten battyogjunk le a performFileOpenOperation() tagfggvnyhez, s valahol


az elejn, de mg mindenkpp azeltt, hogy a helyi fjllal babrlni kezdennk, helyezzk el az
albbi sorokat:
//webSync
if(settings.value("Web/SyncEnabled", "false").toBool())
webSync>syncFileBeforeOpen(fileName, settings.value("Web/
Address").toString());
//webSync done

Szvnk titkos zugai elruljk neknk, hogy a kvetkez teendnk az lesz, hogy hasonl
sorokat helyeznk el a performFileSaveOperation() tagfggvnyben is, de ezttal olyan
helyre, ahol mr nem fogunk babrlni a helyi fjllal. Ilyen hely pldul az if(success)
teljeslse esetn lefut g. Az elhelyezend sorok a kvetkezk:
//webSync
if(settings.value("Web/SyncEnabled", "false").toBool())
webSync>syncFileAfterSave(fileName, settings.value("Web/
Address").toString());
//webSync done

Aki most a WebSynchronizer osztly kt meglv tagfggvnyben elhelyez egy-egy


qDebug() zenetet, az figyelemmel ksrheti, hogy milyen remekl lefutnak a tagfggvnyek.
Jah, krem, hogy semmi rtelmeset nem csinlnak? Kicsinysg!
Illetve nem is. Taln mgis inkbb meg kne ket csinlni rendesen. Kezdjk a feltltssel,
mr csak azrt is, hogy utbb legyen mit letlteni.
A feltlts sorn ugyebr kt fjlt kell majd feltltennk. Az egyik maga a feladatlista-fjl, a
msik az idblyeget tartalmaz prja. Ismtld feladatra leltnk, azaz mehetnk tagfggvnyt
rni. A neve legyen a fantziads uploadFile(). Legyen mondjuk kt argumentuma, a fjlnv
s a webcm. Eddig teht a syncFileAfterSave() hvja ktszer az uploadFile()-t.

101
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

Hogy is trtnik maga a feltlts? Az gynevezett rlap-alap feltlts mddal dolgozunk,


amelynek a mdjt a 1867-es RFC rja le.27
Eszerint elkldnk egy krst a webszervernek, ebben tudtra adjuk, hogy mi pp adatot
akarunk kldeni neki. Elruljuk neki, hogy milyen hatr (boundary) fogja jelezni az adat
elejt s vgt, aztn az orrra ktjk azt is, hogy milyen hossz lesz az adat. Az adat ebben az
esetben nem csak magt a fjlt jelli, mint az hamarosan nyilvnvalv vlik.
Az adat hossznak megadst kveten pedig jn az adat: mvelet kezdett jell boundary,
majd megmondjuk, hogy mit kell vele csinlni mi majd azt mondjuk, hogy oda kell adni az
upload.php-nek. Ezt kveti megint egy boundary, aztn elruljuk a fjl nevt, a fjl tpust,
majd jhet maga a fjl, vgl a lezr boundary.
Az elz kt bekezds a Qt esetben is lesen elvlik. A krs (a kdunkban: request) utn
kvetkezik az adattmb (array). Az adattmb legyrtst megint kln tagfggvnyre bzzuk,
gy jobban elklnl a dolog logikja. Azaz a fggvnyhvsi lnc gy egszl ki, hogy az
uploadFile() tagfggvny minden futsa alkalmval egyszer meghvja a createArray()
tagfggvnyt.
Rgen nylkltunk mr a ToDoList.pro fjlban, itt az ideje nmi turklsnak ismt.
Keressk meg azt a sort, amelyben a Qt keretrendszer ltalunk hasznlt rszeit soroljuk fel, s
egsztsk ki a network szval. Szval:
QT += core gui network

A Qt-ban a hlzati mveleteket egy, a QNetworkAccessManager nev osztlybl


pldnyostott objektummal szoktuk elvgeztetni. Egy pldny elg az egsz alkalmazsnak. A
WebSynchronizer osztly konstruktorban pldnyostsunk magunknak egyet:

networkManager = new QNetworkAccessManager(this);

A networkManager objektum post() tagfggvnyt hasznljuk arra, hogy elkldjk


a krst, illetve az adattmbt. A krs QNetworkRequest osztly objektum lesz, amely
konstruktornak ktelez paramtere az URL, ahova a krs irnyul. Ha kszen vagyunk a
krs pldnyostsval, be kell lltanunk a tartalomtpusra s az adattmb hosszra fejlceket,
majd elkldeni az egszet a webszervernek. A webszerver vlasza egy QNetworkReply
osztly objektumknt rkezik meg.
Akkor lssuk mindezt megvalstva:

27 A hlzati mveletek mikntjeit RFC-nek nevezett kvziszabvnyokban szoks megfogalmazni.


RFC-bl sok van, szmmal azonostjk ket. A bennnket rdekl RFC szvege a kvetkez c-
men (is) olvashat: http://tools.ietf.org/html/rfc1867

102
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv

void WebSynchronizer::uploadFile(QString fileName, QString webAddress)


{
QByteArray array = createUploadArray(fileName);
if(!array.isEmpty()){
QUrl URL = QUrl("http://" + webAddress + "/upload.php");

QNetworkRequest request(URL);
request.setHeader(QNetworkRequest::ContentTypeHeader,
"multipart/form-data; boundary=margin");
request.setHeader(QNetworkRequest::ContentLengthHeader,
QString::number(array.length()));

QNetworkReply *uploadReply = networkManager>post


(request,array);

QEventLoop loop;
connect(uploadReply, SIGNAL(finished()),
&loop, SLOT(quit()));
loop.exec(); //wait until upload finished
if(uploadReply>error())
qDebug() << uploadReply>errorString() << uploadReply>
error();

delete uploadReply;
}
}

A tagfggvny els sorban elksztjk az adattmbt, benne a feltltend fjllal (mindjrt


megmutatjuk, hogy miknt). Ha a tmb nem res ami akkor kvetkezhet be, ha a helyi
fjlt nem sikerlt megnyitni , nekikezdnk a feltlts elksztshez. Legyrtjuk az URL-t,
majd ennek felhasznlsval pldnyostjuk az a QNetworkRequest osztly objektumot,
amelyiknek a pldnyostst kvet kt sorban belltjuk a fejlceit. A mr sokat emlegetett
boundary neve lesz a margin. Lehetne pp brmi ms, az RFC csak annyit kt ki, hogy az
adatban ne forduljon el. Ht, a mi tesztadatainkban nem fog, s punktum. Figyeljk meg,
ahogy az adattmb hosszt is elhelyezzk a megfelel helyen.
Az uploadReply nev mutat jelzi a webszerver vlaszt a networkManager objektum
ltal kezdemnyezett beszlgetsre, melynek sorn az elksztett krst s az adattmbt HTTP
POST krsknt kldtk el a webszervernek.
A vlasz nem rkezik meg azonnal, s lehet, hogy nagyon sok jn majd, mert pldul lass a
hlzat. Tbb mdszer is volna arra, hogy csak akkor nyljunk a vlaszhoz, amikor mr megr-
kezett. Mindegyik azon alapul, hogy amikor visszart a vlasz, a QNetworkReply objektum egy
finished() signal-t emittl. Ezt a signal-t kthetnk ahhoz a slot-hoz, amelyik feldolgozza a v-
laszt, de taln a Qt hlzati programozsban mg nem profik szmra knnyebben kvethet,
ha inkbb indtunk egy esemnyhurkot, amit az uploadReply objektumbl rkez finished()
signal megrkeztekor megszaktunk. Magyarn: megvrjuk, amg visszar a vlasz.
Ha visszart, akkor feldolgozzuk. Ha hibt tartalmaz, akkor mind a hibt, mind a hibakdot
kirjuk. A kirsnak ksbb mg keresnk jobb helyet, egyelre megteszi a qDebug().
Vgl trljk a szksgtelenn vlt QNetworkReply osztly objektumot az uploadReply
mutat vgrl. Kifacsartuk, elvettnk mindent, amit adhatott, s most eldobjuk, igazi szvtipr
mdjra.

103
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

Ha rtjk, hogyan mkdik majd a feltltsnek ez a rsze, akkor lssuk a


createUploadArray() tagfggvnyt. Lnyegesen egyszerbb lesz, grjk.

QByteArray WebSynchronizer::createUploadArray(QString fileName)


{
QFile file(fileName);
QByteArray array;

if (file.open(QIODevice::ReadOnly)){
array.append("--margin\n");
array.append("Content-Disposition: form-data; name=\"action\"
\n\n");
array.append("upload.php\n");
array.append("--margin\n");
array.append("Content-Disposition: form-data;
name=\"uploaded\"; filename=\"" + fileName + "\"\n");
array.append("Content-Type: text/tdolst\n\n");
array.append(file.readAll());
array.append("\n");
array.append("--margin--\n");
}

return array;

A QByteArray remek kis osztly, de a mi szempontunkbl jelenleg csak annyi az rdekes


belle, hogy tulajdonkpp bjtsorozatokat trolhatunk a belle pldnyostott objektumokban.
Ha sikerlt megnyitni a fjlt, akkor berunk mindenfle okossgokat az adattmb elejre,
majd a QFile::readAll() tagfggvny hvsval az adattmb kzepre bepakoljuk az egsz
fjlt. Utbb mg pr okossgot fznk az adattmbz, s az egszet visszaadjuk a hvnak.
Ha nem sikerlt megnyitni a fjlt, akkor res objektumot adunk vissza, ezt az informcit az
uploadFile() tagfggvnyben ki is hasznljuk.
A syncFileAfterSave() tagfggvny trzse egyelre egyetlen sor:
uploadFile(fileName, webAddress);

Mikor mindezzel elkszltnk, futtassuk a mvnket. A ments ikonra kattintva elvileg


megtrtnik a feltlts ltnunk kell a webszerver naplfjljaiban is, illetve a megfelel
knyvtrban meg kell jelennie a feltlttt fjlnak.
Azrt ez elg sok buktats feladat volt, fleg azok szmra, akik elszr teleptettek
webszervert, elszr futtatnak PHP-parancsfjlt. gyhogy ha tnyleg felment a fjl, egy
btortalan s ktelked Hurrr!!! igencsak helynval.
Most pedig... Nem engednk a csbtsnak, nem nyargalunk letltst rni, hanem elszr gy
istenigazbl rendberakjuk a feltltst.
Azzal kezdjk a nagy rendberakst, hogy a syncFileAfterSave() tagfggvnyben
legyrtjuk s feltltetjk az idblyeget is. A tagfggvny egysoros trzst egszytk ki az
albbi nhny sorral:

104
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv

QString timeStampFileName = fileName + ".timestamp";


QFile file(timeStampFileName);
if (file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
QTextStream out(&file);
QFileInfo fi(fileName);
out << fi.lastModified().toString(Qt::ISODate);
}
if(!file.error())
uploadFile(timeStampFileName, webAddress);

Elvileg egyetlen jdonsgot tallunk a fentiekben. Az idblyeget tartalmaz fjl nevt


az eredeti fjlnv kiegsztsvel kpezzk. Elbb legyrtjuk itt helyben a fjlt, amelybe
a QFileInfo osztly lastModified() tagfggvnyvel lekrdezett QDateTime osztly
vlaszt rjuk bele, de a belers eltt a vlaszt karakterlncc alaktjuk, mgpedig az ISO ltal
meghatrozott formtumra. Ha a fjl adathordozra val rsa sikeres volt, az uploadFile()
tagfggvny msodik hvsval utnakldjk a feladatlistt tartalmaz fjlnak.
A rendberakst ott folytatjuk, hogy kiptnk egy mechanizmust, melynek hasznlatval
a felhasznlt a fablak llapotsorban rtestjk a hlzati mveletek sikerrl, illetleg
balsikerrl.
A WebSynchronzier osztly webSync objektum a fileOperator objektum privt
objektuma, azaz a fablak nem ltja, gy a singal-jait sem hallhatja. Ksztsk ht fel a
FileOperator osztlyt az zenet tovbbtsra. Definilunk egy tulajdonsgot, gy, ahogy azt
nem is olyan rg megtanultuk:
Q_PROPERTY(QString lastWebSyncMessage READ lastWebSyncMessage
WRITE setLastWebSyncMessage NOTIFY lastWebSyncMessageChanged)

Elhelyezzk az m_lastWebSyncMessage nev, QString osztly objektumot trolni kpes


provt vltozt, deklarljuk a void lastWebSyncMessageChanged(QString message);
signal-t, majd megrjuk a tagfggvnyeket:
QString FileOperator::lastWebSyncMessage()
{
return m_lastWebSyncMessage;
}

void FileOperator::setLastWebSyncMessage(QString message)


{
m_lastWebSyncMessage = message;
emit lastWebSyncMessageChanged(message);
}

Fontos, hogy a belltfggvnyt ne sima fggvnyknt, hanem publikus slot-knt adjuk meg,
mert majd ezt a tagfggvnyt fogja hvni a webSync objektum.
A fablakot ksztsk fel a fileOperator objektum fell rkez zenet vtelre, illetve az
llapotsoron val megjelentsre. Szksgnk lesz egy publikus slot-ra:
void MainWindow::lastWebSyncMessageChanged(QString message)
{
ui>statusBar>showMessage(message, 4000);
}

105
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

A szm az zenet utn azt mondja meg ezredmsodpercben, hogy mennyi id utn tnjn el
az zenet az llapotsorrl. Ha 0-t adunk meg, akkor a kvetkez zenetig ott marad. A fablak
preparationAtStartUp() tagfggvnyben elhelyezzk a connect utastst:

connect(fileOperator, SIGNAL(lastWebSyncMessageChanged(QString)),
this, SLOT(lastWebSyncMessageChanged(QString)));

Mostanra a fileOperator objektum s a fablak kztt zavartalan az informciramls. A


WebSynchronizer osztlyban tnykednk tovbb. Deklarlunk egy signal-t:

void syncReport(QString);

Eddig az uploadFile() tagfggvnyben a hlzati hibt egy qDebug()-kirssal jeleztk. Ezt


cseljk le most, radsul akkor is beszlnk, ha minden rendben ment. me a teljes if-utasts:
if(uploadReply>error())
emit syncReport(uploadReply>errorString() + " (code:" +
QString::number(uploadReply>error()) + ")");
else if(fi.suffix() != "timestamp")
emit syncReport("Network upload (" + fi.fileName() + ")
just happened.");

Ha volt hiba, akkor kirjuk, ha nem, akkor megmondjuk, hogy mit tltttnk fel, de csak
akkor, ha feltlttt fjl kiterjesztse nem timestamp. Azrt hallgatunk mlyen az idblyeg-
fjl feltltsrl, mert rgtn a feladatlista-fjl feltltse utn trtnik meg, s gy az zenetek
olyan gyorsan kvetnk egymst, hogy nem volna idnk megnzni az llapotsoron az els,
szmunkra fontosabb zenetet.
A fi, amelyet hasznlunk, egy QFileInfo objektum, amit persze a tagfggvny elejn
inicializlni kell:
QFileInfo fi(fileName);

A kiterjeszts ellenrzsn fell azrt van r szksgnk, hogy a fileName vltozbl


gyorsan s fjdalommentesen le tudjuk csippenteni az elrsi utat ha nem csippentennk le,
nem frne el az llapotsoron az zenet.
Az egsz hlzati mvelethez csak akkor fogunk hozz a tagfggvnyben, ha nem res a
createUploadArray() tagfggvnytl visszakapott tmb. Erre szolgl a tagfggvny eleje
fel tallhat
if(!array.isEmpty())

ellenrzs. Emltettk mr, hogy res tmbt akkor kapunk vissza, ha nem sikerlt a helyi
fjl megnyitsa. Ha ilyen malr trtnne, azt is tudjuk kzlni a felhasznlval, mgpedig gy,
hogy megrjuk a fenti if utasts else-gt:
}else
emit syncReport("Local file error while syncing " +
fi.fileName());

Kicsit tesztelhetjk a programot: rdemes rossz URL-t megadni a belltsok ablakban, vagy
lekapcsolni a webszervert, vagy eltenni az upload.php-t mshova. Ha minden jl megy, sok
szp j hlzati hibazenetet s -kdot ismernk majd meg. Az izgalmasabbakat felolvashatjuk
a hrcsgnek is, mikzben a flig becsukott markunkbl figyeli a monitort, s csak a
bajszocskja mozog izgalmban. A feltltssel vgeztnk, kezddhet a

106
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv

13.4. Letlts a WebSynchronizer osztllyal a QNetworkReply


osztly s a fjlok kicsomagolsa
Az a tervnk, hogy a syncFileBeforeOpen() tagfggvnnyel letltetjk az idblyeg-fjlt,
s a benne trolt adatot sszehasonltjuk a helyi fjlval. Ha a hlzaton lv fjl az jabb, akkor
letltjk, s fellrjuk vele a helyit. A fileOperator osztly errl mit sem tud: egyszeren
megnyitja az ott lv fjlt, amit kzben vagy jabbra cserltnk, vagy sem.
void WebSynchronizer::syncFileBeforeOpen(QString fileName, QString
webAddress)
{
QString timeStampFileName = fileName + ".timestamp";
downloadFile(timeStampFileName, webAddress);
QFile file(timeStampFileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
QString timestamp = in.readAll();
if(!file.error()){
QFileInfo fi(fileName);
if(fi.lastModified() < QDateTime::fromString(timestamp ,
Qt::ISODate))
downloadFile(fileName, webAddress);
}
}
}

A szoksos mdi szerint ellltjuk az idblyeg-fjl nevt, majd letltjk


mindjrt megmutatjuk, hogyan. Ha lert, megnyitjuk, s beolvassuk a tartalmt. A
QDateTime::fromString() tagfggvny a fjl tartalmbl llt el QDateTime osztly
objektumot, amit ssze lehet hasonltani a QFileInfo::lastModified() tagfggvny
ugyanilyen osztly kimenetvel.
A letltst vgz tagfggvny az albbi:

107
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...

void WebSynchronizer::downloadFile(QString fileName, QString


webAddress)
{
QFileInfo fi(fileName);
QUrl URL = QUrl("http://" + webAddress + "/" + fi.fileName());

QNetworkRequest request(URL);
QNetworkReply *downloadReply = networkManager>get(request);

QEventLoop loop;
connect(downloadReply, SIGNAL(finished()),
&loop, SLOT(quit()));
loop.exec(); //wait until download finished
if(downloadReply>error())
emit syncReport(downloadReply>errorString() + " (code:" +
QString::number(downloadReply>error()) + ")");
else{
if(fi.suffix() != "timestamp")
emit syncReport("Network download (" + fileName + ") just
happened."); //we dont want succesful timestamp download reports
QFile file(fileName);
if(file.open(QIODevice::WriteOnly))
file.write(downloadReply>readAll());
else
emit syncReport("Local file error while syncing " +
fi.fileName());
}

delete downloadReply;
}

A legtbb rsze mr ismers. Ellltjuk az URL-t, s a hasznlatval pldnyostunk


magunknak QNetworkRequest osztly krst. Ezttal a networkManager objektum get()
tagfggvnyt hasznljuk, amelynek egyetlen paramtere a krs. A szerver vlasza ezttal
is QNetworkReply osztly objektumknt rkezik. Igyeksznk okosabbak lenni az esetleges
hibazenetek alapjn, de ha egy md van r, akkor kiszedjk a fjlt a vlaszbl. Ez meglepen
egyszer dolog: a readAll() tagfggvnyre r lehet bzni az egszet.
Elkszltnk: tudunk fjlt webszerverre feltlteni, s onnan letlteni. Amennyiben azt
szeretnnk, hogy vals letben is hasznlhat hlzati szinkronizcink legyen, mg rengeteg
fejlesztenivalnk van, de errl a fejezet elejn mr elmlkedtnk.

108
14. Tbbszl programot runk Qt strandknyv

14. Tbbszl programot runk

Ebben a fejezetben kivtelesen nem rgtn a ToDoList-tet megynk csinostgatni, hanem


elbb elfilzgatunk a tbbszlsg szpsgeirl, aztn ksztnk egy pldaalkalmazst, s
csak mindezek befejeztvel trnk vissza kis kedvencnkhz, a feladatlista-nyilvntart
alkalmazsunkhoz.
Manapsg a tbbszlsg, a tbbszl programozs ismerete olyannyira alapkvetelmnny
vlt, hogy mg a strandknyvekbe is bekerl a tma. Meggondoland azonban, hogy a mi
programunkban tnyleg szksg van-e r, ugyanis a tbbszl programok hamar vlnak
nagyon bonyolultt.
Egy tbbmagos processzoron s napjainkban nehz olyat tallni, amelyik nem ilyen a
szlak futsa tnyleg trtnhet prhuzamosan. Ha ilyen esetben kt szl is nylkl ugyanabban
az objektumban, az knnyen bajhoz vezet. A problma illusztrlsra tekintsnk egy olyan osz-
tlyt, amelyikben van egy karakterlnc adattag. Ha a kt szl egyszerre matat a karakterlnccal,
nem biztos, hogy mire az egyik szlban jra hozznylunk a karakterlnchoz, azt talljuk ott,
amit ott hagytunk. A helyzet lehet mg ennl is rosszabb, ugyanis a karakterlnc mdostsa
nem elemi mvelet. Mi trtnik, ha az egyik szl pp a karakterlnc mdostsnak kells kze-
pn tart, amikor a msik is elkezdi mdostani ugyanazt a karakterlncot? Leginkbb taln mg
a jsok tudjk elmondani, de a programozk aligha. gy aztn feltalltk az osztlytagok zrol-
st ezt vgzi a QMutex s a QMutexLocker osztly ami az adattagokhoz, tagfggvnyekhez
val hozzfrseket sorba lltja, s vigyz, hogy egyszerre csak egy matats trtnjen.
A Qt biztost olyan osztlyokat is, amelyek szlbiztosak (angolul: thread-safe). A nagy tbbsg
azonban nem ilyen, ugyanis a folyamatos zrolgats-feloldozgats a legtbb esetben csak
flsleges plusz terhelst jelentene. Ilyen osztlyok hasznlatakor magunknak kell gondoskodni
a zrolsokrl, vagy olyan programot kell rnunk, amelynek a szlai nem turklnak
ugyanabban az objektumban. Vagy olyat, amelyik nem tbbszl.
Tbbszl programot gyakran runk olyankor, amikor a httrben valamilyen komoly
adatfeldolgozs zajlik, de azt szeretnnk, hogy a grafikus fellet ne merevedjen le az
adatfeldolgozs idtartamra. Persze ilyenkor sem kell mindig tbbszl programot rnunk.
Az adatfeldolgoz ciklusban ugyanis idrl idre hvhat a QEventLoop::processEvents()
tagfggvny, ami az idkzben felgylt esemnyek feldolgozsra ad lehetsget.
Mg egy fontos megemltenivalnk van: a QThread osztly hasznlatt sok esetben mind
internetes forrsok, mind szakknyvek a most ismertetsre kerl mdszerhez kpest teljesen
mskpp rjk le a lnyeg ezekben a forrsokban tbbnyire az lesz, hogy ha tbb szlat
akarunk hasznlni, akkor leszrmazott osztlyt kell ksztennk a QThread-bl, s jra meg
kell valstanunk a run() tagfggvnyt. Ez a megolds mra elavult s ellenjavallt.
Nos, ha ennyi bevezet s eltntort dolog utn mg mindig tbbszl program
rsra adnnk a fejnket, akkor most nekikezdnk, de eltte mg indtsunk egy kln
hrcsgsimogat folyamatot: egy darabig most nlklnk kell ellennie.

14.1. Tanulmnyprogram a szlak tanulmnyozsra


A programunk elszr termszetesen egyszl lesz, mg akkor is, ha mr most a threads
nevet adjuk neki. Helyezznk el a fablakon QLabel-t showNumber nven, kt QLineEdit-et

109
Qt strandknyv 14. Tbbszl programot runk

startEdit s stopEdit nven, valamint kt nyomgombot, startButton s stopButton


nven. Taln sejtjk, hogy valami szmlls dolog lesz benne, aminek megtudjuk adni a kezd
s befejez rtkt. Nos, nem is csalatkozunk sejtsnkben.
A szmllt kln osztlyban helyezzk el. Az osztly egy tagvltozbl, kt slot-bl s egy
signal-bl ll. Az egyik slot indtja a szmllt, s a benne lv ciklus a fablak kt QLineEdit
elemben megadott rtk kztt fut. A ciklus olyan gyors volna, hogy nem ltnnk, ahogy
a szmok vltoznak, gy nmi ksleltetst helyeznk el benne egy QEventLoop osztly
objektummal emlksznk mg r? Ilyen objektummal oldottuk meg azt is, hogy a ToDoList
megvrja a hlzati mvelet vgt. A QEventLoop-ot akkor a QNetworkReply osztly
hlzati vlasz megrkeztekor szaktottuk meg, most pedig egy QTimer osztly objektum
timeout() signal-ja lesz a jel szmra, hogy elg volt belle.
A msik slot a tagvltozt lltja igaz rtkre. Ha a tagvltoz igazz vlik, mg azeltt
megszaktjuk az els slot ciklust, hogy az elszmolt volna a clrtkig.
me a kt slot:
void Counter::startCounter(int start, int stop)
{
QTimer *timer = new QTimer(this);
timer>setSingleShot(true);
m_stop = false;
for(int i = start; i < stop; i++){
if(m_stop)
break;
timer>start(100);
QEventLoop loop;
connect(timer, SIGNAL(timeout()),
&loop, SLOT(quit()));
loop.exec();
emit numberChanged(i);
}
delete timer;
}

void Counter::setStop(bool stop)


{
m_stop = stop;
}

A fablak lnyegben hrom slot-bl ll. Az egyik a szmll numberChanged() signal-ja


hatsra mdostja a showNumber nev objektum kirst. A msik a stop gomb lenyomst
rzkeli, s kibocstja azt a signal-t, amelynek hatsra a Counter::setStop() fut le. Ezt a kt
slot-ot gyorsan meg is mutatjuk, aztn rtrnk a harmadikra, a lnyegre.
void MainWindow::onNumberChanged(int number)
{
ui>showNumber>setText(QString::number(number));
}
void MainWindow::on_stopButton_clicked()
{
emit stopCounter(true);
}

110
14. Tbbszl programot runk Qt strandknyv

Szval a harmadik slot, a lnyeg. me:


void MainWindow::on_startButton_clicked()
{
Counter *counter = new Counter(this);
connect(this, SIGNAL(startCounter(int,int)), counter,
SLOT(startCounter(int,int)));
connect(this, SIGNAL(stopCounter(bool)), counter,
SLOT(setStop(bool)));
connect(counter, SIGNAL(numberChanged(int)), this,
SLOT(onNumberChanged(int)));
emit startCounter(ui>startEdit>text().toInt(), ui>stopEdit>
text().toInt());
delete counter;
}

Ltrehozunk benne egy szmllt. gy tesznk, mint ha nem volna elegend a stack-en
ltrehoznunk, s a heap-re tesszk. A kvetkez hrom utastssal bektgetjk az j objektum
slot-jait s signal-jt, majd elindtjuk a szmllt. Az utols utasts pedig trli a szmllt. A
m ksz, de jobban megfigyelhet a mkdse, ha az emit utasts el s mg elhelyeznk
egy-egy qDebug() utastst, amelyik a signal emittlst, illetve az objektum trlst jelzi.
Kzben Linux opercis rendszeren van rtelme kiadni a
watch -n 1 "ps H -C threads -o 'pid tid cmd comm'"
parancsot. A threads a fut binris fjlunk neve, a ps parancs emgyen felparamterezve
megmutatja tbbek kztt a folyamatazonostt (pid) s a szlazonostt (tid). A watch
parancs azt oldja meg, hogy msodpercenknt automatikusan jra lefusson a ps parancs,
gy a vilgkpnk is, legalbbis, ami a szlakat illeti. Nmileg megtdve lthatjuk, hogy a
programunk mris tbb szl megjelensvel jr, de hogy azokat nem mi kvettk el, az tuti.
Ha kitanulmnyoztuk magunkat, itt az ideje, hogy a szmllt zemeltet objektumot
kln szlra helyezzk. A szl szmunkra egy QThread osztly burkol (angolul:wrapper)
objektumon keresztl lesz elrhet. Az egyik problmnkat az okozza majd, hogy van mdunk
arra, hogy egy objektumot msik szlra tegynk t, de onnan visszavenni mr nem annyira
egyszer. A msik nyavalynk meg azzal kapcsolatos, hogy a msik szlon lv objektumot
mikor kell trlni? s mikor trlhet maga a szl?
No, majd megltjuk. Helyezzk el a mainwindow.cpp elejn a <QThread> fejlcet, majd a
szmll pldnyostst kveten pldnyostsunk magunknak szlat is:
QThread *thread = new QThread;

Ahogy ezzel megvagyunk, helyezzk t a szmllt az j szlra:


counter>moveToThread(thread);

Ezutn kvetkezhet a hrom connect, majd a szl elindtsa:


thread>start();

amit a mr eddig is meglv, startCounter() signal-t emittl sor, illetve a counter


objektumot trl sor kvet. Tudomsul vesszk, hogy a thread objektumot nem trltk, s ez
memriaszivrgssal jr, de egyelre kibrjuk. Indulhat a program. Lefordul, s futni kezd, de a
szmll indtsakor kapunk egy olyan zenetet, hogy:

111
Qt strandknyv 14. Tbbszl programot runk

QObject::moveToThread: Cannot move objects with a parent

s a counter vigyorogva marad az eredeti szlon, amit egybknt megfelelen elhelyezett


qDebug() << counter->thread(); utastsokkal magunk is ellenrizhetnk: a
QObject::thread() tagfggvny val a hasznlt szl kidertsre. Akkor ht a counter
objektumot pldnyost utastsbl trljk a this szlt, ami amgy is csak arra kellett
volna, hogy ne kelljen kzzel trlni az objektumot.
A counter objektum szpen thelyezdik a msik szlra ez lthat a qDebug() sorok
kimenetbl , de utbb gy elszll a programunk, hogy Lindbergh replgpe sem klnben.
Mirt is? Mert azltal, hogy az objektumunk msik szlra kerlt, kikerlt ennek a szlnak a
fciklusbl, s a delete nem vrja meg, amg lefut a szmll slot, hanem trli az objektumot.
Ha mi meg a delete sort trljk, akkor nincs gz a program futsval, de van egy olyan ob-
jektumunk (a counter), aminek mostanra se szlje, se trlse, azaz itt van mg egy memria-
szivrgs s akkor mg mindig nem trdtnk a thread objektum hasonl nygjeivel.
Szerencsre azonban ltezik nhny remek kis signal s slot, amivel minden, a fentiekben
vzolt problmnkon rr lehetnk. A QObject::deleteLater() slot dokumentcija szerint:

A Qt 4.8 ta, amennyiben a deleteLater() tagfggvnyt egy fut esemnyciklus


nlkli szlban l objektumon hvjuk, a szl befejezdsekor az objektum
megsemmisl.
Eddig ppec. Most az a krds, hogyan lltjuk le a szlban az esemnyciklust. Erre lesz j a
QThread::quit() slot, amely:

A szl esemnyciklust arra kri, hogy 0 (sikert jelz) visszatrsi rtkkel lpjen ki.
Azaz eddig gy ll a dolgunk, hogy lelltjuk a szl esemnyciklust, aztn hvjuk
a counter->deleteLater() tagfggvnyt, ami elkszti a counter objektum
megsemmistst. Mr csak a szl megsemmistsrl kell gondoskodni. Erre megint a
deleteLater() tagfggvnyt hasznljuk, de ezttal a thread objektumon hvva, ugyanis a
dokumentci szerint:

Az objektum trldik, amikor a vezrls visszatr az esemnyciklushoz.


Mrmint ahhoz az esemnyciklushoz, amelyikbl a deleteLater()-t hvtuk.
Mr csak megfelel signal-okat kell tallnunk a slot-jainkhoz. A QThread::quit() slot
hvst vgz signal-t neknk kell ellltanunk, gyhogy a Counter osztlyban deklarlunk
egyet magunknak, counterFinished() nven. A signal emittlst idztsk akkorra, amikor
a Counter::startCounter() slot-ban lv ciklus mr lefutott, vagy kilptnk belle, azaz
legyen ez a slot trzsnek utols sora. Kiadjuk a megfelel connect utastst is a fablakban,
olyankor, amikor mr van szl is, meg counter is:
connect(counter, SIGNAL(counterFinished()), thread, SLOT(quit()));

Mi legyen az a signal, ami a kt objektum deleteLater() tagfggvnyt hvja? Nos, szl


emittl magbl egy finsihed() signal-t, kzvetlenl a vgrehajts lellst megelzen. Ezt
fogjuk hasznlni mind a counter, mind a thread trlsre:
connect(thread, SIGNAL(finished()), counter, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

A mvnk gy mr rendben van, de ha szeretnnk ltni is, hogy tnyleg kipusztul a kt

112
14. Tbbszl programot runk Qt strandknyv

objektum, rdemes az objektumok destroyed() signal-jhoz rni egy akkora slot-ot, amelyik
egy qDebug() zenettel rteste bennnket az objektum elhallozsrl. Csak a connect-
utastsokat kzljk:
connect(thread, SIGNAL(destroyed()), this, SLOT(onThreadDestroyed()));
connect(counter, SIGNAL(destroyed()), this, SLOT(onCounterDestroyed()));

A programunk szpen fut, objektumok szletnek s pusztulnak. Van egy implementcis


hibnk, nevezetesen az, hogy a szmllt akrhnyszor elindthatjuk. Azonban, tekintve,
hogy ez a program csak illusztrci, nem is bnjuk. A fenti ps parancsot futtatva azt ltjuk,
hogy tbbszri szmll-indtskor szpen szaporodnak a QThread nev szlak. Ha van
kedvnk, a thread->setObjectName("worker"); utastssal elnevezhetjk a szlat, esetleg
hasznlhatjuk a thread->setObjectName("worker" + QString::number((quintptr)
thread).toLatin1()); utastst, amellyel a szl cmbl kpznk a szlaknak egyedi nevet.

14.2. Szlakat ptnk a ToDoList-be


Taln nem meglep, ha frissen megszerzett tudsunkat szeretnnk ddelgetett
projektecsknkbe is bepteni. Nem igazn kvnkozik hossz s kemny munkt ignyl
feladat, olyan, ami igazn kln szlnak val. Sebaj, majd kln szlra tesszk a webes
szinkronizcit gy ha az urambocs hosszan eltart, mert mondjuk ezermilli feladat van
felvve a listba, s lass a net, akkor mehet a httrben, mi pedig dolgozhatunk tovbb a a
programban.
Elsknt a WebSynchronizer osztlyon ejtjk meg a szksges talaktsokat. Megfogadva
egy jtancsot28, a konstruktorbl eltvoltjuk azt az utastst, amely a heap-en hoz ltre
objektumot, ugyanis az ilyen objektum mg azeltt ltrejn, hogy a webSync objektum
tkerlne az j szlra. A tnyllsbl pedig az kvetkezik, hogy gy a konstruktorban
ltrehozott objektum marad a fszlon, azaz a kt objektumunk kt kln szlon lesz. Az
lmosknyv szerint ez nem szerencss. Persze QNetworkAccessManager-re szksgnk
van, de majd ksztnk magunknak az adott tagfggvnyben. Olvastuk azt is, hogy
alkalmazsonknt egyetlen ilyen osztly objektum pp elg, de vgs soron egy szlban gy
is csak egy lesz. rtsk ht ki a WebSynchronizer osztly konstruktort, szntessk meg a
flslegess vlt deklarcit, cserbe a downloadFile() s az uploadFile() tagfggvnyben
helyezzk el az albbi sort:
QNetworkAccessManager networkManager;

A kt szksges helyen cserljk le a nyilat (->) pontra, hiszen immron nem a heap-re
pldnyostunk magunknak hlzatkezelt.
Kvetkez feladatunk a syncFileBeforeOpen() s a syncFileAfterSave() tagfggvnyt
slot-t alaktani, ami nem jelent mst, mint a kt sor thelyezst a websynchronizer.h
fjlban. Szksgnk lesz mg egy syncFinished() signal-ra, deklarljuk ht, s helyezzk
el az emittlsra val sort a kt, jonnan slot-t avanzslt fggvny trzsnek vgn. Ezzel
a WebSynchronizer osztly mdostsaival vgeztnk is. Az osztly mkdsben a
FileOperator osztly szempontjbl egyelre semmi nem vltozott, azaz ezen a ponton
rdemes lefordtanunk s kiprblnunk a programunkat mr most derljn ki, ha
elrontottunk valamit.

28 http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

113
Qt strandknyv 14. Tbbszl programot runk

Ha minden rendben fut, akkor elkezdjk a FileOperator osztly mdostst is. Hasonl
mdon kzeltnk a problmhoz, mint a threads nev pldaprogramunknl, azaz a
megfelel tagfggvny, illetve jabban slot hvst signal-hoz ktjk. Mg a signal emittlsa
eltt ltrehozzuk az j WebSynchronizer osztly objektumunkat, ttesszk az j szlra,
s kialaktjuk a mefelele signal-slot kapcsolatokat. A megfelel slot indtst vgz signal
ugyebr ugyanolyan paramterlistj, mint a slot, azaz esetnkben QString, QString.
Az els paramter a szinkronizland fjl neve, a msodik a webcm, ahova-ahonnan
szinkronizlunk.
A kt slot hvst egyazon signal-lal is elvgezhetjk, csak arra kell majd odafigyelni, hogy
az pp aktulis, WebSynchronizer osztly objektumunk melyik slot-jhoz ktjk a signal-t.
Deklarljuk ht a FileOperator osztly startSync(QString, QString) signal-jt.
Ahol eddig a webSync objektum syncFileAfterSave(), illetve syncFileBeforeOpen()
tagfggvnyt hvtuk, helyezznk el egy j hvst a FileOperator osztly sajt
tagfggvnyre. Az els esetben a
startWebSyncThread("save", fileName, settings.value("Web/Address").
toString());

a msodik esetben pedig a


startWebSyncThread("open", fileName, settings.value("Web/Address").
toString());

hvssal cserljk le. rjuk meg gyorsan ezt a remek kis szlkezelt:
void FileOperator::startWebSyncThread(QString job, QString fileName,
QString webAddress)
{
QThread *thread = new QThread;
WebSynchronizer *webSync = new WebSynchronizer();
webSync>moveToThread(thread);

connect(webSync, SIGNAL(syncFinished()), thread, SLOT(quit()));


connect(thread, SIGNAL(finished()), webSync, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

connect(webSync, SIGNAL(syncReport(QString)), this,


SLOT(setLastWebSyncMessage(QString)));
if(job == "open")
connect(this, SIGNAL(startSync(QString, QString)), webSync,
SLOT(syncFileBeforeOpen(QString,QString)));
else if(job == "save")
connect(this, SIGNAL(startSync(QString, QString)), webSync,
SLOT(syncFileAfterSave(QString,QString)));
thread>start();

emit startSync(fileName, webAddress);

A szoksos mdon ltrehozzuk a szlat, majd pldnyostunk magunknak


WebSynchronizer osztly objektumot (ne feledjk a konstruktorban lv pldnyostst,
trlni, az ottani connect pedig jl jn majd itt). A ksz objektumot ttesszk az j szlra, a

114
14. Tbbszl programot runk Qt strandknyv

kvetkez hrom connect pedig mr ismers: ezek az utastsok gondoskodnak a webSync s


a thread objektum trlsrl, ha eljn az ideje.
Ezutn a konstruktorbl thelyezett connect kvetkezik, majd a tagfggvny els
paramtere (open vagy save) alapjn eldntjk, hogy melyik slot-ot indttajuk el majd a
startSync() signal emittlsval.
Vgl elindtjuk az j szlat s emittljuk signal-t.
Megvagyunk, ksz a tbbszl ToDoList. Mr a fejezet elejn jeleztk, hogy nem minden
esetben ri meg tbbszlv alaktani a programunkat: az egszen szlssges helyzetektl
eltekintve most is csak lasstottunk rajta. Cserbe viszont van elkpzelsnk, hogy mi a teend,
ha egy szp napon olyan programot kell rnunk, amelyik szp mretes mappkban szmol majd
minden fjlra kln SSHA-ellenrzsszeget.

115
Qt strandknyv 15. XML-fjlba mentjk a feladatlistt

15. XMLfjlba mentjk a feladatlistt

A legtbb programoz megegyezik abban a krdsben, hogy elfordulnak olyan esetek,


amikor clszer lehet ezt-azt XML formtumban menteni. Az, hogy pontosan melyek ezek az
esetek, mr kshegyig men vitk trgyt kpezi. A vita termszetesen nem dlhet el addig,
amg a legfbb hozzrt a hrcsg llst nem foglalt, azaz nem most vrhat, hogy mi,
egyszer fldi halandk is megtudjuk a tutit.
Ebben a rpfejezetben a FileOperator osztlyunk trsval megvalstjuk azt, hogy a
ToDoList ne egyszer szvegfjlba mentse a feladatlistt, hanem egy remekbe szabott XML-
dokumentumba. Ahogy az egy j strandknyvhz illik, nem terheljk az Olvas lelkivilgt
az XML, illetve a Qt XML-lehetsgei teljes mlysgvel. Az XML akkora tma, hogy tbb szz
oldalas knyveket rnak rla, neknk meg csak pr oldalunk van a tmra.
Amit mindenkpp tudnunk kell az XML-rl, azt most gyorsan sszefoglaljuk. Az XML fjl
lnyegben szvegfjl, amelyben tbbnyire < s > jel kz kerlnek a jellk, ami meg
nem jell, az sima szvegknt van jelen. Az XML dokumentum elemekbl ll, amelyek
bizonyos esetekben startcmkvel kezddnek s stopcmkvel vgzdnek, az gynevezett res
elemek viszont egyetlen cmkbl llnak. Minden elemnek lehetnek jellemzi. Minden XML
dokumentum pontsoan egy gykrelemet tartalmaz. Minden elemnek lehetnek gyermekelemei.
A mi feladatlista-fjlunk gy fog kinzni:
<Tasks>
<Task job="egyik teend" category="egyik kategria"/>
<Task job="msik teend category="msik kategria"/>
<Task job="harmadik teend" category="egyik kategria"/>
</Tasks>
Ebben a dokumentumban a <Tasks> a gykrelem nyitcmkje, a </Tasks> a gykrelem
zrcmkje. A gykrelem gyermekelemei a <Task /> elemek, amelyek res elemek nincs
bennk igazi tartalom, szveg. res elemek lvn egyetlen cmkbl llnak. Br tartalmuk
nincs, vannak jellemzik, mgpedig mindegyiknek egy job s egy category jellemzje. A
jellemzk rtke az a szveg, amelyet aztn a programunk felhasznli felletn mi is ltunk,
megadunk, mdostunk, trlnk.
A DOM, azaz Document Object Model kiss pongyola, de szmunkra most hasznlhat
megfogalmazsban az XML dokumentumok memriabeli reprezentcija.
XML-gyorstalpalnk vgre rve mg elmondjuk, hogy a fejezetben a ToDoList-nek abbl
a vltozatbl indulunk ki amikor mr modellben troljuk az adatokat, de mg sem webes
szinkronizci, sem tbbszlsg nem kerlt a programba. Aki idkzben elhnyta ezt a
vltozatot, az termszetesen letltheti a knyv webhelyrl.
Mi sem mutatja jobban, hogy az XML mekkora tma, hogy ismt a ToDoList.pro fjl
megnyitsval kezdjk a munkt, s elhelyezzk az xml bejegyzst:
QT += core gui xml

Eltekintve hrom nansznyi mdoststl, a FileOperator osztlynak abban kt


tagfggvnyben fogunk piszklni, amelyek a tnyleges fjlmveletekrt felelsek, azaz a

116
15. XML-fjlba mentjk a feladatlistt Qt strandknyv

performFileOpenOperation() s a performFileSaveOperation() fggvnyben. Szoks


szerint a mentssel kezdjk, hogy legyen mit betlteni.
Eddig a modellbl olvastuk ki az adatokat, s a kiolvasst kveten fjlba rtuk ket.
Ezt a feladatot ltja el a mentsrt felels tagfggvny kzepn trnol ciklus. Egyesvel
vgignylazza a modell sorait, s amennyiben a teend nem res, mind a teendt, mind a
kategrit berja a fjlba.
Az XML-dokumentumok mentse mskpp trtnik: egy teljes utasts szksges a
kirsukhoz. Ha felttelezzk, hogy a mentend dokumentum DOM-ja, a mi olvasatunkban
memriabli reprezentcija a documentToSave objektumban rendelkezsre ll, akkor a
mentst vgz utasts ennyi:
out << documentToSave.toString();

Le is cserlhetjk ezzel az utastssal az elbb emlegetett ciklust. Csak az egyrtelmsts


kedvrt jelezzk meg, hogy az j sor a QTextStream out(&file); sor utnra s az if(!file.
error()) sor el kerl.
Persze ezzel mg nincs ksz a dolog, hiszen az imnt abbl a felttelezsbl indultunk ki,
hogy a documentToSave objektum rendelkezsnkre ll, holott nem ez a helyzet. Valahol az
imnt beszrt sor eltt helyezzk el a kvetkez rtkadst, de csak a <QDomDocument> fejlc
megadst kveten:
QDomDocument documentToSave = createXmlDocumentFromModel();

Azaz amit nyertnk a rven, vesztjk a vmon: nem kell ugyan piszmognunk a mentssel,
de el kell lltanunk a modellben lv adatok alapjn a DOM-ot. Milyen lpsekbl ll a
folyamat? Ltrehozunk egy res DOM-ot, azaz QDomDocument osztly objektumot, felvesszk
a gykrelemt, majd a gykrelem gyermekeiknt megadjuk az alkalmazsban hasznlt
modellnk egyes sorai alapjn ellltott elemeket. Mindez Qt-ul:
QDomDocument FileOperator::createXmlDocumentFromModel()
{
QDomDocument xmlDocument;
QDomElement rootElement = xmlDocument.createElement("Tasks");
xmlDocument.appendChild(rootElement);
for(int i = 0; i < model>rowCount(); i++){
QString job = model>index(i,0).data().toString().simplified();
if(!job.isEmpty()){
QString category = model>index(i,1).data().toString().
simplified();
QDomElement task = xmlDocument.createElement("Task");
task.setAttribute("job", job);
task.setAttribute("category", category);
rootElement.appendChild(task);
}
}
return xmlDocument;
}

Figyeljk meg, hogy az elem ltrehozsa sem a gykrelem, sem az egyes task
elemek esetben nem egyenrtk az elem felvtelvel a DOM-ba. Az elem ellltsa a
createElement(), a felvtele az appendChild() tagfggvny feladata. Lthat az is, hogy
a ksz task elemre mg a DOM-ba val felvtelt megelzen rbiggyesztjk a jellemzket. Az

117
Qt strandknyv 15. XML-fjlba mentjk a feladatlistt

XML-be val ments mkdik, de mieltt kiprbljuk, idzzk esznkbe, hogy sz volt hrom
nansznyi mdostsrl. A kierjesztseket kell trogatnunk, a ments esetben ktszer is
mert a QFileDialog statikus tagfggvnyn fell mg az automatikus kiterjesztsadst is meg
kell vltoztatni illetve a betltsnl. Kiprblhatjuk a mentst. Ha nem akarjuk, hogy a trolt
belltsok kztti LastFileName gondot okozzon, hasznlhatunk pp LastXMLFileName
kulcsot is, nem csak itt, de a mainwindow.cpp s a modelmanager.cpp fjlban is.
Taln nem vagyunk meglepve, ha kiderl, hogy a betlts hasonl szpsgekkel s
csnyasgokkal jr: ezttal is borzaszt egyszer dolog a fjl betltse a DOM-ba, de jn a
vm: rnk vr a DOM bejrsa s a modell feltltse. A performFileOpenOperation()
tagfggvny kzepn lv QTextStream-sort s a while-ciklust cserljk fel a kvetkez
utastssal:
documentLoaded.setContent(&file);

Ennyit a betltsrl. Persze kicsit mondjuk kt sorral korbban pldnyostani kell


magunknak DOM-ot:
QDomDocument documentLoaded;

Sikeres fjlmvelet esetn mr eddig is elvgeztnk ngy sorban hrom mveletet:


belltottuk a fjlnevet, szltunk, hogy a modell s a fjl tartalma mostantl megint azonos,
s j sort helyeztnk a modell vgre. Most mg, taln a msodik helyen, fel kell tltennk a
modellt. Helyezzk el a kvetkez fggvnyhvst:
populateModelFromXml(documentLoaded);

me a modellt benpest tagfggvny:


void FileOperator::populateModelFromXml(QDomDocument xmlDocument)
{
QDomNode rootElement = xmlDocument.documentElement();
QDomNode node = rootElement.firstChild();
while(!node.isNull()){
QDomElement element = node.toElement();
if(!element.isNull()){
QStandardItem *item = new QStandardItem(element.
attribute("job"));
model>setItem(model>rowCount(), 0, item);
item = new QStandardItem(element.attribute("category"));
model>setItem(model>rowCount()-1, 1, item);
}
node = node.nextSibling();
}
}

A fggvny els sorban beolvassuk a gykrelemet. A msodik sorban beolvassuk a


gykrelem els csompontjt (angolul: node), s a negyedik sorban elkezdjk bejrni a tbbit.
Figyeljnk meg, hogy a bejrst vgz ciklus bennmaradsi felttele, illetve a ciklus lptetst
vgz node.nextSibling() utasts egytt nagyjbl gy fordthat: amg van mg
csompont ezen a szinten. Lvn a mi XML-nk elgg lapos struktrj, nem kell bajldnunk
ms szintek ellenrzsvel.
A ciklus els sorbl nagyjbl kiderl, hogy az elem s a csompont valahogy gy
van egymssal, mint a bogr meg a rovar: nem minden csompont elem, de minden elem

118
15. XML-fjlba mentjk a feladatlistt Qt strandknyv

csompont. gy aztn konvertlnunk kell, s a konvertlst vgz lps eltt nagyvonalan nem
nzzk meg, hogy ez a csompont tnyleg elem-e egybirnt a QDomNode::isElement()
tagfggvnnyel ejthetnnk meg ezt az ellenrzst.
Az elem jellemzibl QStandardItem osztly objektumokat kpznk, amiket aztn vgre
tud kezelni a modellnk, el is helyezzk benne.
Ezt volt ht az XML-formtumot kezel ToDoList mesje. Ahogy az a j meskhez illik,
ennek is van tanulsga br a tanulsgot pr fejezettel ezeltt is levontuk , nevezetesen:
les projektben ellenriznnk kell, hogy a bemeneti fjl tnyleg neknk val-e, klnben a
programunk esetleg nem pont azt teszi majd, amit vrunk tle.

119
Qt strandknyv 16. A ToDoList, mint SQL-kliens

16. A ToDoList, mint SQLkliens

Ha mr az elz fejezetben a feladatokat XML-fjlban troltuk, lpjnk mg egy nagyot:


troljuk az adatainkat SQL-ben. Ezttal a fjltrols megvltozsa nem egyszer felszni
vltozs, hanem az egsz alkalmazsunkat rinti.
A mi adatbzisunk egyetlen tblt, a tasks nevt tartalmazza, amelyben nem meglep
mdon kt mez job s category bujkl, egyelre mg az elsdleges kulcsrl is
lemondunk. Az adatbzis adatait egy QSqlTableModel osztly objektumban helyezzk el,
amit termlsztesene ismt olyan nzethez kapcsolunk majd, amely kpes az adatok vltozsnak
megfelelen frissteni magt, illetve kpes arra, hogy a megvltozott adatokat visszahelyezze a
modellbe.
Azt grtk, hogy ezttal nem felszni, hanem az alkalmazsunk mlysgeit rint lesz
a vltozs. Ha ezt grtk, gy is lesz, de mi ennek az oka? Az, hogy a QSqlTableModel
osztly objektumok nagyon szorosan egytt lnek az adatbzis-kapcsolatukkal a modell
adatvltozsai azonnal megjelennek magban az adatbzisban is. Nem lesz ht kln
osztlyunk, amely a mentst s a megnyitst vgzi, s maga a ments nem klnl majd el
idben az adatmodell megvltozstl a felhasznl szempontjbl egy mvelet lesz egy j
feladat felvtele s a vltozs trolsa, egy meglv feladat mdostsa s a vltozs trolsa,
egy szksgtelenn vlt feladat trlse s a vltozs trolsa. St, nem csak a felhasznl
szempontjbl lesz egy mvelet, hanem a Qt-ban fejleszt fejleszt szmra is.
Azrt is vltozik az alkalmazsunk, mert az adatbzisok tbliban a legritkbb esetben van
jelentsge a rekordok sorrendjnek. gy rtelmt vesztik a ToDoList azon funkcii, amelyek a
sorok beszrsval, flfel-lefel mozgatsval kapcsolatosak. (Ha nagyon kell, termszetesen
megvalsthat az, hogy ezek a gombok visszanyerjk ltjogosultsgukat, de ez csak egy
strandknyv.)
A Qt adatbzis-szerverek egsz sorhoz kpes kapcsoldni s termszetesen rhatunk
sajt csatolt a sajt fejleszts SQL-szervernkhz, ha nem elegend a vlasztk. Mi ebben a
fejezetben az elterjedt SQL-adatbzisok sok tekintetben legkezdetlegesebb, mgis nagyon sok
helytt hasznlt megvalstsval, az SQLite-tal gykdnk.
Mi ht az, ami az SQLite-ot olyan vonzv teszi sok jelents alkalmazs szmra is? Az, hogy
nem kell hozz SQL-szerver, s szerver alatt ebben a szvegsszefggsben szmtgpet s
szoftvert egyarnt rthetnk. Az SQLite az SQL olyan megvalstsa, amely lnyegben nem
ms, mint egy fjl a httrtron egy SQLite fjl ilyen rtelemben igen hasonlt a Microsoft
Access, illetve a LibreOffice Base sajt adatfjljaira.

16.1. Gyomllunk
Trjnk vissza azokhoz a rgi szp idkhz, amikor mg pp csak belekezdtnk a modelles
ToDoList-vltozat kialaktsba. A knyv webhelyn is megtalljuk azt az llapotot, amikor
mr ki van alaktva a felhasznli fellet, de mg csak a nvjegy mkdik. Ebbl az llapotbl
indulunk ki, s mg trlnk is belle.
Nem fog kelleni a buttonNewBefore, a buttonUpSelected s a buttonDownSelected
gomb, a buttonNewAfter gombot meg kereszteljk t buttonNewTask-ra s a feliratt is ilyen
rtelemben vltoztassuk meg. Az Action Editor-ban hajigljuk ki a ments, ments msknt

120
16. A ToDoList, mint SQL-kliens Qt strandknyv

s a belltsok QAction-jt, illetve az ez utbbi QAction-hz tartoz slot-ot is puszttsuk.


Emltettk mr, hogy a ments lnyegben rtelmt veszti. A ments msknt nem volna
tbb egyszer tnevezsnl. A belltsok ablakban meg azt az automatikus mentst tudtuk
szablyozni, ami gyis megvalsul.
Utols mozzanatknt a szksgtelenn vlt ikonokat is eltvoltjuk az erforrsfjlbl, illetve
a ments ikonra val hivatkozst az about.html-bl. Ekkora tarols utn kszen llunk az j
feladatra.

16.2. Els QSqlTableModel-nk


Az els fl nzetvel ezttal egy QSqlTableModel osztly modellt fogunk sszekapcsolni.
Ha megnzzk az osztly dokumentcijt, azt ltjuk hogy a konstruktornak szinte ktelez
paramtere az adatbziskapcsolat. A szinte ktelez azt jelenti, hogy ha nem adnnk meg
paramterl egy kapcsolatot, akkor az alaprtelmezettet fogja hasznlni a tbla. S br mg nem
beszltnk egy szt sem errl az adatbziskapcsolat-dologrl, azt rtennk kell, hogy adatbzis
nlkl nincs modell. Azaz az j modell kialaktst megelzi az adatbzishoz val kapcsolds,
s ezen az sem vltoztat, hogy a mi esetnkben az adatbzis egy fjl.
Az osztly dokumentcijban semmi nem utal arra, hogy meg tudnnk vltoztatni a
modell alatt lv tblt. Ha pedig ez nem lehetsges, akkor szembeslnnk kell azzal hogy az
eddigiekkel ellenttben nem egy modellt fogunk rtgetni, pldul mieltt j fjlt nyitnnk
meg, vagy amikor res feladatlistt kezdennk, hanem kidobjuk a rgi modellt s jat fogunk
hasznlni. A nzet a fablakban lakik, a modellkezelt meg nll osztlyba tesszk, teht
amikor j modellnk van, szlnunk kell rla a nzetnek is. Erre kell majd egy signal, meg az
slot-ja.
A fentiekbl az is kvetkezik, hogy nem jrhat t az, hogy elindtjuk az alkalmazst, s majd
egyszer mentjk a tblnkat29, hanem az j feladatlista az j fjl nevnek megadsval, illetve
a fjl mentsvel kezddik. St, az a helyzet, hogy ez a ments valjban nem is ments, hanem
elszr ltrehozunk egy SQLite-fjlt, aztn abban a tblt, s megnyitjuk a fjlt, benne a tblt.
Mindebbl pedig az szrhet le, hogy a ltez fjl megnyitsa s az j fjl kialaktsa sok
szempontbl azonos dolog, csak az j fjl megnyitsa bonyolultabb. Megjegyezzk mg, hogy
e hosszas gondolkods azrt volt szksges, hogy hamarabb megrtsk, mirt pont gy rjuk a
programunkat, ahogy rjuk. s akkor most vgre rjuk!
A projektfjlban helyezzk el az sql bejegyzst:
QT += core gui sql

A projektben nyissunk egy TableManager osztlyt, mgpedig olyat, amelyiknek a


QWidget osztly az se. Megint pusztn azrt pont az, mert akarunk belle QFileDialog-
okat nyitogatni, s a QFileDialog-ok sosztlynak QWidget-leszrmazottnak kell lennie. A
fablak konstruktorban mris pldnyosthatunk magunknak belle, de eltte mg rjuk le a
ktelez krket a QSettings osztly knnyebb hasznlata rdekben:

29 Ha nagyon akarjuk, ppen megoldhat. Az SQLite adatbzisok lakhatnak egy specilis fjlban, a
:memory:-ban, s t lehet ket kltztetni ket igazi fjlba, de a megolds nem nyilvnval, s
inkbb SQLite-ismereteket ignyel, semmint ltalnosan hasznlhat Qt-SQL ismereteket. gy
aztn ez (is) kimarad strandknyvnkbl.

121
Qt strandknyv 16. A ToDoList, mint SQL-kliens

QCoreApplication::setOrganizationName("Sufni & Co.");


QCoreApplication::setOrganizationDomain("example.com");
QCoreApplication::setApplicationName("ToDoList");
tableManager = new TableManager(this);

A tablemanager.h fjlban deklarljunk publikus mutatt az modellnknek:


QSqlTableModel *tmodel;

Az osztly konstruktorban adjunk rtket az j mutatnknak, de mg nem igazi objektumot:


tmodel = NULL;

Ugyanis, mint emltettk, tbb esetben lesz majd j modellre szksgnk, ami radsul j fjl
elksztsvel is jr. gy a munkt rdemes volna kln tagfggvnyre bzni. A tagfggvny
meg ne bortson be bennnket QSqlTableModel osztly objektumokkal: rdemes eltte
ellenrizni, hogy van-e mr rgi, s ha igen, azt trljk, mieltt jat pldnyostannk. A
rgi objektum megltrl vagy nemltrl gy tudunk legegyszerbben megbizonyosodni,
hogy megnzzk a mutatjnak rtkt. Ha inicializlatlan mutat rtkt nzzk meg, abbl
nagy elszllsok lesznek, s ezrt a mutat rtkt az els lehetsges alkalommal inicializljuk:
NULL rtkre. Ezek utn tudni fogjuk, hogy amennyiben a mutat NULL, akkor nincs mg
modellnk, ha meg valami ms, akkor van.
Munklkodsunkat a konstruktorban azzal folytatjuk, hogy sztnznk, volt-e mr fjlunk
valaha, azaz a trolt belltsok kztt.
QSettings settings;
QString fileName = settings.value("Files/LastSQLFileName", "").
toString();
if(fileName.isEmpty())
newModel();

Egyelre nem foglalkozunk azzal az esettel, ha mr volt. Lssunk neki a newModel()


fggvny megrsnak. St, ne is modell legyen, hanem slot, s akkor mris kthetjk kevs
megmaradt fablakbli QAction-nk kzl az actionNew() nevezethz.
rjuk meg a newModel() slot elejt:
if(tmodel != NULL){
delete tmodel;
}
QString fileName = QFileDialog::getSaveFileName(this,
tr("New Database"),
QDir::homePath(),
tr("SQLite Database Files (*.sqlite)")
);
QFile file(fileName);
if(file.exists())
file.remove();

Azaz ha mr van modell, azt kukzzuk, krdezzk meg a felhasznlt hogy mi legyen az j
fjlnv, ha mr van ilyen fjl, azt kukzzuk... s?
Most jn az a rsz, hogy az j fjl felhasznlsval ltrehozzuk az j modellt. Ne feledjk
azonban, hogy a modellnek felttlen kellene SQLite-fjlhoz kapcsoldnia, hanem nyugodtan
hasznlhatnnk valami nagygy adatbzisszervert is. Azaz a modell adatbzis-kapcsolatot

122
16. A ToDoList, mint SQL-kliens Qt strandknyv

vr paramterl. Adatbzis-kapcsolatot meg mr meglv fjl megnyitsa esetn is ki kell


ptennk, azaz most, hogy flig sem vagyunk ksz a slot-unkkal, megrjuk a privt openDb()
tagfggvnyt
QSqlDatabase TableManager::openDb(const QString fileName)
{
if(db.isOpen())
db.close();
else
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(fileName);
db.open();
QSettings settings;
settings.setValue("Files/LastSQLFileName", fileName);
return db;
}

A fggvny kezdett olvasgatva feltnhet, hogy ilyen vltoznk mg nincs is a fejlvfjl


provt vltozi kztt helyezzk el a
QSqlDatabase db;

sort, s ne feledjk az #include <QSqlDatabase> sort a fjl elejrl. Szlunk s


hangslyozzuk: a QSqlDatabase osztly objektumok nem adatbzist, hanem adatbzis-
kapcsolatot jelentenek. Ennek tudatban rtelmet nyer, ahogy a fenti tagfggvny kezddik:
ha a kapcsolat nyitva volna, csukjuk be. Ha nincs nyitva kapcsolat, az a mi programunkban
csak azrt lehet, mert mg csak most fogjuk kipteni az elst, azaz mg adatbziskapcsolat-
objektumunk sincs. Ilyenkor pldnyostunk egyet magunknak, s a program sorn mostantl
mindig ezt fogjuk hasznlni. Az adatbzis-kapcsolat SQLite adatbzishoz fog kiplni, azaz ezt
a csatolt illesztprogramot, magyarosan driver-t adjuk meg.
A kvetkez cselekedetnk a fjlnv megadsa. A setDatabaseName() tagfggvny
paramterl egsz csnya dolgokat is elfogad, fleg, ha komolyabb, tbbfle adatbzist is kezel
csatolval pldnyostottuk a kapcsolat-objektumot. Ha az adatbzis nevt is tudja mr a
kapcsolat, elkzelhet, hogy felhasznlnevet, jelszt, miegymst is be kell lltanunk, de nem
a mi esetnkben, mi vigyorogva folytatjuk a megnyitst. Az open() tagfggvny visszatrsi
rtke logikai, azaz kiderl, hogy sikerlt-e megnyitnunk az adatbzist. Mi e pillanatban
annyira gyztesnek rezzk magunkat, hogy meg sem vizsgljuk a visszatrsi rtket ebbl
is ltszik, hogy nem ipari projekten dolgozunk.
Mg gyorsan eltroljuk a fjlnevet a sikeres(?) megnyits rmre, majd visszaadjuk a
hvnak az adatbziskapcsolat-objektumot.
Visszaoldaloghatunk a newModel() slot-ba, s most hogy van ilyen j kis adatbzisfjl-
nyitogat tagfggvnynk, megrhatjuk a modellt pldnyost sort:
tmodel = new QSqlTableModel(this, openDb(fileName));

Persze ez a fjl mg res, nincs benne egy incifinci picike kis tbla sem. A slot kvetkez
kt sorban ezen segtnk, s kzben megismernk egy j osztlyt (ne feledjk elhelyezni a
fejlct):

123
Qt strandknyv 16. A ToDoList, mint SQL-kliens

QSqlQuery query(db);
query.exec("CREATE TABLE tasks (job TEXT, category TEXT);");

A QSqlQuery osztly nem egyszer darab, tbbek kztt azrt sem, mert nem csak CREATE,
hanem SELECT utasts is megadhat benne, s gy az eredmny trolsa is feladata lehet,
radsul eszkzket biztost az eredmny bejrsra. Tovbb bonyoltja a helyzetet, amirl
az adatbzis-szakemberek vagy a lass hlzati kapcsolattal rendelkezk tudnnak
meslni, hogy nem minden lekrdezs lefutsa pillanatszer, s akkor mg nem is beszltnk
az egyb nyalnksgokrl. Mi most pp csak zeltt kaptunk a hasznlatbl. Hatrtalan
nbizalmunkban megint nem vizsgljuk, hogy rendben lefutott-e a lekrdezs, de vgre
megemltjk a QSqlDatabase::lastError() tagfggvnyt, ami egy kis qDebug()-gal
kombinlva sok fejfjstl kmlhet meg bennnket.
Itt az id, hogy szljunk a QSqlTableModel-nek, hogy ksz az j tbla, tessk hasznlatba
venni. Azonban ugyangy a szjba kell rgnunk a hasznland tbla nevt akkor is, amikor
mr ltez fjlt nyitunk majd meg, radsul a modellen mg szmos egyb belltanivalnk
van, azaz mr megint j privt tagfggvnyt runk:
void TableManager::setTableModelProperties()
{
tmodel>setTable("tasks");
tmodel>setEditStrategy(QSqlTableModel::OnFieldChange);
tmodel>setHeaderData(0, Qt::Horizontal, tr("job"));
tmodel>setHeaderData(1, Qt::Horizontal, tr("category"));
tmodel>select();
}

Az els dolgunk a megfelel tbla hasznlatba vtele. A msodik annak megadsa, hogy
mikor mentse a modell az SQL-tblaba az adatokat. A setEditStrategy() tagfggvny
paramtere a Qt j szoksa szerint egy enum, amely ezttal hrom rtket vehet fel. Amit
mi hasznlunk, az akkor menteti a vltozsokat, ha befejeztk a mez szerkesztst. Figyelem!
Ez nem adatbzis-mez, hanem modell-mez, azaz tulajdonkpp egy adott rtk szerkesztsrl
van sz. A kt msik lehetsg kzl az egyik, hogy akkor mentjk a vltozsokat, ha
kilptnk a rekordbl, a harmadik meg az, hogy majd kzzel mentjk a vltozsokat, amikor
pp nincs jobb dolgunk.
A munkt a fejlcek belltsval folytatjuk. Ha nem adunk meg fejlcet, akkor az
adatbzismez nevt hasznlja a modell, ami esetnkben trtnetesen megegyezik az ltalunk
belltottal. Akkor meg minek megadni? Azrt, mert gy lefordthatv vlik a kt oszlopcm.
Vgl a select() tagfggvny hvsa kvetkezik, amely szl a modellnek, hogy most mr
tveheti az adatokat a tblbl eddig resen llta a modell.
A newModel() slot trzsnek vgre helyezzk el a fenti tagfggvny hvst:
setTableModelProperties();

Az j modell hasznlatra ksz, mr csak szlni kne a fablaknak, hogy bellthassa a nzete
modelljl. Deklarljuk a fejlcfjlban a newTmodelReady(); signal-t, s itt, a slot vgn
emittljuk, kzben pedig elmagyarzhatjuk a hrcsgnek, hogy ez a tmodel nem az a T-Model,
ezt mi egyedl csinltuk, semmi kze Henry Ford-hoz.

124
16. A ToDoList, mint SQL-kliens Qt strandknyv

Kullogunk t a fablakba, s rjuk meg az elbbi signal-t fogad slot-ot.


void MainWindow::onNewTmodelReady()
{
ui>tableView>setModel(tableManager>tmodel);
}

A konstruktorban pedig, a tmodel objektum pldnyostsa al helyezzk el a connect


utastst:
connect(tableManager, SIGNAL(newTmodelReady()),
this, SLOT(onNewTmodelReady()));

Felmerl persze persze! egy hangynyi problma. A pldnyost sor lefutsval ltrejn
a tmodel objektum, annak meg kvzi a konstruktorbl hozzuk ltre az j modellt. Emittljuk
a modell elkszltt jelz signal-t, s utna hozzktjk a signal-hoz a slot-ot. No bueno.
Termszetesen ksbb j lesz mg ez a kapcsolat, de most pp semmire sem megynk vele.,
gyhogy kzzel hvjuk a slot-ot, rogtn a connect utasts utn:
onNewTmodelReady();

Ha ms gyis ktgetnk, mint a nagyi, adjuk mg ki az j feladatlistt kszt gombot a


newModel() slot-hoz kapcsol connect utastst:

connect(ui>actionNew, SIGNAL(triggered()),
tableManager, SLOT(newModel()));

Itt az id, hogy ennyi rengeteg mel utn vgre futtassuk az alkalmazsunkat. Ha felvettk az
sszes kihagyott fejlcet, akkor elindul az alkalmazs, s szpen bekri a fjl nevt, aztn ott a
nagy nesze semmi, fogd meg jl. Mkdik a New menpont is, hasonl eredmnnyel lehet,
hogy azrt, mert ugyanaz a slot fut le mindkt esetben?
Pusztn a szpsg min helyezznk el kt tovbbi sort a fablak konstruktorban nem is
kommentljuk ket, ismersek, csak mg nem jutottak az esznkbe:
ui>tableView>horizontalHeader()>setSectionResizeMode(QHeaderView::
Stretch);
ui->tableView->verticalHeader()->hide();

J volna taln, ha vgre berhatnnk egy teendt. Az j modellnkbe azonnal elhelyezhetnk


j sort, de a new task gombhoz is hozz kellene az j sor hozzadst. Alighanem slot-ot fogunk
rni:
void TableManager::appendEmptyRedord()
{
tmodel>insertRecord(-1, tmodel>record());
}

A sor jelentse a kvetkez: az utols sor utn (negatv indexek ezt jelentik ebben a tagfgg-
vnyben) szrj be egy olyan rekordot, amilyet egybirnt a tmodel-ben is tall az ember. Azaz
nem kellett szttygnnk az j rekord mezinek belltsval de senki ne izguljon, lesz mg
olyan is, amikor fogunk. Br elsre nem olyan feltn, de a slot a QSqlRecord osztlybl pl-
dnyostja a beszrand rekordot, gy helyezzk el ezt a fejlcet is a tbbi kztt. Ezt a slot-ot
kell hvunk, mg mieltt emittlnnk a newTmodelReady() signal-t. A fablak konstruktor-
ban pedig megrjuk a megfelel connect utastst, gy innen is hvhat a slot:

125
Qt strandknyv 16. A ToDoList, mint SQL-kliens

connect(ui>buttonNewTask, SIGNAL(clicked()),
tableManager, SLOT(appendEmptyRedord()));

Taln mg emlksznk, hogy szvesen lltottuk kijelltnek az els cellt, ezzel is hvogatva
a felhasznlt, hogy rjon mr vgre egy feladatot. A kijellseket a QItemSelectionModell
osztlybl pldnyostott objektum tartalmazza, s ha vletlen lustn arra gondolnnk, hogy
tl nagy macera ez, akkor emlkeztetnnk arra, hogy a trls gomb slot-jnak valahonnan
gy is meg kell tudnia, hogy melyik sor a trlend. gy aztn deklarlunk publikus mutatt
magunknak a tablemanager.h fjlban:
QItemSelectionModel *selectionModel;

A kijellsmodell konstruktornak ktelez paramtere a forrsmodell, azaz a


kijellsmodellt egytt trljk s egytt pldnyostjuk a tmodel objektummal. gy alakult,
hogy a newModel() slot elejn a delete tmodel sor al egy
delete selectionModel;

sor is kerl, s a tmodel obejktumot pldnyost sort az albbi kveti majd:


selectionModel = new QItemSelectionModel(tmodel, this);

Az appendEmptyRecord() slot-ba rjuk bele azt a kt sort, amely az utols sor els oszolpr
lltja a kijellst:
selectionModel>clearSelection();
selectionModel>setCurrentIndex(tmodel>index(tmodel>rowCount()-1,0),
QItemSelectionModel::SelectCurrent);

Vgl a fablak onNewTmodelReady() slot-jt egsztjk ki egy sorral:


ui>tableView>setSelectionModel(tableManager>selectionModel);

Mieltt kiprblnnk a fejleszts eredmnyt, a trolt belltsok kzl trljk az utols


SQL-fjlra vonatkozt, ugyanis csak akkor lesz rvnyes kijellsmodellnk, ha j fjlt
kezdtnk, fjlt megnyitni egyelre nem tudunk. Az onNewTmodelReady() slot viszont
mindenkpp belltja a selectionModel mutatt mint kijellsmodellt, azt meg mr a hrcsg
is tudja, hogy az inicializlatlan mutat olyan, mint a romlott rpa: aki babrl vele, rosszul jr.
Ha kiprbltuk a programunkat, s elgedettek vagyunk vele, tegynk egy kicsit azrt, hogy
ne kelljen mr kitrlgetni a trolt fjlnevet: valstsuk meg az automatikus betltst, privt
tagfggvnyknt. A gondos tervezsnek ksznheten meglepen kevs dolgunk lesz vele, nem
utolssorban azrt, mert, ahogy emltettk a fejezet elejn, az j fjl s a ltez fjl megnyitsa a
mostani esetben negyon hasonl lpsekbl ll. Lnyegben egy lebuttott newModel() slot-ot
kell rnunk:

126
16. A ToDoList, mint SQL-kliens Qt strandknyv

void TableManager::modelFromExistingFile(const QString fileName)


{
if(tmodel){
delete tmodel;
delete selectionModel;
}
tmodel = new QSqlTableModel(this, openDb(fileName));
selectionModel = new QItemSelectionModel(tmodel, this);
setTableModelProperties();
emit newTmodelReady();
}

Hasznlatba venni gy tudjuk, hogy a TableManager osztly kosntruktorban lv


elgazst kiegsztjk egy else-ggal:
else
modelFromExistingFile(fileName);

Oldjuk meg mg gyorsan azt is, hogy mkdjn a megnyits menpont is. Lnyegben arrl
van sz, hogy a felhasznltl bekrjk annak a fjlnak a nevt, amit meg szeretne nyitni, majd
a fjlnvvel felparamterezve hvjuk a modelFromExistingFile() tagfggvnyt. A feladatot
a kvetkez publikus slot valstja meg:
void TableManager::openFile()
{
QString fileName = QFileDialog::getOpenFileName(this,
tr("Open Database"),
QDir::homePath(),
tr("SQLite Database Files
(*.sqlite)")
);
if(!fileName.isEmpty())
modelFromExistingFile(fileName);
}

A fablak konstruktorban a kiadjuk a connect utastst:


connect(ui>actionOpen, SIGNAL(triggered()),
tableManager, SLOT(openFile()));

Az alapvet funkcik mkdnek, gyhogy megfelezhetnk egy almt a hrcsggel. Elg


kicsik ezek az almk, adjuk neki a nagyobb felt, j?

16.3. Trls s visszavonsa barkcsoljunk kzzel QSqlRecord


osztly objektumot!
A trlskor megszntetett rekord rtkei szoks szerint a privt undo objektumba kerlnek.
Aki vletlen nem emlkszik r, me az objektum deklarcija:
QStack<QPair<QString, QString> > undo;

Magt a trlst publikus slot-knt valstjuk meg:

127
Qt strandknyv 16. A ToDoList, mint SQL-kliens

void TableManager::onDeleteSelectedTask()
{
int row = selectionModel>currentIndex().row();
if(row >= 0){
undo.push(QPair<QString, QString>(
tmodel>index(row,0).data().toString(),
tmodel>index(row,1).data().toString()));
tmodel>removeRow(row);
tmodel>submit();
tmodel>select();
int rowcount = tmodel>rowCount();
selectionModel>clearSelection();
if(rowcount > 0){
if(row <= rowcount-1){
selectionModel>setCurrentIndex(
tmodel>index(row,0),
QItemSelectionModel::SelectCurrent);
}else{
selectionModel>setCurrentIndex(
tmodel>index(rowcount-1,0),
QItemSelectionModel::SelectCurrent);
}
}
}
}

A kijellsmodelltl a szoksos mdon megtudjuk az pp aktulis sor szmt, amely masszv


trlsi mveleteket kveten lehet akr negatv is ezrt az ellenrzs. A sor tartalmt
elhelyezzk az undo veremben, s utna trljk a sort. Aztn jn kt rdekes sor:
tmodel>submit();
tmodel->select();

Ha kihagyjuk ket, rgtn megltjuk mire kellenek fleg akkor ltszik szpen a helyzet, ha
ideiglenesen megjegyzss alaktjuk a fablaknak azt a sort, ami a nzet fggleges fejlct
vakarja ki. A QSqlTableModel osztlyban a trlt sor csak trlsre jelltetik a fggleges
fejlcen a szma helyett felkiltjelet ltunk s csak akkor trtnik meg a trls, ha elkldjk
a krst, majd jra betltjk az immr megvltozott tblt.
A kvetkez sorok mr csak azzal bajldnak, hogy pontosan hova kerljn a kijells a trlst
kveten.
A szoksos connect utasts a fablak konstruktorba:
connect(ui>buttonDelete, SIGNAL(clicked()),
tableManager, SLOT(onDeleteSelectedTask()));

A trls megy. A visszavonst vgz publikus slot-ban ismt az egyszer top(), msszor pop()
mdszert hasznljuk:

128
16. A ToDoList, mint SQL-kliens Qt strandknyv

void TableManager::onUndoLastDelete()
{
if(!undo.isEmpty()){
QSqlRecord record;
record.append(QSqlField("job", QVariant::String));
record.append(QSqlField("category", QVariant::String));
record.setValue("job", undo.top().first);
record.setValue("category", undo.pop().second);
tmodel>insertRecord(-1, record);
}
}

Az jdonsg az, ahogy a beszrand rekordot kzzel sszelltjuk. Hasznlhatnnk azt a


mdszert is, ahogy az appendEmptyRecord() slot dolgozik, azaz a modelltl elkrt rekordot
szrjuk be a modell vgre, utbb pedig bellthatnnk a rekord rtkeit. Azrt nem hasznljuk
azt, mert gy szp j mdszerrel gazdagodunk. Figyeljk meg, hogy az j rekordnak elsknt a
mezit adjuk meg minden mez QVariant tpus, de azrt tesznk utalst arra nzvst, hogy
mi a mez igazi adattpusa. A mezk megadst kveten rtket adunk az egyes mezknek,
majd az utols utni helyre (negatv index) beszrjuk a rekordot. Adjuk meg a <QSqlField>
fejlcet.
A szoksos connect-sor:
connect(ui>buttonUndo, SIGNAL(clicked()),
tableManager, SLOT(onUndoLastDelete()));

Most jutottunk el arra a pontra, hogy az els fl ksz gy aztn a keresfllel folytatjuk a
munkt.

16.4. Keress s kiegszts a QSqlQueryModel osztllyal


A QSqlTableModel osztly egy adott tbla tartalmnak megjelentsre alkalmas. Bizonyos
szempontbl lnyegesen gyesebb a QSqlQueryModel osztly, ez ugyanis nevhez mltan
egy SQL-lekrdezs eredmnyt jelenti meg modelll. A lekrdezs lehet tbbtbls, s
hasznlhat benne a teljes SQL-nyelvi repertor mr amit a programunkban hasznlt SQL-
megvalsts is ismer.
Persze ott van az a frnya bizonyos szempontbl kittel. A QSqlQueryModel osztly
objektumok ltal nyjtott modellek ugyanis csak olvashat modellek.
Mi most mindenesetre szeretnnk magunknak egy ilyet j, j kettt, de nem akarunk
annyira mohnak tnni, gyhogy a msikat majd ksbb. Deklarlunk neki publikus mutatt a
TableMaganer osztlyban:

QSqlQueryModel *searchModel;

Ne feledjk megadni a <QSqlQueryModel> fejlcet. A konstruktorban pldnyosthatjuk:


searchModel = new QSqlQueryModel(this);

A fablak konstruktorban adjuk meg, mint a searchView (a msodik fln lv nzet)


objektum modelljt, s ha mr gyis arra jrunk, elhelyezhetjk a szoksos kozmetikz sort is:

129
Qt strandknyv 16. A ToDoList, mint SQL-kliens

ui>searchView>setModel(tableManager>searchModel);
ui>searchView>horizontalHeader()>setSectionResizeMode(QHeaderView::
Stretch);

s hogy mire kell keresni? Termszetesen a msodik fln lv searchEdit tartalmra, a


category mezben. A searchEdit tartalmnak vltozst egy j kis signal folyton emittlja,
mr csak annyi a dolgunk, hogy runk egy publikus slot-ot a TableManager osztlyba, ami
amjd elkapja ezt a signal-t:
void TableManager::updateSearchModel(const QString searchString)
{
searchModel>setQuery("SELECT job, category FROM tasks WHERE
category LIKE \"%" + searchString + "%\";", db);
}

Figyeljnk a lekrdezsben szerepl idzjelek levdsre. A slot-hoz val connect utasts a


fablak konstruktorba:
connect(ui>searchEdit, SIGNAL(textChanged(QString)),
tableManager, SLOT(updateSearchModel(QString)));

Szinte tkletesen mkdik. Egyetlen rdekes problmnk addhat mg: ha kerestnk


valamire, majd felvesznk egy olyan jabb feladatot a feladatlistba, amelyiknek a
kategrijra illeszkedik a keressi felttel, akkor visszatrve a keresflre, a keress mg nem
frissl. A nehzsg lekzdhet, ha a tabWidget flszm-vltozst jelz signal-hoz runk
egy j slot-ot. Az j slot a QSqlQueryModel::query tagfggvny hasznlatval megtudja a
jelenlegi lekrdezst, majd a mr hasznt setQuery tagfggvnnyel jra lefuttatja.
Mg egyetlen dolgot szeretnnk megvalstani a ToDoList SQL-es vltozatban, azt, hogy
mkdjn az automatikus kiegszts. A kiegsztst vgz QCompleter osztly objektumot
az els fl tblzatban nem fogjuk hasznlni, legalbbis itt, a knyveben - nem fogunk
ugyanis delegltat rni. Semmi nem akadlyoz meg azonban bennnket abban, hogy a keresfl
searchEdit objektumnl be ne lltsuk a kiegszt hasznlatt.
Hozzunk ltre ht a TableManager osztlyban egy publikus mutatt a kiegsztst vgz
objektumnak, meg egy privtot, az elz objektum modelljnek:
QCompleter *completer;

illetve
QSqlQueryModel *completerModel;

Az osztly konstruktorban mindketthz pldnyostunk objektumot:


completerModel = new QSqlQueryModel(this);
completer = new QCompleter(completerModel, this);

s megrjuk a completerModel objektum frisstst vgz slot-ot:

130
16. A ToDoList, mint SQL-kliens Qt strandknyv

void TableManager::updateCompleterModel(const int tabIndex)


{
if(tabIndex == 1)
completerModel>setQuery("SELECT DISTINCT category FROM
tasks", db);
}

A slot-ot a fablakban a tabWidget flszmvltozst jelz signal-hoz ktjk, a completer


objektumot pedig belltjuk a searchEdit objektum segtjeknt:
connect(ui>tabWidget, SIGNAL(currentChanged(int)),
tableManager, SLOT(updateCompleterModel(int)));
ui->searchEdit->setCompleter(tableManager->completer);

A ToDoList SQL-es vltozata ezzel elkszlt. Klassz, igaz?

131
Qt strandknyv 17. Egyszer Qt program csomagolsa...

17. Egyszer Qtprogram csomagolsa


Linuxrendszerekhez

Ebben a fejezetben teleptt ksztnk kedvenc projektnkhz, de kizrlag Linux-


rendszereken. Windows-ra ugyanis annyifle mdon tudunk teleptt kszteni, hogy
elmondani is nehz ez cserbe azt is jelenti, hogy a sok-sok egysgestsi trekvs utn sincs
egy dvzt megolds.
Ezzel szemben a Linux rendszereken jellemzen ltezik az egy dvzt mdszer. Br
nha markns eltrsekkel, de alapveten azt az elkpzelst szoks megvalstani, hogy
az alkalmazsok csomagokba vannak szervezve, amelyek tudjk magukrl, hogy mely
egyb csomagoktl fggenek. Ha pldul egy alkalmazs tartalmaz alapvet fjlokat, de
kiegsztseket is, akkor azokat sok esetben kln fjlokba csomagoljk, s a kiegsztsek
fggenek az alapcsomagtl. Pldul amennyiben egy napon megrjuk kedvenc bicikli-
szimultorunkat, akkor az alapcsomag esetleg csak a jtk motorjt s egy gyakorlplyt
tartalmazza, a tbbi plyt kln-kln csomagoljuk majd. Termszetesen minden plya fgg
az alapcsomagtl.
De mit jelent az, hogy fgg? Nos, a gyakorlatban azt, hogy a csomag a teleptsekor
megkrdezi az pp fut rendszert, hogy teleptve vannak-e azok a fjlok, amelyektl maga
fgg? Ha igen, akkor is telepedik, ha nem, akkor a telepts lell, s megtudjuk, hogy mirt
nem hajland a rendszernkre az adott csomag felmszni: kirja, hogy kri mg ezt-s-ezt
telepteni, aztn prbljuk jra. Szokott mg egy ennl ersebb teleptsi mdszer is lenni,
amelyik, mikor az elz pldban felhozott jtk valamelyik plyjt teleptennk gy, hogy
maga a jtk mg nincs teleptve, udvariasan rdekldni fog, hogy akkor most telepteni
akarjuk a jtkot is, vagy hagyjuk a francba az egszet.
Mirt idztnk el ennyit a fggsnl? Azrt, mert a mi alkalmazsunk is fggeni fog a Qt
programknyvtraitl, a bennk trolt objektumoktl. Azaz, teleptskor meg fogja majd krdezni
a gpnk, hogy Ha mr teleptend ezt az alkalmazst, jhetnek a Qt megfelel rszei is?
A msik megolds az volna, hogy a hasznlt Qt-rszeket statikusan linkeljk az
alkalmazsba. Ez azonban rszint gazdasgtalan, rszint licencelsi problmkat vethet fel.
Mieltt nekikezdennk a kt legelterjedtebb Linuxos csomagformtum (deb s rpm)
valamelyikvel foglalkozni, mg pr gondolat a Qt-projektek fordtsrl. A fordts
alaprtelmezs szerint gy trtnik, hogy a qmake segdprogram elllt egy makefile-t,
aminek alapjn a gpnkn hasznlt make program fel tudja gy paramterezni a fordtt,
hogy a vgn azt kapjuk, amit szeretnnk. A qmake pedig alapveten a projektfjlbl
(projektnv.pro) lmodja meg, hogy mit rjon a makefile-ba.
A pldnkban egy rm egyszer alkalmazst fogunk lefordtani, olyat, amelyik nem egyb,
mint egy fablak. Ha Qt Creatorral vgeztk a fejlesztst, a kvetkez fjlok tallhatk majd a
projekt knyvtrban:
akarmi.pro
akarmi.pro.user
main.cpp
mainwindow.cpp

132
17. Egyszer Qt program csomagolsa... Qt strandknyv

mainwindow.h
mainwindow.ui
Ezek kzl az akarmi.pro.user a szempontunkbl teljesen flsleges, a tovbbiakban nem is
hasznljuk trlhetjk is, ha mg kelleni fog, a Qt Creator majd csinl magnak. Az akarmi.
pro fjlba viszont helyezznk el kt jabb sort:

target.path = /usr/bin
INSTALLS += target

Ezt kveten futtassuk le a mappnkban a qmake parancsot. A futs eredmnye j esetben


egy j, Makefile nev fjl a mappban.

17.1. Teleptcsomag ksztse Debian alap rendszerekre


Az akarmi mappt lssuk el verziszmmal is, nzzen ki gy: akarmi-0.2 . Eztn teleptsnk
mindent, amire szksg lehet a fordtshoz-csomagksztshez:
$ sudo apt-get install build-essential dpkg-dev
Lpjnk be a mappba:
$ cd akarmi-0.2
Debianizljuk a mappt:
$ dh _ make -s -c gpl -e en@email.nincsilyen -createorig
Ezzel elkszl egy debian mappa az akarmi-0.2 alatt, benne mindenfle megrtenival. A
README kezdet fjlokban benne avn, hogy mit kne rni beljk, trlhetjk ket. Sem az ex,
sem az EX vg fjlokra nincs szksg, s kivghatjuk a docs fjlt is. A control fjlba kerl
nhny, a felhasznlk szmra szl informci, de van r fontosabb dolog: ha a csomagunk
fgg egytl-mstl, az is ide kerl. Beszltnk mr arrl, hogy a Qt programknyvtraitl
most pp fggeni fogunk, de a berst megsszuk, mert automatikusan ki fog tltdni ez a
rsz is, ha a hasznlt Qt-unk hivatalos csomagokbl szrmazik. Ha a qt-project.org honlaprl
tltttk le a Qt-ot, akkor lvn az egy disztribcifggetlen vltozat erre nem sok esly
van. A copyright fjl nagyja a parancssori paramter miatt megvan, kiptolhatjuk benne, amit
szksges.
Utolsknt kiadhatjuk a
$ dpkg-buildpackage
parancsot. Kisvrtatva panaszkodni fog, hogy nem volt kulcs, amivel alrhatta volna a
fjlt, de attl mg megcsinlta az akarmi-0.2 mappval egy szinten. Hasznlhat, kedves
egszsgnkre.

133
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

18. Kirnduls a Qt Quickszigetekre (meg


a QML tengerbe)

Ennyi kemny munka utn itt az ideje, hogy bepakoljuk a frdrucit s a hrcsgt, s
elutazzunk. Szba se jhet Skandinvia, pedig (vagy pp azrt, mert) a Qt-nak sok kze van
hozz. Amikor a replnk a sarkon balra fordul, s bemondjk, hogy a Qt Quick-szigetek fltt
az g kiss zavaros, a hmrsklet forr, nhol kdszitlssal, sejtjk, hogy nem lesz egyszer
ez a nap sem, de sebaj, mert itt van kedvenc strandknyvnk, majd segt.
A Qt Quick, hasonlan a Qt-hoz, egy keretrendszer, kicsit ugyanaz, de inkbb mgis teljesen
ms. A magyarzatot a kijelents vgn kezdjk, az eleje meg taln kiderl a fejezet vgre.
A Qt egy rgi jl bevlt nyelvet, a C++-t terjeszti ki, tuprozza fel. A Qt Quick meg teljesen
j nyelvet kapott, a QML30-t, ami annyira hasonlt a C++-ra, mint a szilvalekvr a rsejbnire:
mind a kett kaja, de aki egytt tudja enni ket, annak ers a gyomra. A hagyomnyos,
widget-ekre alapul Qt tbb-kevsb ksz, radsul ma mr nem trendi a sz hagyomnyos
rtelmben vett szmtgpekre programot fejleszteni, programozsi krnyezetet mg
kevsb. gy a Qt hza tjn azt a Qt Quick-et fejlesztik ersen, amely jelen llapotban fknt
felhasznli felletek rsra j, s a jsnkn kvl senki nem tudja biztosan, hogy ez mindig
gy lesz-e. A Qt Quick fejlesztse e knyv rsval prhuzamosan is gyors nyelvi vltozsokkal
jr. A dokumentci szoks szerint a fejleszts nyomban kullog, s ez mg akkor is igaz, ha
olyan, meglehetsen korrektl dokumentlt projektrl van sz, mint a Qt.
Az internet teli van elavult pldkkal (amelyek mg a Qt 4 s a Qt Quick 1 korbl
szrmaznak), s a gyors fejldst mi sem illusztrlja jobban annl, hogy ebben a fejezetben
is lesz olyan pldaprogram, amihez nem elg a Qt 5.0 sem, hanem Qt 5.1 kell. Aki el akar
merlni a tmban, figyeljen oda, hogy Qt Quick 2.x-re pl pldkat nzegessen. A kt verzi
kztt nem csak nyelvi, hanem alapvet, bels motorbli klnbsgek is vannak: az 1.0 mg a
QPainter-rel rajzol, a 2.x pedig OpenGL-re pl.
A QML alkalmas a mobileszkzk elterjedsvel kvetelmnny vlt izg-mozg, zizeg-
zsezseg felhasznli felletek elksztsre. A nyelv deklaratv, azaz csak annyit kell benne
megmondani, hogy mi legyen, a hogyan egsz ritkn rdekes. Amit nem tud a QML, azt tudja
a JavaScript igen, mr itt is JavaScript , amit meg egyik sem tud, azt tudja a C++, meg a j
reg Qt a komolyabb httrfeladatok elvgzsre a QML nyelv programok is ket hasznljk.
Vannak, akik a widget-ekre pl Qt-ot lassan temetni kezdik, s azok szerint, akik meg
akarjk vigasztalni a gyszolkat, mg nhny vig biztos lesznek widget-ek. Nyilvn a piac
dnt majd: amg hasznljk a widget-eket, addig lesznek.

18.1. QML-programot runk


Ebben a rszben bemutatjuk, mibl pl fel egy tiszta QML-program. Szoks szerint nem
treksznk teljessgre, rszint, mert nincs r hely kln knyvet rne a dolog , rszint,
mert olyan gyors a fejlds, hogy erre a legersebb trekvs mellett sem volna esly, rszint
30 Az XML, az SQL s a QML kztt sok-sok klnbsg van, s alighanem a legkevsb fontos,
hogy a QML betsz jelentse nem tisztzott. A klnfle forrsokban tallkozni lehet pldul
Qt Markup Language, Qt Quick Markup Language, Qt Modelling Language s Qt Meta-
language varicival. Mindenki kivlaszthatja a neki leginkbb tetszt.

134
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv

pedig azrt, mert a kombincik szma vgtelen. A komoly projektek esetben eddig is kln
ember vagy rszleg foglalkozott a felhasznli fellet tervezsvel, de a Qt Quick eljvetelvel
ismt vrhat egy rtegszakma kialakulsa. Az j szakma kpviselinek a feladata sok
tekintetben a webdesignerek feladathoz lesz hasonl: a nyelv kpessgeinek pontos ismeretben
kpesek lesznek klnlegesebbnl klnlegesebb hatsok elrsre olyanokra, amelyekre a
nyelv fejleszti soha nem gondoltak volna. A Qt Quick igazn j hasznlata a hagyomnyos
rtelemben vett programozi szemllettl nmileg eltr logikt kvn.
QML-programot Qt Creatorban is rhatunk ha gy akarunk tenni, akkor most a
projektnyitskor felajnlott sok Quick-es lehetsg kzl a Qt Quick 2 UI lehetsget rdemes
vlasztanunk. Kt fjlt kapunk, de a projektfjl csak arra val, hogy egytt kezelhessk az
esetleges kpeket s JavaScript-fjlokat. Neknk egyelre ilyesmink nem lesz, lnyegben az az
egy qml-fjl lesz a programunk, amit megnyit a Qt Creator.
Akinek gy tartja kedve, dolgozhat egyszer szvegszerkesztvel is. A program rtelmezst-
futtatst fejleszts kzben legegyszerbben a qmlscene nev alkalmazssal vgeztetheti
a fejleszt az alkalmazs parancssori paramtere a fjl, amit futtatunk. A qmlscene az
alaprtelmezett Qt-teleptsek alkalmval a Qt Creatorral egytt kerl a gpnkre, azaz nem
kell kln levadsznunk valahonnan.
A QML-fjok mindig importlssal kezddnek:
import QtQuick 2.0

A rgebbieknl a verziszm 1.0 volt, a knyv rsakor a legjabb esetekben 2.1 is


elfordulhat.
A Qt Creator ltal felknlt sablon esetben a program tbbi rsze ilyen:
Rectangle {
width: 360
height: 360
Text {
anchors.centerIn: parent
text: "Hello World"
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}

Nzzk meg lpsrl lpsre. A program f eleme egy ngyszg (rectangle), amelynek
kt jellemzjt adjuk meg: a szlessgt s a magassgt. A programban a f elem kt
gyermekelemt is deklarljuk.
Az egyik egy szveg, aminek megint csak kt jellemzjt deklarljuk: az egyik maga a szveg,
a msik pedig az, hogy hova horgonyzand (anchor) a szveg. Esetnkben a szl (parent, a
ngyszgrl van sz) kzepre. A horgonyzs azrt remek eljrs, mert az objektumunk a szl
tmretezsekor is kzpen marad, ki sem lehet robbantani onnan (na j, ki lehet).
A msodik gyermekelem nem lthat tpus. Egrterletet deklarlunk vele, azaz olyan rszt,
ahol a program rzkeny az egrkattintgatsra. Az egrterlet horgonyzst gy vgezzk,

135
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

hogy a szl teljes terlett kitltse, azaz brhova kattinthatunk a ngyszgnkben. Ennek
a gyermekelemnek signal-jai is vannak (na vgre, csak kiderl, hogy Qt lapul a httrben), a
dokumentci31 szerint az egyik a clicked(). A slot-ot meg mr ltjuk is: az onClicked()
nevet viseli. Ha kattintunk, a program kilp.
les szemnkkel rgtn szrevesszk, hogy a pontosvesszket kukztk a nyelvbl, de nem
merszkedtek a Python egyszersgig32: a kdblokkok hatrt itt kapcsos zrjelek jelzik.
Az elemeknek nem adtunk nevet, ami azzal jr, hogy amit egyszer odatettnk, az ott
is marad, mgpedig gy, ahogy letettk. Ha nmi dinamizmust szeretnnk csempszni
a programunkba, akkor az egyes elemeket azonostval (id) kell elltnunk. Tekintsk a
kvetkez programot:
import QtQuick 2.0
import QtQuick.Controls 1.0

Rectangle {
id: rectangle
width: 360
height: 360
Button {
anchors.centerIn: parent
text: "push me"
onClicked: {
rectangle.color = "brown"
}
}
}

Ezttal nem egy szveg dekkol a ngyszg kzepn, hanem egy gomb (Button). Gombunk
vagy akkor lehet, ha leesett a nadrgrl, vagy importlnunk kell a vezrlket (Controls) is.
(Emlksznk mg, amikor j pr fejezettel ezeltt arrl elmlkedtnk, hogy miknt fordthat
a widget sz? Ott emltettk, hogy vannak krnyezetek, ahol az elemeket vezrlknek hvjk.
Ez esetben is kzenfekvnek tnik a vlaszts, mr csak azrt is, hogy ezeket az elemeket
vletlenl se keverjk ssze a Qt widget-eivel.) Sok vezrl ltezik mr a Qt Quick-ben, nzznk
csak krl a dokumentciban.
Ha mg nem tnt fel, most enyhe utalst tesznk arra, hogy az elemek neve nagybetvel
kezddik, cserbe a jellemzk kicsivel, nehogy elkopjon a Shift. Aki megprblt nagy
kezdbets id-t adni a ngyszgnek, az meg mr azt is tudja, hogy azt sem szabad. jabb
megfigyelnival, hogy nem mindig kettsponttal adunk rtket, mert a fejlesztk nem szerettk
volna megbntani az egyenlsgjelet.
A kvetkez vltozatban jabb ngyszget alaktunk ki (az import-sorokat nem rjuk ide
jra):

31 A Qt Creatorban a szoksos mdon, a megfelel szn llva F1-et nyomunk. Ha az internetet


jobb szeretjk, akkor a knyv rsakori legfrissebb dokumentci a http://qt-project.org/doc/qt-
5.1/qtdoc/qmltypes.html webhelyen olvashat.
32 A Python nyelvben a sorok behzsa nyelvi elem: a klnbz mlysg behzsokbl derl ki,
hogy meddig tart a kdblokk. Egymsba gyazott kdblokkok esetben a mlyebben lv na-
gyobb behzst kap.

136
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv

Rectangle {
id: rectangle
width: 360
height: 360
Rectangle{
width: parent.width / 2
height: parent.height / 3
color: "yellow"
anchors.centerIn: parent
Button {
anchors.centerIn: parent
text: "push me"
onClicked: {
rectangle.color = "brown"
}
}
}
}

Kt dolgot nzznk meg jl. Az egyik, hogy ezek szerint a vgtelensgig folytathat az elemek
egymsba gyazsa. A msik, hogy a QML l az gynevezett tulajdonsgkts, magyarn:
property binding hasznlatval. A bels ngyszg szlessge s magassga mindig a kls
adataihoz kpest lesz rtelmezett, azaz az alkalmazsunk vigyorogva tmretezhet.
Az utols tiszta QML-pldnk kvetkezik:
Rectangle {
id: rectangle
width: 360
height: 360
Rectangle{
id: innerRectangle
width: parent.width / 2
height: parent.height / 3
color: "yellow"
anchors.centerIn: parent
Button {
anchors.centerIn: parent
text: "push me"
onClicked: {
rectangle.color = "brown"
}
}
PropertyAnimation {
id: myAnimation
target: innerRectangle
property: "rotation"
from: 0
to: 360
duration: 5000
loops: Animation.Infinite
}
}
Component.onCompleted: myAnimation.start()
}

137
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

A fenti plda a QML rengeteg animcitpusa kzl


hasznlja az esetleg mr a j reg Qt-bl is ismert
PropertyAnimation-t (a Qt-ban QPropertyAnimation
osztly). Belltjuk, hogy mettl meddig tartson az
animci, meg, hogy hnyszor ismtldjk, s mehet is
a dolog. Kell azonban mg egy esemny, meg az slot-ja,
hogy indulhasson a buli. Mi a komponens esetnkben
az egsz QML program elkszlthez, kialaktsnak
vghez ktjk az animci indtst.
Ahogy grtk, a kvetkez pldban mr nem csak QML
lesz a qml-fjlban. Tesznk hozz nmi JavaScript-et is, egy
27. bra: A ferdn ll ngyszg
vletlenszm-elllt fggvny kpben: valjban forog, csak ugye az
llkp...

Rectangle {
id: rectangle
width: 360
height: 360
Rectangle{
id: innerRectangle
width: parent.width / getRandomInt(2, 4)
height: parent.height / 3
color: "yellow"
anchors.centerIn: parent
Button {
anchors.centerIn: parent
text: "push me"
onClicked: {
rectangle.color = "brown"
getRandomInt()
}
}
}
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}

A JavaScript elklnlse nem valami ltvnyos: az egyik sorban mg QML, aztn meg mr
nem, aztn meg mr megint lehetne, de nem lesz, mert vget r a fjl.
A kvetkez, s ebben az alfejezetben utols pldnkban bemutatjuk, hogy miknt helyezhet
az alkalmazs logikja, a belbecs kln JavaScript .js-fjlba, hogy a QML-ben csak a klcsn
maradjon. Ha van kedvnk, a JavaScript-fjlt a Qt Creatorral is elkszttethetjk, de kzzel is
a qml-fjl mell tehetjk. Tegyk bele az imnti fggvnyt egyszer msols-beilleszts ,
majd alaktsuk ilyenn a kdot:

138
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv

import QtQuick 2.0


import QtQuick.Controls 1.0
import "random.js" as RObject

Rectangle {
id: rectangle
width: 360
height: 360
Rectangle{
id: innerRectangle
width: parent.width / RObject.getRandomInt(2, 4)
height: parent.height / 3
color: "yellow"
anchors.centerIn: parent
Button {
anchors.centerIn: parent
text: "push me"
onClicked: {
rectangle.color = "brown"
getRandomInt()
}
}
}
}

Szndkosan rtuk ki jra az import-sorokat: a JavaScript-fjl az importls sorn nll


objektumm avanzsl, aminek radsul ktelezen nagy kezdbets a neve. Az j objektum
tagfggvnynek nevt hvskor a szoksos pont kti az objektum nevhez.

18.2. Oda-vissza Qt s Qt Quick kztt: gy beszlgetnek egymssal a


hibrid programok rszei
Ebben az alfejezetben egy olyan programot valstunk meg, amely egyik ablakban egy Qt
Quick-ben rt widget cscsl. A csacsogst termszetesen signal-okkal s slot-okkal vgzi a
kt rsz. A sikeres munkhoz most mindenkpp legalbb 5.1-es Qt-ra van szksgnk.
Kezdjnk j hagyomnyos Qt C++ projektet, amelynek nmileg megtveszten a Qml1 nevet
adjuk. A fablakban helyezznk el egy nyomgombot. Adjunk hozz egy, a QDialog osztlybl
szrmaztatott ablakot is is a projekthez (Emlksznk mg? Qt Designer Class a kulcssz.).
Kereszteljk Dialog nvre, a belle pldnyostott ablakot pedig a kvetkez slot nyitja meg:
void MainWindow::on_pushButton_clicked()
{
Dialog dialog;
dialog.exec();
}

Adjunk qmldialog.qml nven QML-fjlt a projektnkhz, s helyezzk el benne az albbi


kdot:

139
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

import QtQuick 2.1


import QtQuick.Controls 1.0

Rectangle{
id: main
width: 100
height: 100

CheckBox {
id: cb
text: "check me"
anchors.centerIn: parent
PropertyAnimation {
id: cbAnimation
target: cb
property: "rotation"
from: 0
to: 360
duration: 5000
loops: Animation.Infinite
}
}

Component.onCompleted: cbAnimation.start()

A kd elvileg mostanra knnyen megfejthet: egy CheckBox-ot prgetnk majd benne,


amely termszetesen prgs kzben is piplhat. A qml-fjlt helyezzk el egy ersforrs-
fjlba, hogy ne kelljen kzzel msolgatni a programmal egytt. Ha mindezzel megvagyunk,
trjnk vissza a Dialog osztlyhoz, s a felhasznli felletet alaktsuk ilyenre:
Az unchecked felirat egy QLabel,
csak j nagy betkkel. A nevn sokat
gondolkodtunk, majd a hrcsgt is bevonva
a tancskozsba, alapos megfontols utn
a label mellett dntttnk. A label
objektum alatt egy QCheckBox van,
amellyel majd gyorsabb prgst lehet
kapcsolni, s amit a hangzatos s a jellemt
jl tkrz checkBox nvvel indtottunk el
az let nagy orszgtjn.
Az ablak fels rszn lv nagy semmi
egy QFrame osztly, frame nev keret. 28. bra: A prbeszdablakunk
Abban a keretben fog majd futni a QML-kdunk.
A Dialog osztly kdjt szerkesztve a fejlcfjlban a mr megismert mdon helyezzk
el a duration nev tulajdonsg defincijt (a duration rtk az, amely a QML
PropertyAnimation animci sebessgt szablyozza: ezredmsodpercben adjuk meg, hogy
mennyi id alatt kell lejtszdnia az animcinak):
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY
durationChanged)

140
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv

Deklarljuk a privt tagvltozt:


int m_duration;

Deklarljuk a signal-t:
void durationChanged(int animationDuration);

Figyeljk meg, hogy szoksunktl eltren megadtuk a signal-ban lv vltoz nevt is


mskor csak a tpust szoktuk. Deklarljuk s valstsuk meg a kt publikus slot-ot:
int Dialog::duration() const
{
return m_duration;
}

void Dialog::setDuration(const int &d)


{
if(d != m_duration){
m_duration = d;
emit durationChanged(d);
}
}

Ha mindezzel megvagyunk, rtrhetnk a programunk lnyegi rszre, amely elssorban


a Dialog osztly konstruktorban helyezkedik majd el. rjuk be a konstruktorba az albbi
sorokat, aztn majd megbeszljk, hogy melyik mire j:
QQuickView *qView = new QQuickView();
QWidget *qmlScene = QWidget::createWindowContainer(qView, this);
QVBoxLayout *layout = new QVBoxLayout();
layout>addWidget(qmlScene);
ui>frame>setLayout(layout);
qView>setSource(QUrl("qrc:/stuff/qmldialog.qml"));
qView>setResizeMode(QQuickView::SizeRootObjectToView);
qView->show();

Kt fejlc is kelleni fog: a <QQuickView> s a <QVBoxLayout>. s akkor kvetkezhet a


magyarzat.
Az els sorban elssorban egy olyan objektumot pldnyostunk, amely alkalmas arra,
hogy hagyomnyos Qt programon bell futtathassunk Qt Quick alkalmazst. Figyeljk meg,
hogy ez is egy View, azaz nzet. A modellje maga Qt Quick program, illetve annak kimenete.
A msodik sorban szerepl statikus tagfggvny az, amelyik miatt 5.1-es Qt kellett. Ez a
tagfggvny alkalmas arra, hogy a QQuickView osztly, a QWidget osztly objektumok
kz nem illeszked, azok egymsba-pakolhatsgrl mit sem sejt objektumoknak egy olyan
tartalmazt, angolul container-t adjon, amely mr QWidget osztly, azaz el tudjuk helyezni a
hagyomnyos elemeink kztt.
A qmlScene objektumunkat remekl elhelyezhetnnk pldul a Dialog felletn, de a
mretnek szablyzsval gondjaink lennnek. Ltezik ugyan, s mi is hasznljuk pr sorral
lejjebb a setResizeMode() tagfggvnyt, de kzvetlenl a Dialog felletre helyezve a
qmlScene-t bajos volna a mret belltsa. gy aztn a QFrame osztly frame osztlyban
prbljuk elhelyezni az jdonslt widget-nket, de a QFrame osztly nem kpes widget-eket
kzvetlenl fogadni. Elrendezs (layout) viszont bellthat neki, s ezt kihasznljuk: elszr

141
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

pldnyostunk magunknak elrendezst, majd a qmlScene objektumunkat rhelyezzk


az elrendezsre. Vgl a frame objektum gondjaira bzzuk az elrendezst. A setLayout()
tagfggvny dokumentcija szerint a fggvnnyel belltott elrendezs a QFrame osztly
objektum tulajdonv vlik, ezrt nem trtk magunkat az elrendezs pldnyostsakor
szlobjektum megadsval.
Kszen ll ht az ablak, ami a QML-nyelv programot futtatja majd, gy a
QQuickView::setSource() tagfggvnnyel az erforrsfjlbl megadjuk a futtatand qml-
fjlt. Megadjuk, hogy a QML forrsban megadott gykrobjektum (a Rectangle) mrete
alakuljon t a QFrame, pontosabban a benne lv QVboxLayout mreteinek megfelelen. Ha
mindez megvan, akkor show()-time!
A programunk megyeget, pp csak azt nem tudja mg, amirt rni kezdtk: nem tud mg
beszlgetni egymssal a Qt s a Qt Quick. Elszr az oldjuk meg, hogy a Qt Quick rtesljn a
Qt mondanivaljrl.
Elsknt rjuk meg a faster rotation felirat objektum slot-jt:
void Dialog::on_checkBox_stateChanged(int arg1)
{
if(arg1 == Qt::Checked)
setDuration(500);
else
setDuration(5000);
}

A setDuration() tagfggvny hvsa ugye egytt jr a durationChanged() signal


emittlsval. Ezt a signal-t igyeksznk majd elkapni a Qt Quick-en bell. A Quick a
krnyezetbl rkez signal-okat figyeli, meg kell adnunk ht, hogy mi szmt a krnyezetnek.
A Dialog osztly konstruktorban, akr mg a QML-forrsfjl megadst megelzen
helyezzk el az albbi sort (meg a fjl elejre a <QQmlContext> fejlcet):
qView>rootContext()>setContextProperty("dialog", this);

Ezzel azt mondtuk meg a QQuickView osztly nzetnek, hogy a krnyezete maga a QDialog
osztly objektum (this), azaz az ebbl az objektumbl rkez signal-okat kell figyelnie.
Azt is belltjuk, hogy a krnyezetre "dialog" nven szeretnnk hivatkozni a QML-nyelv
programban.
A qml-fjlban kevs dolgunk volna, ha csak egy egyszer tulajdonsg rtkt szeretnnk
Qt-bl vltoztatni. A CheckBox szvegnek vltoztatgatsa annyival elintzhet lenne (j
megrt Q_PROPERTY-t felttelezve), hogy a text: jellemz rtkl dialog.text-et adunk
meg azonnal t is rdik. Neknk ez esetben nem elegend az animci duration jellemzjt
megvltoztatni, mert az a mr fut animcin nem vltoztat. (Ha ilyen esetekben nem rezzk a
nyelv deklaratv mivoltt teljesen konzekvensnek, nos, akkor...)
gy aztn a qml-fjl vgre, de mg az utols zr kapcsos zrjelt megelzen helyezzk el az
albbi blokkot:

142
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv

Connections {
target:dialog
onDurationChanged: {
cbAnimation.stop()
cbAnimation.duration = animationDuration
cbAnimation.start();
}
}

Ez a rsz felel meg a Qt connect utastsnak. Megadjuk, hogy kitl vrjuk a signal-t:
a dialog nev krnyezettl. A signal nevnek megfelel onDurationChanged: blokkba
helyezzk el a tennivalkat a slot-ok nevnek megvlasztsakor nem lvezzk azt a fajta
szabadsgot, mint a Qt-ban, ami taln nem is baj. Taln mg rmlik, hogy a Qt-ban a signal
deklarlsakor kivtelesen megadtuk a signal-ban lv vltoz nevt is. Ht, most ltjuk,
hogy mirt: gy tudunk r hivatkozni a QML-nyelv alkalmazsunkban. Termszetesen a
Connections blokkban tbb krnyezet tbb signal-jt is kezelhetnnk, de ht most neknk
csak ennyi van. Mindenesetre a program lefuttatsakor a Qt-ban lv checkBox piplsval
a Qt Quick-ben lv CheckBox forgsa mostantl gyorsthat s lassthat. Ha a hrcsg
szdls, a program kiprblsa eltt fordtsuk msfel a monitort.
Folytassuk azzal, hogy tudatjuk a nagyvilggal, hogy a qml-fjlban bujkl CheckBox-nak
milyenre vltozott az llapota. A CheckBox tulajdonsgai kztt (taln az anchors sor al, de
vgs soron mindegy) helyezzk el a kt kvetkez sort:
signal checkStateChanged(bool checked)
onClicked: cb.checkStateChanged(cb.checked)

A signal vltozjnak nevt ktelez megadnunk, br hasznlni nem fogjuk. A signal-t az


onClicked: slot-bl bocstjuk ki. s akkor mirt nem hasznljuk kzvetlenl a CheckBox
QML-tpus clicked() signal-jt? Mert abbl nem derl ki, hogy most pp odatette a
felhasznl a pipt, vagy kivette onnan.
Battyogjunk vissza a dialog.cpp fjlba, s kicsit trjk a fejnket. A connect utastsnak
kelleni fog egy slot is, ezt gyorsan megrjuk:
void Dialog::onQmlCheckBoxStateChanged(bool checked)
{
if(checked)
ui>label>setText("checked");
else
ui>label>setText("unchecked");
}

De kelleni fog neki egy QObject is, amibl jn a signal. Ilyen honnan vadsszunk? Ht, majd
krnk a nzettl (de eltte elhelyezzk a fjl elejn a <QQuickItem> fejlcet)!
QObject *qmlRoot = qView>rootObject();

Ilyen egyszer? Nem, persze nem. A nzet visszaadja neknk a gykrobjektumt, ami a
QML-ben lv Rectangle. Neknk viszont a CheckBox kell, az elz gyermekobjektuma.
Szerencsre a QObject-ek gyermekei jl nevelt klykk: ha mondod a nevket, eljnnek. Ha
van nevk, ami nem azonos az ket trol vltoznvvel. Kullogjunk vissza a qml-fjlba, s a
CheckBox elemnek adjunk nevet az

143
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...

objectName: cbName"

utastssal. les helyzetben alighanem okos gondolat nvknt s id-knt azonos


karakterlncot megadni, de ezttal hangslyozni szeretnnk, hogy id s objectName nem
ugyanaz. A boldog keresztel utn slattyogjunk vissza ismt a dialog.cpp-be, s az elz sor
al helyezzk el a kvetkez kt utastst:
QObject *qmlCheckBox = qmlRoot>findChild<QObject* >(cbName");
connect(qmlCheckBox, SIGNAL(checkStateChanged(bool)),
this, SLOT(onQmlCheckBoxStateChanged(bool)));

Az els elszltja neknk a gyermeket,


akinek immron van neve, a msodik meg
kialaktja a vrva vrt kapcsolatot signal s
slot kztt.
A programunk mkdik, s lassabb
egerszeknek kihvst jelenthet a gyorsan
forg CheckBox-ba pipt tenni.

29. bra: Vaku nlkl sem mozdult be a kp!

144
Bcs Qt strandknyv

Bcs
Ennyi egytt tlttt oldal utn bcst vesz egymstl Olvas, hrcsg s r. Az r remnyt
fejezi ki, hogy a knyv elolvassa s a benne lv programok megrsa sorn az Olvas nem
csak szebb lett, de okosabb is. Az Olvas alighanem hasonl remnyeket tpll, s mind a ketten
tplljk a hrcsgt: hol nmi zldsggel, hol dival, hol citromos npolyival. gy szp az let,
igaz?

145
Qt strandknyv Szszedet

Milyen osztlyokrl, makrkrl s egyb


szpsgekrl van sz a knyvben?
B QGraphicsTextItem 58
QGraphicsView 53, 72
Button 138, 139, 140
QHash 55, 56
C QItemSelectionModel 79
QItemSelectionModell 79, 128
Component 140
QLabel 11, 12, 13, 14, 18, 20, 23, 68, 75
Connections 146
QLinearGradient 57
M QLineEdit 15, 20, 23, 47, 52, 75, 97, 98
QList 24, 37, 38, 40, 41, 51, 76
moc 7, 15, 90
QListView 72, 73, 75
MouseArea 137
QListWidget 20, 21, 22, 24, 25, 34, 39, 42, 44,
P 52, 72
QListWidgetItem 24, 25, 27, 45
PropertyAnimation 140
QMap 15, 17, 18, 32, 55, 56, 57
Q QMapIterator 58
QMessageBox 28, 31, 92
QAbstractItemModel 75
QMutex 112
QAbstractProxyModel 100
QMutexLocker 112
QAction 28, 50, 59, 60, 62, 63, 74, 75, 89
QNetworkAccessManager 104, 105
QApplication 10, 47
QNetworkReply 105, 106, 109, 111, 113
QBrush 57
QNetworkRequest 106, 111
QByteArray 107
QObject 16, 29, 37, 53, 70, 75, 87, 115, 146
QCheckBox 68
Q_OBJECT 15
QColor 57
QPair 83, 84
QCompleter 46, 48, 53, 72, 99, 132
QPonitF 58
QCoreApplication 49, 61, 85
Q_PROPERTY 92, 93, 144, 145
QDateTime 108, 110
QPushButton 11, 12, 13, 20, 39, 66, 74
QDebug 15, 16
QQuickView 144, 145
QDialog 70
QSettings 49, 50, 69, 70, 85, 86
QDir 29
QSharedPointer 37, 39, 40, 41, 83
QDomDocument 120, 121
QSortFilterProxyModel 100
QDomElement 120, 121
QSpinBox 68
QDomNode 121
QSqlDatabase 126, 127
QEventLoop 112, 113
QSqlField 131
QFile 31, 32, 107
QSqlQuery 126
QFileDevice 32
QSqlQueryModel 131, 132
QFileDialog 28, 29, 30, 33, 120
QSqlRecord 130
QFileInfo 29, 30, 108, 109, 110
QSqlTableModel 123, 124, 125, 127, 131
QFont 57
QSringList 32
QFrame 144
QStack 83, 84
QGraphicsRectItem 58
QStandardItem 75, 76, 78, 121
QGraphicsScene 54, 72

146
Szszedet Qt strandknyv

QStandardItemModel 75, 76, 78, 97, 99 R


QStatusBar 18
Rectangle 137, 138, 139, 140
QString 13, 15, 17, 18, 22, 28, 37, 38, 55, 56, 83,
84, 116 S
QStringList 27, 28, 32, 33, 34, 35, 37, 38, 40, 41,
SIGNAL() 17
46, 49, 53
SLOT() 17
QStringListModel 49, 72
static_cast 98
QStyledItemDelegate 97
QTableView 73, 74, 78 T
QTabWidget 53, 75
Text 137
QTextBrowser 64, 66, 67
QTextCodec 18
QTextEditor 66, 67
QTextStream 31, 121
QThread 112, 114, 115
QTimer 67, 70
QToolBar 62
QTranslator 60
Q_UNUSED 29, 98, 99
QVariant 50, 69
QVboxLayout 145
QVBoxLayout 144
QWebView 67
QWidget 26, 29, 47, 66, 87, 97, 98, 144

147
ISBN 9 7 8 9 6 3 0 8 8 2 5 5 2

Nemzeti Fejlesztsi gynksg


www.ujszechenyiterv.gov.hu
06 40 638 638

A projekt az Eurpai Uni tmogatsval, az Eurpai


Regionlis Fejlesztsi Alap trsfinanszrozsval valsul meg.

You might also like