spájame
slovenskú
IT komunitu
pridaj sa
Registrácia · Login

Peter Bočan 13.9.2012
Hodnoť článok:
3 0

QtGui - Hello World

Začíname s toľko vytúženými GUI aplikáciami v Qt frameworku, kde si napíšeme jednoduché Hello World...

Úvodom

Tak nakoniec je tu aj to vytúžený QtGui seriál, kde sa budem zameriavať na predovšetkým GUI a jeho rozširovanie a úpravu. QtGui bude akýsi “namespace“ pre rôzne podtutoriály, ktoré sa budú zaoberať tvorbou postupne zložitejších príkladov. Dnes si vytvoríme typický Hello World projekt s popiskom a tlačidlom, ktorému pridáme jednoduchú funkcionalitu.

Vytvárame projekt

Pre náš prvý projekt budeme potrebovať prázdny projekt no v budúcom, už si vystačíme s projektom, ktorý bude mať vygenerované súbory. Preto si tento raz vytvorte prázdny projekt (Other Projects > Empty Qt Project). Otvorí sa nám projektový súbor.

Hello World - Editujeme projektový súbor

Projektový súbor (s príponou .pro) obsahuje jednoduchú syntax, je to totiž iba Makefile súbor, takže budeme používať podobnú syntax ku Makefile, tá sa volá QMAKE a je to univerzálny Makefile, ktorý sa neskôr použije na to, aby sa vygeneroval potrebný Makefile pre danú platformu a kompilátor.
Ale najprv malé upozornenie: dávajte pozor na operátory, jedným znakom rovná sa prepisujete hodnoty daných premenných, čo pri niektorých premenných ako napr. CONFIG vyústi do zle vygenerovaných Makefile a teda sa neskompiluje ani váš program.
Pretože budeme potrebovať vygenerovať GUI aplikáciu, budeme to musieť nejako povedať QMAKE. Na túto potrebu existuje premenná TEMPLATE, tá určuje, či to bude aplikácia alebo knižnica. Preto do súboru napíšeme:

TEMPLATE = app

To nám zaručí, že výstupom bude aplikácia. Ďalej určíme názov aplikácie – teda ako sa bude volať EXE/APP súbor, na to slúži premenná TARGET:

TARGET = Demo

A nakoniec určíme, ktoré moduly Qt budeme potrebovať, pre túto potrebu slúži premenná QT, ktorá obsahuje zoznam (list) potrebných modulov. Nám bude na začiatok stačiť modul QtCore a QtGui, preto napíšeme:

QT += core gui

To nám bude na začiatok úplne stačiť, projektový súbor môžete zatvoriť. Teraz si pridajte nový C++ súbor, napr. main.cpp a ideme sa venovať našemu prvému GUI programovaniu.

Hello world!

Takže predtým, než si vytvoríme nejaké okno, musíme vytvoriť najprv premennú, ktorá viacej komunikuje so systémom a obstaráva funkcie, ako napr. hlavnú slučku programu, odchytávanie eventov atď. Táto premenná, bude typom trieda (od teraz väčšina premenných budú objekty), presnejšie trieda QApplication. Táto trieda sa nachádza v module QtGui a je postavená na triede QCoreApplication – rozdiel medzi týmito dvomi triedami je v tom, že QCoreApplication neumožňuje vytvárať GUI. Teda predtým, než budeme vytvárať akékoľvek UI, musí byť inicializovaná trieda QApplication.

Ako vždy a všade, i tu bude vstupným bodom do aplikácie funkcia main, ktorá bude zaobstarávať už našu aplikáciu

#include <QtGui/QApplication>

int main(int argc, char **argv)
{
QApplication application(argc, argv);

}

To ale nestačí, pretože funkcia main, vracia celočíselný typ – preto musí vrátiť stav, ako sa ukončil program. Túto funkciu zastáva metóda exec. Preto funkcia main, bude na poslednom riadku vracať:

return application.exec();

A čo teraz? Vytvoríme si okno, na to budeme potrebovať ďalšiu triedu, ktorá zaobstaráva prácu s oknami, máme možnosť z QMainWindow, QWidget, QDialog. Kde sa QDialog a QMainWindow odvíjajú priamo od triedy QWidget.
QWidget trieda ale nie je okno v pravom slova zmysle, je to len nešpecifikovaný grafický objekt, ktorý, ak nemá grafického rodiča, bude systémom „obalený“ rámom okna.

(Pozn.: Systém Windows sa volá preto po oknách, lebo je tvorený oknami – aj pracovná plocha je v podstate zvláštny typ okna, navyše veľa grafických prvkov sa vytvára pomocou funkcie CreateWindowEx – „morbídna“ funkcia o 12 parametroch)

Túto triedu neskôr použijeme na vytvorenie vlastných, špecifických widgetov. Nám momentálne bude stačiť trieda QWidget, ako naše prvé okno:

#include <QtGui/QApplication>
#include <QtGui/QWidget>

int main(int argc, char **argv)
{
QApplication application(argc, argv);
QWidget window;
window.show();
return application.exec();
}

Ak si to skompilujete a spustíte, dostanete dosť „hlúpe“ okno. Vložme doň popisok (label) a tlačidlo (push button).

Pridávame popisok – QLabel

Na začiatok si rutinne naincludujeme hlavičkový súbor QLabel a pridáme si QLabel je jednoduchá trieda, ktorá zabezpečuje zobrazovanie „prostého“ textu do aplikácie:

QLabel label("Hello cruel world!");
label.show();

Dve okná - rodičom je pracovná plocha

Po skompilovaní programu na vás vybehnú 2 okná – v jednom je náš label, v druhom naše okno, to sme teda nechceli. Kde sme teda urobili problém? Nikde. Unikol nám ale malý detail, ktorý, ako som spomínal vyššie, že pri triedach grafických prvkov, treba nastaviť rodiča, rodič je podstatná vec ako grafické prvky „vnárať“ do seba:

QLabel label("Hello cruel world!", &window)
// alebo:
label.setParent(&window);

A pokiaľ sa vám nepáči, ako je umiestnený náš popisok, stačí ho „premiestniť“ metódou move, povedzme o 10 a 10 px.

label.move(10, 10);

QPushButton

Tak ako sme si vytvorili náš prvý popisok , tak pridáme aj naše prvé tlačidlo, a pridáme mu funkcionalitu – zatvorenie okna a tým aj aplikácie. Preto si naincludujeme triedu QPushButton a vytvoríme premennú, povedzme quitButton a hneď konštruktoru predáme text – popisok, ktorý sa ukáže na tlačidle a rodiča, tak ako pri premennej label.

QPushButton quitButton("Quit", &window);

Opäť ho môžete posunúť, pomocou metódy move a teraz pridáme tomuto tlačidlu funkčnosť – ukončenie aplikácie.

Jedinečnosť Qt Frameworku – Signály a Sloty

Signály a Sloty – jedinečný callback systém tohto frameworku nám umožňuje využívať bezpečný callback systém, bez toho aby sme museli akokoľvek manipulovať s ukazovateľmi alebo funkciami a predávať ich priamo. Na to slúži metóda connect, ktorá je implementovaná v triede QObject.

Táto metóda požaduje po vás tieto 4 parametre:

  • Adresu na objekt, ktorý pošle signál (napr. Button)
  • Signál objektu, teda metódu, ktorá sa vyvolá, keď sa niečo stane, napríklad, keď sa tlačidlo klikne, stlačí alebo posuvník zmení svoju pozíciu, a i.
  • Adresu na objekt, ktorý bude odchytávať tento signál (Application)
  • Metódu (tzv. slot), ktorá bude niečo robiť s programom alebo oknom

A teda to môžeme zapísať takto:

quitButton.connect(
&quitButton,        // objekt odosielateľa, ktorý obsahuje špecifický signál
SIGNAL(pressed()),  // určíme signál odosielateľa
&application,       // objekt „reciever“, ktorý obsahuje slot
SLOT(quit())        // určíme špecifický slot „recievera“
);

Ako si môžete všimnúť, v tomto prípade volám metódu connect cez naše tlačidlo, ktorému predávam adresu na tlačidlo, kde ako signál predávam pressed() a tým hovorím, že ak sa stlačí tlačidlo quitButton vtedy sa vyvolá slot quit(), ktorý je v našej premennej application. Chcem upozorniť na to, že signály a sloty sa trieda od triedy líšia (napr. trieda QLabel neobsahuje slot quit()) a na nutnosť používať makrá SIGNAL() a SLOT(), ktorá odpadne s príchodom verzie Qt 5.0.
Ale je to zbytočné a zmätočné, aby sme cez naše tlačidlo quitButton, predávali adresu na seba samého. Preto použijeme metódu connect v inom zmysle – povieme aplikácii: ak sa vyvolá signál pressed() v objekte quitButton, potom skonči. Na to sa používa preťažená metóda connect len s troma parametrami:

  • Odosielateľ – v našom prípade tlačidlo
  • Signál odosielateľa
  • Slot, ktorý obsahuje trieda QApplication (teda našej premennej application)

A teda to prepíšeme ako:

application.connect(&quitButton,
SIGNAL(pressed()),
SLOT(quit()));

Skompilujeme to a uvidíme, že náš prvý projekt - Hello World je hotový.

Hotový Hello World Projekt

Qt 5 - Signály a Sloty

A keďže je už v podstate predvečer vydania verzie Qt 5.0 (momentálne dostupná Beta), ktorá umožňuje novú syntax, navyše implementovať novinky štandardu C++0x:
Vynechať makrá SIGNAL a SLOT, namiesto toho predať adresu signálu a slotu:

application.connect(&quitButton,
&QPushButton::pressed
&QApplication::quit);

Alebo využiť novinku zo štandardu C++0x – bezmenné lambda funkcie:

QObject::connect(&quitButton,
&QPushButton::pressed,
[] () {
// lambda funkcia – od Qt 5.0
// rob niečo!...
});

Tak si nakoniec zrekapitulujme čo všetko sme dnes napísali, tu uvediem celý funkčný kód:

HelloWorld.pro:

TEMPLATE = app
TARGET = HelloWorld
QT += core gui
SOURCES += main.cpp

main.cpp

#include <QtGui/QApplication>
#include <QtGui/QPushButton>
#include <QtGui/QWidget>
#include <QtGui/QLabel>

int main (int argc, char* argv[])
{
QApplication application(argc, argv);
QWidget window;

QLabel label("Hello cruel world!", &window);
label.move(10, 10);

QPushButton quitButton("Quit", &window);
quitButton.move(50, 40);

application.connect(
&quitButton,
SIGNAL(pressed()),
SLOT(quit()));

window.show();
return application.exec();
}

Záverom

V ďalších častiach sa vrhneme na ďalšie widgety a jednoduché projekty, na ktorých budem demonštrovať Qt framework a/alebo jeho iné komponenty.

Peter Bočan Peter Bočan

O mne? Zde: http://about.me/peter.bocan


Hodnoť článok:
3 0

22 komentárov k článku:

Komentovať môžu iba prihlásení

Zaregistruj sa cez bezplatnú registráciu alebo použi login cez Facebook (FB Connect)

Prihlás sa tu, ak už máš profil na Zajtra.sk:


Zabudol som heslo

0 0 Peter Bočan 5.10.2012 16:27:34
Ach jó, pondelok budú dve časti...
0 0 Miloš 4.10.2012 21:46:13
Zrejme si dosť vyťažený, kedy plánuješ sľúbenú novú časť?
0 0 Miloš 1.10.2012 21:05:21
Nepochopil si ma. Nehovorím, že open source sa zruší. Hovorím, že pokiaľ chceš všetko čo najnovšie, ale ako open source, tak to už nie je v jednom balíku ako kedysi, ale musíš stiahnuť každý balík zvlášť. Ak to vyznelo tak, že som to pochopil tak, že open source skončilo, tak sa ospravedlňujem.
0 0 Peter Bočan 1.10.2012 16:27:10
Hej chlape, Digia ponúka platenú/komerčnú od roku 2009, kde im to prepustila Nokia. Qt framework je odvždy delený na tieto dve licenčné jednotky - komerčnú a free-ware/(L)GPL. Na stránke Digie máš možnosť vyskúšať si práve túto platenú verziu, open-source sa nezruší a nebude zrušená v krátkej budúcnosti. Preto by si dovolím tvrdiť, že by si mal viacej čítať, než hneď vytvárať hoaxy ;)...
0 0 Miloš 1.10.2012 13:02:04
Možnosť stiahnuť platené Qt 4.8.3:
http://qt.digia.com/Try-Qt-Now/

Neplatené Qt:
http://qt-project.org/downloads
Tu sa dá stiahnuť:
- celé Qt SDK 1.2.1 (Qt 4.8.1, Creator 2.4.1) - ešte od Nokie
- samostatné Qt 4.8.3
- samostatný Creator 2.5.2

Pokiaľ chceš aj Qt Designer, tak musíš stiahnuť staršie SDK a ak chceš, doňho musíš doplniť nové Qt, prípadne Creator z iného balíka. V samostatnom balíku si to už nestiahneš komplet. Designer je zrejme nový len v platenej verzii. Pokiaľ sa mýlim, tak ma oprav.
0 0 Peter Bočan 30.9.2012 13:46:13
Vážne? Môžeš mi prosím poslať odkaz na článok?...
0 0 Miloš 29.9.2012 12:27:13
Peter: Len čo je horšie, že len Frameworku. Qt Creator aj s Qt Designerom si v novej verzii už nestiahneš. Navyše Qt Creator a QT Framwwork musíš ťahať zvlášť, pokiaľ chceš novú verziu.
0 0 Peter Bočan 27.9.2012 17:14:35
@Miloš, Digia podpísala s Nokiou kontrakt a dohodu, kde sa zaviazali, že Digia bude aj naďalej poskytovať Open-Source verziu frameworku, takže niet sa čoho báť.
0 0 Miloš 25.9.2012 22:47:21
Peter: Nie, milión objektov nechcem, len obyčajnú bitmapu a ten príklad ukazuje práve tie 2D objekty.

Pred týždňom som bol na Qt stránke, že či je novšia verzia než tá čo mám už stiahnutú (ale nenainštalovanú) a nebola. Dnes som pozrel na Qt stránku a skoro mi oči vypadli. Qt je už len 30 dňový trial, inak je platené. Ľudia sa báli, že Nokia pochová Qt, ale ako vidím, pochová ho práve Digia. Sú oni normálni? Jediný poriadny multiplatformový C++ framework a oni ho pochovajú. Nemám slov.
0 0 Peter Bočan 23.9.2012 21:40:01
Vyskúšam to. Čo sa týka tvojej otázky ohľadom milióna objektov - nereálne, nielen neskutočná záťaž na procesor a RAM ale aj nereálnosť mať milión prvkov na ploche - nikdy v živote nebudeš potrebovať vidieť toľko grafických prvkov na 2D ploche. Ak narážaš na 3D hry - tak tam je to pri načítaní "levela" načítané a pri ukončení zase z pamäte vypustené, nedrží sa všetko v RAM. Pochybujem, žeby si mal na ploche viac než 1000 objektov, vtedy hovorme už o nejakom nútenom zlepšení algoritmu...

Čo sa týka pamätanie a kreslenie len nejakého výsledného objektu, dá sa to. Je to jednoduché, ale vydrž. Zajtra príde ďalšia časť...
0 0 Miloš 22.9.2012 07:01:33
Peter: Nie, inak. Daj si v Qt prázdnu Qt aplikáciu (žiadny súbor okrem projektového) a vlož do nového cpp súboru kód zo stránky. Nakreslí sa. Teraz zavri všetko a otvor si nový Qt projekt a vlož v dizajnéri QGraphicsView. No a teraz uspôsob ten kód z tej stránky, aby to fungovalo. No a ja chcem vedieť, že čo trebalo urobiť inak než v tom čistom projekte, aby to fungovalo aj v prípade použitia dizajnéra.

No a druhá vec. V tom kóde zjavne vkladám do scény nejaké grafické objekty. Výsledok je takýto:

Scéna:
- elipsa
- obdĺžnik
- ...

No a ja chcem toto:

Scéna:
- bitmapa

Ide o to, že keď vkladáš do bitmapy, tak si pamätá program len bitmapu, ale ak vkladáš objekty, pamätá si objekty. Ako rýchlo sa zobrazí nakreslený obrázok, ak si program bude pamätať len výsledný bitmapu a ako rýchlo, ak si bude pamätať 1000000 objektov? A to nehovorím o rozdiele pamäťovej náročnosti bitmapy 800x600 a pamäťovej náročnosti scény 800x600 s 50000 objektami. Prvé bude zaberať cca 150 kB a druhé môže zaberať od 400 kB (pri bodoch so súradnicami typu int (4 B na každý bod X a Y) a prí náročnejších objektoch elipsa, obdĺžnik aj rádovo viac.

Takže moja otázka: Ako v Qt kresliť čistú bitmapu (nemusí si pamätať vkladané objekty, len výsledný obrázok). Samozrejme na diel seriálu o tomto si rád počkám a cením si, že niekto sa pustil do predstavenia tak zaujímavého frameworku ako je Qt.
0 0 Peter Bočan 17.9.2012 16:36:12
Ak ťa teda rozumiem správne, kreslíš pomocou C++ v nejakej metóde v nejakom objekte a ty chceš, aby sa to zobrazilo v dizajnéri?
0 0 Miloš 17.9.2012 00:23:45
Tak nič, nekreslí to. Skúsil som vytvoriť čistý projekt Qt a doňho vložiť kód z uvedenej stránky a nakreslilo peknú čiernu kružnicu na biele pozadie. Ani farby som nemusel nastavovať. Takže neviem, kde to viazne. Keď budeš písať o grafike, napíš vždy aj o tom, aby by takí kód vyzeral, keby je písaný pre objekty vytvorené v Qt Designeri. V určitých veciach sú tam odlišnosti a zrejme pre nejakú odlišnosť mi ani ten kód nefunguje.
0 0 Miloš 16.9.2012 22:56:42
Peter: A ešte sa ťa spýtam: Toto mi príde ako vkladanie objektov do grafickej scény (blobo povedané akoby vektorová grafika). Je aj nejaký spôsob ako skutočne kresliť a nie vkladať objekty? Niečo v zmysle:

ui->graphicsView->line(0, 0, 50, 50);

A grafická scéna by si pamätala bitmapu?
0 0 Peter Bočan 16.9.2012 18:53:49
@Miloš si náročný na otázky ;) ... ale to je dobre, neprestať sa pýtať... Takže, postupne:

1. Takže ty si chcel použiť OpenGL ako externú knižnicu, a nie QtOpenGL, áno aj to sa dá, je to jednoduché, len si poprosím čas to všetko spísať, dať tomu štábnu kultúru, hlavu a pätu a postupne to sem púšťať.

2. Kresliť na hlavné okno? Ak narážaš na "custom" okná, aj k tomu dôjdem, neboj, bude sa aj kresliť, len daj všetkému čas :).
Skús nastaviť farbu štetca/pera:
item->setPen(QPen(QColor("red")));
item->setBrush(QBrush(QColor("black")));
0 0 Miloš 16.9.2012 16:45:07
A keď už som pri grafike. Chcel by som sa spýtať, čo robím zle? Mám cez Designera nahodený GraphicsView s rozmermi 200x200 a doňho cez program kreslím:

QGraphicsScene scene;
scene.setSceneRect(0.0, 0.0, 200.0, 200.0 );

QGraphicsEllipseItem *item = new QGraphicsEllipseItem( 0, &scene );
item->setRect( 50.0, 50.0, 100.0, 100.0 );

ui->graphicsView->setScene(&scene);

ui->graphicsView->setRenderHints( QPainter::Antialiasing );
ui->graphicsView->show();

Elipsu (resp. kružnicu) nikde nevidím. Že by bola biela a teda ju na bielom pozadí nevidieť? Skúsim pohľadať, ako sa mení jej farba.

Je to mierne upravený kód zo stránky:
http://www.qtcentre.org/threads/3149-Examples-of-QGraphicsView
0 0 Miloš 16.9.2012 16:14:25
A aby som nezabudol: Dočkáme sa aj práce s grafikou? Lebo to som v slovensky zrozumiteľnom tutoriále ešte nevidel. Teda, aby som upresnil. Na nejakom serveri som našiel aplikáciu, čo kreslila na hlavné okno aplikácie, ale ja mám na mysli triedu QGraphicsView.
0 0 Miloš 16.9.2012 15:49:32
Peter: OpenGL: S tým by som nesúhlasil, čo sa jazyka C++ týka, pretože, ked tam je tutoriál, tak asi fungovať musí. Len ide o to, že ho nemám kam napísať, aby fungoval. A o to mi ide. Nejde mi teraz o to, že Qt má vsebe nejakú obálku. Ja si povedzme stiahnem OpenGL priamo z Internetu, pridám knižnice do kódu a chcem, aby to fungovalo. To, že Qt nejaké OpenGL obsahuje chcem ignorovať. Dúfam, že už mi rozumieš. Nejde mi ani tak konkrétne o OpenGL, ale často nachádzam nejaké zrojáky v C++, ktoré sa do Qt len tak prepísať a spustiť nedajú. Ako príklad som uviedol to OpenGL.
0 0 Peter Bočan 14.9.2012 18:41:33
Ďakujem za chválu :).

Neplánujem v krátkej budúcnosti (1 diel :D) používať Qt Designér, potom poukážem aj naň a rozdiely pri písaní programového kódu s a bez neho - je tam minimum zmien, no programátora núti byť viazaný na práve danú formu.

A ku QtOpenGL...
QtOpenGL je "obálka" pre OpenGL, no ku čistému OpenGL sa len tak jednoducho nedokopeš - má to vlastné triedy, názvy, štruktúry, enumerácie, ai., takže používať tutoriály priamo zo stránok OpenGL nemá zmysel.
1 0 Miloš 14.9.2012 16:50:07
Neviem, či je vhodné písať taký komentár sem do tohto tutoriálu, ale skúsim:
Často narážam na rôzne ukážky kódu v C++ v rôznych článkoch, napr. o OpenGL. Lenže tieto kódy len tak prepísať do Qt a spustiť sa nedá. Mohol by si v niektorej časti seriálu napísať ako takú ukážku kódu rozbehať v Qt? Viem, že Qt obsahuje aj OpenGL, ale akési nekompatibilné s oficiálnym OpenGL a ukážku kódu OpenGL by som s oficiálnou OpenGL knižnicou v Qt nevedel rozbehať. Dúfam, že som to napísal zrozumiteľne o čo mi vlastne ide.
Zajtra.sk > Programovanie > Seriály > QtGui - Hello World


Kritika

Vieš ako robiť veci lepšie? Pomôž našim odvážnejším členom a skritizuj im projekty!

Reklama

Seriály zo Zajtra.sk

Reklama