Magyar

Bevezetés a Qt programozás alapjaiba

Üdvözöllek a Qt keresztplatformos fejlesztői rendszer világában. Ebben a leírásban egy egyszerű jegyzettömb alkalmazás elkészítése során bemutatjuk a Qt programozás alapfogásait. Az útmutató tanulmányozása során nyugodtan nézz bele az API dokumentációba, amennyiben valamivel kapcsolatban több információra van szükséged.

Az útmutató angol nyelvű változata elérhető az http://doc.qt.nokia.com/gettingstartedqt.html [doc.qt.nokia.com] oldalon.

Hello Jegyzettömb

Példánkban egy egyszerű szövegdobozt hozunk létre egy ablakkeretben. Ez lesz a legegyszerűbb felhasználói felülettel rendelkező Qt segítségével írt program.

Notepad

Íme a kód:

  1. #include <QApplication>
  2. #include <QTextEdit>
  3.  
  4. int main(int argv, char **args)
  5. {
  6.     QApplication app(argv, args);
  7.    
  8.     QTextEdit textEdit;
  9.     textEdit.show();
  10.    
  11.     return app.exec();
  12. }

Vizsgáljuk meg a kódot sorról sorra. Az első két sorban a QApplication [doc.qt.nokia.com] és a QTextEdit [doc.qt.nokia.com] osztály használatához szükséges headerfájlokat includoljuk be a programunkba. A Qt összes osztályának megvan a nevével megegyező headerje.

A 6. sorban létrehozunk egy QApplication [doc.qt.nokia.com] objektumot. Ez az objektum kezeli az alkalmazás összes erőforrását. Egy ilyen objektum létrehozása szükséges minden grafikus Qt alkalmazáshoz. Argumentumként át kell adni az argv és args paramétereket, mivel a Qt-ban írt alkalmazásoknak van néhány beépített parancssori argumentuma [doc.trolltech.com].

A 8. sorban létrehozunk egy QTextEdit [doc.qt.nokia.com] objektumot. Ez a szövegdoboz egy megjeleníthető grafikus elem. A Qt-ban az ilyeneket widgeteknek hívjuk. További widgetek például: gördítősávok, címkék, rádiógombok. Minden widget lehet egyfajta konténer további widgetek számára, mint például egy ablak, vagy egy dialógus.

A 9. sorban megjelenítjük a szövegdobozt a saját ablakkeretében. Mint már említettem, a widgetek konténerként is funkcionálnak mint például a QMainWindow [doc.qt.nokia.com], amelynek van menüsora, státuszsora, és még néhány egyéb beépített widgete. Továbbá lehetőség van egyetlen widget megjelenítésére is főablakba helyezés nélkül, ez esetben maga a widget kap egy ablakkeretet. A widgetek alapból nem láthatóak, a show() metódus meghívása teszi őket azzá.

A 11. sorban meghívjuk a QApplication [doc.qt.nokia.com] eseménykezelő rutinját. Ha egy Qt alkalmazás futása közben esemény történik, akkor az kiküldődik a megfelelő widgethez. Például az egérkattintás és a billentyűleütések esetében a szövegdobozunk fogja megkapni ezeket az eseményeket. Az ilyen és ehhez hasonló feladatokat intézi ez a metódus.

Az alakalmazás fordításához és futtatásához nyiss meg egy parancssort, lépj be abba a mappában, ahol a .cpp fájlod található, és futtasd le a következő parancsokat:

  1.          qmake -project
  2.          qmake
  3.          make

Ezen műveletek után egy futtatható binárisnak kell megjelennie a part1 mappában. (Megjegyzés a Windowst és VisualStudiot használóknak: a make nmaket kell futtatniuk, továbbá a bináris a part1/debug vagy part1/release mappában fog létrejönni.)
A qmake a Qt build eszköze ami argumentum nélkül egy projekt fájlt keres, amit fel tud dolgozni. A -project argumentummal létrehoz automatikusan egy .pro fájlt a mappában található fájlokat hozzáadva. A qmaket futtatva létrejön a Makefile, amit a make programod fog feldolgozni. A .pro fájlok írásáról később még lesz szó. Amennyiben Windows alatt valamit hiányolna a fordítás során, akkor keresd ki a start menüből a Qt Command prompt-ot, és abból futtasd a parancsokat.

További olvasnivalók:
Miről Hol
Widgetek és ablakok geometriája Window and Dialog Widgets [doc.qt.nokia.com]
Események és a Qt eseménykezelője The Event System [doc.qt.nokia.com]

Kilépés gomb hozzáadása

Egy igazi alkalmazásban általában egynél több widgetre van szükséged. Helyezzünk egy QPushButtont [doc.qt.nokia.com] a szövegdoboz alá. Ez arra fog szolgálni, hogy ha a felhasználó rákattint, a program lépjen ki.

Eingabefeld mit Beenden Knopf

Nézzük a kódot:

  1. #include <QtGui>
  2.  
  3. int main(int argv, char **args)
  4. {
  5.   QApplication app(argv, args);
  6.  
  7.   QTextEdit textEdit;
  8.   QPushButton quitButton("Quit");
  9.  
  10.   QObject::connect(&quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
  11.  
  12.   QVBoxLayout layout;
  13.   layout.addWidget(&textEdit);
  14.   layout.addWidget(&quitButton);
  15.  
  16.   QWidget window;
  17.   window.setLayout(&layout);
  18.  
  19.   window.show();
  20.  
  21.   return app.exec();
  22. }

Az első sorban beincludoljuk a QtGui [doc.qt.nokia.com] headert, ami magában foglalja a Qt összes grafikus osztályát.

A tizedik sorban a Qt szignál slot mechanizmusa segítségével lekezeljük a Kilépés gomb megnyomását.

A szlot függvény a futás során hívódik meg a neve segítségével. A szignálok olyan függvények, amelyek a megfelelő esemény bekövetkezésekor automatikusan meghívják a hozzájuk csatlakoztatott slotokat. Esetünkben a quit() [doc.qt.nokia.com] a widgetünk egy beépített slotja, amelyet meghívva az alkalmazás kilép. A gombunk clicked() [doc.qt.nokia.com] szignálja pedig a gomb megnyomásakor emittálódik. A szignál-slot hozzárendelést a statikus QObject::connect() [doc.qt.nokia.com]# függvénnyel hozhatjuk létre. Az argumentumként használt SIGNAL és SLOT makrókban kell megadnunk a megfelelő függvényneveket, azok argumentumainak típusával egyetemben. A connect() függvénynek át kell adni a küldő és fogadó objektumokra mutató mutatókat is.

A 12. sorban egy QVBoxLayout-ot [doc.qt.nokia.com] hozunk létre. Ahogy már említettük a widgetek (szülő widget) tartalmazhatnak további widgeteket (gyermek widgetek). Lehetőségünk van a gyermek widgeteknek pontos méretet, pozíciót megadni, de egyszerűbb, ha ehelyett layoutokat használunk, így a azok fogják kezelni a gyermekwidgetek elrendezését. A QVBoxLayout [doc.qt.nokia.com] például a gyermekeket egy függőleges oszlopba rendezi.

A 13. és 14. sorban hozzáadjuk a szövegdobozunkat és a nyomógombunkat a layouthoz, majd a 17. sorban érvénybe léptetjük az elrendezést.

További olvasnivalók:
Miről Hol
Szignálok és Szlotok Signals & Slots [doc.qt.nokia.com]
Layoutok Layout Management [doc.qt.nokia.com], Widgets and Layouts [doc.qt.nokia.com], Layout Examples [doc.qt.nokia.com]
A QT beépített widgetkészlete Qt Widget Gallery [doc.qt.nokia.com], Widget Examples [doc.qt.nokia.com]

Származtatás a QWidget osztályból

Miután a felhasználó a kilépés gombra kattint, sok esetben érdemes egy megerősítést kérő ablakot megjeleníteni, hogy valóban ki akar-e lépni. Példánkban a QWidget [doc.qt.nokia.com] osztályból származtatjuk a programfelület legfelsőbbb osztályát, és létrehozunk egy slotot, amihez hozzákötjük a Kilépés gomb kattintás szignálját.

Íme a kód:

  1.  
  2.          class Notepad : public QWidget
  3.          {
  4.               Q_OBJECT
  5.  
  6.           public:
  7.              Notepad();
  8.  
  9.          private slots:
  10.              void quit();
  11.  
  12.          private:
  13.              QTextEdit *textEdit;
  14.              QPushButton *quitButton;
  15.          };

A Q_OBJECT makrónak kell az osztálydeklaráció elején lennie. Ez a makró az osztályunkat QObjectként [doc.qt.nokia.com] deklarálja (természetesen a használatához a QObjecttől [doc.qt.nokia.com] kell származtatnunk). Ez a példánkban is így van, hiszen a QWidget osztály a QObjectből van származtatva. A QObject [doc.qt.nokia.com] különféle funkciókkal bővíti ki a C++ osztályok tulajdonságait. Például az osztály és szlotok neve lekérdezhetővé válik futásidőben. Továbbá lehetőség van a szlotok argumentumainak típusát lekérdezni, illetve név alapján meghívni azokat.

A 9. sorban deklaráljuk a quit() szlotot a slots makró segítségével. A slotunkat ezután hozzáköthetjük bármilyen szignálhoz, amelynek nincs argumentuma.

A következőekben létrehozunk egy Notepad osztályt, amelynek a konstruktorában hozzuk létre a felületet, és kötjük össze a slotokat a szignálokkal. Ez megoldás elegánsabb, mint ha ezt a main() függvényben tennénk meg.

  1.          
  2.          Notepad::Notepad()
  3.          {
  4.              textEdit = new QTextEdit;
  5.              quitButton = new QPushButton(tr("Quit"));
  6.  
  7.              connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
  8.  
  9.              QVBoxLayout *layout = new QVBoxLayout;
  10.              layout->addWidget(textEdit);
  11.              layout->addWidget(quitButton);
  12.  
  13.              setLayout(layout);
  14.  
  15.              setWindowTitle(tr("Notepad"));
  16.          }

Amint láthatod mutatókat használunka textEdit és a quitButton elérésére. Az ilyen grafikus elemeket mindig a heapen példányosítjuk, és sohasem másoljuk őket.

Az ablak címét a setWindowTitle metódus segítségével állítjuk be. Amint láthatod a felhasználó számára látható szöveget a tr() [doc.qt.nokia.com] függvényen keresztül adjuk át a függvénynek. Ez a függvény a többnyelvű alkalmazások írásánál nyújt segítséget. Most nem megyünk bele a részletekbe, további információt a Qt Linguist [doc.qt.nokia.com] dokumentációjánál találsz.

További olvasnivalók:
Miről Hol
tr() és a többnyelvűsítés Qt Linguist Manual [doc.qt.nokia.com], Writing Source Code for Translation [doc.qt.nokia.com], Hello tr() [doc.qt.nokia.com] Example, Internationalization with Qt [doc.qt.nokia.com]
QObject [doc.qt.nokia.com] és a Qt objektummodell-rendszere Object Model [doc.qt.nokia.com]
qmake a Qt build rendszere qmake Manual [doc.qt.nokia.com]

Az elkövetkezőkben írunk egy saját .pro fájlt, ahelyett, hogy a qmake-el generáltatnánk le.

  1.          HEADERS =  notepad.h
  2.          SOURCES =  notepad.cpp \
  3.                     main.cpp

A következő parancsokkal tudod lefordítani a példát:

  1.          qmake
  2.          make

A QMainWindow használata

Sok alkalmazás írásakor kézenfekvő lehet a használata, ugyanis ez az osztály egy előre megadott layout-ot tartalmaz, amelyre elhelyezhetjük saját menü sorunkat, eszköztárainkat, a dokkolható widgetek számára rögzítési területet, illetve az állapotsort is.
A QMainWindownak [doc.qt.nokia.com] van egy középső területe, ahol elhelyezhetünk bármilyen widgetet. Esetünkben is ide fog kerülni a szövegdobozunk.

QMainWindow

Tekintsük át a Notepad osztály definícióját!

  1.          #include <QtGui>
  2.  
  3.          class Notepad : public QMainWindow
  4.          {
  5.              Q_OBJECT
  6.  
  7.          public:
  8.              Notepad();
  9.  
  10.          private slots:
  11.              void open();
  12.              void save();
  13.              void quit();
  14.  
  15.          private:
  16.              QTextEdit *textEdit;
  17.  
  18.              QAction *openAction;
  19.              QAction *saveAction;
  20.              QAction *exitAction;
  21.  
  22.              QMenu *fileMenu;
  23.          };

Az elkövetkezendőkben implementálni fogunk két slotot, mely a dokumentum megnyitására és mentésére fog szolgálni.

A fő ablakokban gyakran megesik az, hogy egy szlotot több widget is meghív. Esetünkben a menü egyes elemei és az eszköztár gombjai is ezt teszik. Az ilyen esetek leegyszerűsítésére hozták létre a QAction [doc.qt.nokia.com] osztályt, amelyet átadhatunk több widgetnek is, majd hozzákötjük a megfelelő szlothoz a szignálját. Például a QMenu [doc.qt.nokia.com] és a QToolBar [doc.qt.nokia.com] is létrehozhat menüket, eszköztárelemeket ugyanannak a QAction [doc.qt.nokia.com] -nek a hatására.

Ahogyan már említettük a grafikus elemeket a Notepad osztály konstruktorában inicializáljuk.

  1.          Notepad::Notepad()
  2.          {
  3.              saveAction = new QAction(tr("&Open"), this);
  4.              saveAction = new QAction(tr("&Save"), this);
  5.              exitAction = new QAction(tr("E&xit"), this);
  6.  
  7.              connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
  8.              connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
  9.              connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
  10.  
  11.              fileMenu = menuBar()->addMenu(tr("&File"));
  12.              fileMenu->addAction(openAction);
  13.              fileMenu->addAction(saveAction);
  14.              fileMenu->addSeparator();
  15.              fileMenu->addAction(exitAction);
  16.  
  17.              textEdit = new QTextEdit;
  18.              setCentralWidget(textEdit);
  19.  
  20.              setWindowTitle(tr("Notepad"));
  21.          }

A QAction-ök [doc.qt.nokia.com] konstruktorának egy szöveget adunk meg argumentumként. Ez a szöveg fog megjelenni azokon a widgeteken, amelyekhez hozzárendeljük őket (esetünkben a menüelemeken). Ha ugyanazt a funkciót el szeretnénk érni az eszköztárunkról is, akkor az akcióhoz ikont is kell rendelnünk. Tehát ha egy menüre rákattintunk az triggerelni fogja az akciónkat, amely ezáltal a emittálja a triggered() szignálját, amely meghívja a hozzákötött szlotokat.

További olvasnivalók:
Miről Hol
Főablak és egyéb ablakok Application Main Window [doc.qt.nokia.com], Main Window Examples [doc.qt.nokia.com]
MDI alkalmazások QMdiArea [doc.qt.nokia.com], MDI Example [doc.qt.nokia.com]

A következő kódrészletben implementálni fogjuk a mentés és megnyitás kezelésére szolgáló szlotokat.

Kezdjük az open() szlottal:

  1.          QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
  2.              tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
  3.  
  4.          if (fileName != "") {
  5.              QFile file(fileName);
  6.              if (!file.open(QIODevice::ReadOnly)) {
  7.                  QMessageBox::critical(this, tr("Error"),
  8.                      tr("Could not open file"));
  9.                  return;
  10.              }
  11.              QString contents = file.readAll().constData();
  12.              textEdit->setPlainText(contents);
  13.              file.close();
  14.          }

Első lépésként bekérjük a felhasználótól a fájl nevét. A Qt-ban direkt erre a feladatra van egy osztály a QFileDialog [doc.qt.nokia.com] , amely egy dialógus megjelenítésére szolgál, amelyből a felhasználó kitallózhatja a kívánt fájlt.

A képen ezt a dialógust láthatjuk, ahogyan az KUbuntun megjelenik.

A getOpenFileName() [doc.qt.nokia.com]) statikus metódus egy dialógust jelenít meg (az alkalmazás többi ablaka nem használható ameddig jelen van), és addig nem tér vissza amíg a felhasználó ki nem választott egy fájlt. A visszatérési értékben megkapjuk a fájl elérési útját, vagy egy üres sztringet, ha a felhasználó a mégsem gombra kattintott.

Ha megvan az érvényes fájlnév, akkor megnyitjuk a fájlt a QFile open() [doc.qt.nokia.com] metódusával, amely igazzal tér vissza, ha a megnyitás sikerült. Most nem megyünk bele a hibakezelésbe, de ha érdekel, utána nézhetsz ezeknek a További információ szekcióban. Ha a fájl nem nyitható meg, akkor egy QMessageBox [doc.qt.nokia.com] jelenítjük meg a hibaüzenetet. A részletekért lásd a QMessageBox [doc.qt.nokia.com] osztály súgóját.

Az adatok beolvasása triviális a QFile [doc.qt.nokia.com] readAll() [doc.qt.nokia.com] metódusának köszönhetően. Ez a függvény egy a fájlban lévő adatokat tartlmazó QByteArray [doc.qt.nokia.com] -jel tér vissza. A QByteArray constData() [doc.qt.nokia.com] metódusa segítségével az adatokat const char* formában is elérhetjük, amit már át tudunk adni a QString [doc.qt.nokia.com] konstruktorának. Így a fájl tartalma megjeleníthető a szövegdobozban. Végül a close() [doc.qt.nokia.com] metódussal bezárjuk a fájlt, így adva vissza a fájlleírót az operációs rendszernek.

Most, hogy letudtuk a megnyitás szlotot lássuk a mentést:

  1.          QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
  2.              tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
  3.  
  4.          if (fileName != "") {
  5.              QFile file(fileName);
  6.              if (!file.open(QIODevice::WriteOnly)) {
  7.                  // error message
  8.              } else {
  9.                  QTextStream stream(&file);
  10.                  stream << textEdit->toPlainText();
  11.                  stream.flush();
  12.                  file.close();
  13.              }
  14.          }

Miután megszerkesztettük a szövegdobozunk tartalmát, azt a QTextStream [doc.qt.nokia.com] objektumon keresztül írjuk vissza a fájlunkba. Ez az osztály mintegy ráépül a QFile [doc.qt.nokia.com] objektumunkra, lehetővé téve, hogy a neki küldött szövegeket direktben kiírassuk a fájlunkba.

További olvasnivalók:
Miről Hol
Fájlok és I/O eszközök QFile [doc.qt.nokia.com], QIODevice [doc.qt.nokia.com]

Categories: