Professional Documents
Culture Documents
QT Programozás Könyv PDF
QT Programozás Könyv PDF
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
3
Qt strandknyv Tartalomjegyzk
4
Tartalomjegyzk Qt strandknyv
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
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.
1 http://qt-project.org/wiki/Category:LanguageBindings
7
Qt strandknyv Nhny egyb tudnival
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.
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
return a.exec();
}
9
Qt strandknyv 2. Els programunk. Igen, a "Hell vilg!"...
10
2. Els programunk. Igen, a "Hell vilg!"... Qt strandknyv
11
Qt strandknyv 2. Els programunk. Igen, a "Hell vilg!"...
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_3_clicked();
private:
Ui::MainWindow *ui;
};
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
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;
}
14
3. Sajt signal s slot megvalstsa Qt strandknyv
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!
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!");
}
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
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"));
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:
17
Qt strandknyv 4. Akinek nincs esze...
Ha most egy kicsit tmretezzk az ablakot, az eredmny eszttikai rtke ersen korltozott
lesz.
18
4. Akinek nincs esze... Qt strandknyv
19
Qt strandknyv 4. Akinek nincs esze...
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
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();
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()));
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());
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
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());
23
Qt strandknyv 5. Mentsk, ami menthet!
van
nem
troljuk a fjlnevet
egy vltozban hagyjuk a francba
ments ksz
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
25
Qt strandknyv 5. Mentsk, ami menthet!
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
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);
}
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
29
Qt strandknyv 5. Mentsk, ami menthet!
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
31
Qt strandknyv 5. Mentsk, ami menthet!
fileName = ";
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.
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());
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
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.
34
6. A feladatokhoz kategrikat rendelnk Qt strandknyv
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;
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;
}
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.
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());
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());
}
38
6. A feladatokhoz kategrikat rendelnk Qt strandknyv
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);
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()));
40
7. Mdosthat feladatok, ... Qt strandknyv
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
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, ...
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;
}
QStringListModel *completerModel;
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();
46
7. Mdosthat feladatok, ... Qt strandknyv
47
Qt strandknyv 7. Mdosthat feladatok, ...
48
8. Keress, grafikon s tbbnyelvsg Qt strandknyv
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);
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
50
8. Keress, grafikon s tbbnyelvsg Qt strandknyv
QGraphicsScene *scene;
51
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg
52
8. Keress, grafikon s tbbnyelvsg Qt strandknyv
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
QFont font("Serif");
font.setWeight(QFont::Bold);
QColor startColor(145,232,66);
QColor endColor(210,255,82);
QColor textColor(109,0,25);
54
8. Keress, grafikon s tbbnyelvsg Qt strandknyv
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();
55
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg
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
TRANSLATIONS = ToDoList_hu.ts
57
Qt strandknyv 8. Keress, grafikon s tbbnyelvsg
58
9. Ikonok, ablakok, automatikus ments Qt strandknyv
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.
60
9. Ikonok, ablakok, automatikus ments Qt strandknyv
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;
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,
63
Qt strandknyv 9. Ikonok, ablakok, automatikus ments
64
9. Ikonok, ablakok, automatikus ments Qt strandknyv
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());
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();
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
69
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...
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
71
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...
AboutWindow aboutWindow;
aboutWindow.exec();
72
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv
73
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...
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:
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*)));
75
Qt strandknyv 10. komolyan hasznlni kezdjk a ModelView...
utastst.
A fablak preparationAtStartUp() tagfggvnynek trzsben negyedik sorknt
mutassuk be a kijellsmodellt a nzetnek:
ui>tableView>setSelectionModel(modelManager>toDoSelectionModel);
76
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv
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()));
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);
}
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);
}
}
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()));
80
10. komolyan hasznlni kezdjk a ModelView... Qt strandknyv
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...
autoSaveSpinBox
zz
numberedRowsCheckBox
zz
buttonOk
zz
24. bra: A Settings ablak
buttonCancel
zz
82
11. Adatments s -visszatlts... Qt strandknyv
83
Qt strandknyv 11. Adatments s -visszatlts...
SettingsWindow settingsWindow;
if(settingsWindow.exec() == QDialog::Accepted)
restoreApplicationState();
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();
}
85
Qt strandknyv 11. Adatments s -visszatlts...
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
void FileOperator::onActionSaveAsTriggered()
{
fileSave();
}
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;
}
88
11. Adatments s -visszatlts... Qt strandknyv
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);
}
24 Olvasnival: http://qt-project.org/doc/qt-5.1/qtcore/properties.html
89
Qt strandknyv 11. Adatments s -visszatlts...
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);
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);
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");
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.
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()));
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
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.
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 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
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);
96
12. A modell, a deleglt, meg a proxymodell Qt strandknyv
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...
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.
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);
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);
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
101
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...
102
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv
QNetworkRequest request(URL);
request.setHeader(QNetworkRequest::ContentTypeHeader,
"multipart/form-data; boundary=margin");
request.setHeader(QNetworkRequest::ContentLengthHeader,
QString::number(array.length()));
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;
}
}
103
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...
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;
104
13. Hlzati szinkronizci: a ToDoList... Qt strandknyv
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)));
void syncReport(QString);
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);
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
107
Qt strandknyv 13. Hlzati szinkronizci: a ToDoList...
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;
}
108
14. Tbbszl programot runk Qt strandknyv
109
Qt strandknyv 14. Tbbszl programot runk
110
14. Tbbszl programot runk Qt strandknyv
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;
111
Qt strandknyv 14. Tbbszl programot runk
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:
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 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());
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);
114
14. Tbbszl programot runk Qt strandknyv
115
Qt strandknyv 15. XML-fjlba mentjk a feladatlistt
116
15. XML-fjlba mentjk a feladatlistt Qt strandknyv
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);
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.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
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
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();
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
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
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();
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();
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;
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);
126
16. A ToDoList, mint SQL-kliens Qt strandknyv
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);
}
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);
}
}
}
}
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);
}
}
Most jutottunk el arra a pontra, hogy az els fl ksz gy aztn a keresfllel folytatjuk a
munkt.
QSqlQueryModel *searchModel;
129
Qt strandknyv 16. A ToDoList, mint SQL-kliens
ui>searchView>setModel(tableManager>searchModel);
ui>searchView>horizontalHeader()>setSectionResizeMode(QHeaderView::
Stretch);
illetve
QSqlQueryModel *completerModel;
130
16. A ToDoList, mint SQL-kliens Qt strandknyv
131
Qt strandknyv 17. Egyszer Qt program csomagolsa...
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
133
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...
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.
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
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):
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...
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
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()
}
}
}
}
139
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...
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()
140
18. Kirnduls a Qt Quick-szigetekre... Qt strandknyv
Deklarljuk a signal-t:
void durationChanged(int animationDuration);
141
Qt strandknyv 18. Kirnduls a Qt Quick-szigetekre...
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)
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"
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
146
Szszedet Qt strandknyv
147
ISBN 9 7 8 9 6 3 0 8 8 2 5 5 2