Fillimi i programimit në Qt

Mirë se vjen në botën e Qt—kornizë për zhvillim softuerik ndër-platformësh. Në këtë udhërrëfyes, ne ju mësojmë bazat mbi Qt duke ndërtuar një aplikacion të thjeshtë i cili shërben për mbajtjen e shenimeve që do ta quajmë Notepad. Pasi që ta lexoni këtë udhërrëfyes, ju duhet të jeni gati të hyni më thellë në pamjen e përgjithshme të Qt dhe dokumentimin e API (Ndërfaqe për programim të aplikacioneve) që e kemi në këtë faqe dhe jeni në gjendje të gjeni informacione të tjera lidhur me aplikacionin që ju jeni duke e ndërtuar.
Përshëndetje Notepad
Në këtë shembull ne në mënyrë të thjeshtë e krijojmë dhe e shfaqim një formë ne ndërfaqen tonë punuese (Ang. Desktop), kjo përfaqëson një Qt program shumë të thjeshtë që përmban pamje grafike (GUI).

Pamja

Ja ku është kodi i cili krijon një formë të tillë:

  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. }

Le të kalojmë nëpër këtë kod rresht pas rreshti që ta kuptojmë më mirë. Në dy linjat e para, ne i përfshijmë header skedarët për QApplication dhe QTextEdit të cilët janë dy klasat që ne do ti përdorim në këtë shembull. Të gjitha Qt klasat kanë një header skedarë të emërtuar sipas tyre.

Linja 6 krijon një object të QApplication. Ky objekt menaxhon burimet e të gjithë aplikacionit dhe është I domosdoshëm për çdo Qt program që posedon pamje grafike (GUI). QApplication ka nevojë për argv dhe args argumentet e programin sepse Qt pranon disa argumente që futen nga linja komanduese (command line) e sistemit operativ.

Linja 8-të krijon një objekt të QTextEdit. Editori I tekstit është një element visual në një aplikacion me pamje grafike. Në Qt ne I thërrasim elementet e tilla, widgets. Shembuj të widget-ëve tjerë janë shiritat rrëshqitës, label-at dhe radio butonët. Widget-ët mund të jenë edhe konteinerë për widget-ë të tjerë; për shembull, një dialog apo dritarja kryesore e një apliacionit.

Linja 9 tregon edituesin e tekstit I cili gjindet në kornizën e tij të vetme dhe është I shfaqur në ekran. Pasi që widget-ët funksionojnë edhe si konteiner (për shembull një QMainWindow, I cili përmban shirit për vegla, meny, shirit për gjendje dhe Widget-ë të tjerë), është e mundur të shfaqet një widget të vetëm në dritren e tij të vetme. Widget-ët nuk janë dukshëm në vete; funksioni shoë() e bën një widget të dukshëm.

Linja e 11-të bën që QApplication të hyj në ciklin e tij të ngjarjeve (event loop). Kur një Qt aplikacion është në funksionim, ngjarjet gjenerohen dhe dërgohen tek widget-ët e aplikacionit. Shembuj të ngjarjeve janë shtypjet e miut dhe shtypjet e tasteve në tastierë. Kur ju shkruani tekst në një ëidget për editim të tekstit siç është QTextEdit, ai merr tastet e shtypura në tastierë dhe përgjigjet duke vizatuar tekstin e shtypur në widget.
Për ta bërë aplikacionin të funksionojë, hape një linje të komandave (command line), dhe shkruani direktoriumin në të cilin e keni .cpp skedarin e programit. Komandat e shfaqura poshtë e ndërtojnë programin.

  1. qmake -project          
  2. qmake          
  3. make

Kjo do ta krijoj një skedar ekzekutues në direktoriumin part1 (vëmendje sepse në sistemin operativ Ëindoës, ju duhet të përdorni nmake në vend të make. Po ashtu, skedarët ekzekutues do të vendosen në part1/debug apo part1/release). Qmake është vegla e Qt për ndërtim të programeve I cili pranon një skedar konfigurues. Qmake gjeneron këtë për ne pasi që I jepet –project argumenti. Pasi që skedari konfigurues të jetë krijuar (I cili ka prapashtesën .pro), qmake prodhon një make skedar I cili e ndërton programin për ju. Më vonë do të shikojmë se si mund ti shkruajmë .pro skedarët ne vetë.

Mëso më shumë
Rreth: Gjeometria e widget-ëve dhe dritareve
Këtu: Window and Dialog Widgets [doc.qt.nokia.com]

Rreth: Ngjarjet dhe trajtimi i ngjarjeve
Këtu: The Event System [doc.qt.nokia.com]

Shtimi i një butoni për mbyllje

Në një aplikacion real ju duhet më shumë se një widget. Ne tani do të ju prezantojmë QPushButton që do të vendoset nën edituesin e tekstit. Pasi të shtypet butoni ka për detyrë ta mbyll Notepad aplikacionin (për shembull, pasi që të klikohet mbi të me miun e kompjuterit).

Pamja2

Së pari le të shikojmë kodin.

  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. }

Linja 1 përfshinë QtGui i cili përmban të gjitha klasat e Qt që kanë të bëjnë me pamjen grafike (GUI).
Linja 10 përdorë mekanizmin e sinjaleve (ang. Signal) dhe slot-ëve që janë të inkorporuara në Qt për ta bërë aplikacionin të mbyllet pasi që butoni për mbyllje I emërtuar Quit button të shtypet. Slot është një funksion thirrja e të cilit mund të bëhet në momentin kur aplikacioni është duke funksionuar (Ang. Runtime) duke përdorë emrin e atij funksioni përkatës. Sinjali është po ashtu një funksion thirrja e të cilit rezulton në thirrjen e çdo slot-i i cili është i regjistruar në atë sinjal përkatës; ne e quajmë këtë si lidhe slot-in në sinjal dhe emeto sinjalin.

Quit() është një slot i klasës QApplication i cili e mbyll aplikacionin. Clicked() është një sinjal të cilit e emeton QPushButton pasi që të shtypet. Funksioni static QObject::connect() kujdeset për lidhjen e slot-it me sinjal. SIGNAL dhe SLOT janë dy makro funksione që pranojnë si parametër nënshkrimet e funksioneve të sinjalit dhe slot-ëve për ti lidhur ata. Ne po ashtu duhet ti japim edhe treguesit (pointerët) e objekteve të cilët duhet të dërgojnë dhe pranojnë sinjalin.

Linja 12 krijon një QVBoxLayout objekt. Siç e kemi përmendur, widget-ët mund të përmbajnë widget-ë të tjerë. Është e mundur që menjëherë ti vendosim kufijtë (pozicionin dhe madhësinë) e widget-ëve fëmijë, por zakonisht është më lehtë të përdorim faqosje (Ang. Layout). Faqosja menaxhon kufijtë e fëmijëve të një widget-i. Përshembull QVBoxLayout vendos fëmijët në një rresht vertical.
Linja 13 dhe 14 shton edituesin e tekstit dhe butonit në faqosje. Në linjën 17 ne vendosim faqosjen në widget.

Mëso më shumë
Rreth: Sinjaleve dhe slot-ëve
Këtu: Signals & Slots [doc.qt.nokia.com]

Rreth: Faqosjeve
Këtu: Layout Management [doc.qt.nokia.com], Widgets and Layouts [doc.qt.nokia.com], Layout Examples [doc.qt.nokia.com]

Rreth: Widget-ët që vijnë me Qt
Këtu: Qt Widget Gallery [doc.qt.nokia.com], Widget Examples [doc.qt.nokia.com]

Nënklasimi i QWidget

Kur përdoruesi dëshiron ta mbyllë aplikacionin, ju dëshironi ta shfaqni një dialog i cili pyet nëse përdoruesi është i sigurtë për mbylljen e aplikacionit. Në këtë shembull, në e krijojmë një nënklasë (Ang. Subclass) të QWidget, dhe shtojmë një slot të cilin e lidhim me butonin për mbyllje.

Pamja3

Së pari le të shikojmë kodin.

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

Q_OBJECT makro funksioni duhet të jetë i pari në përcaktimin e klasës, dhe kjo deklaron klasën tonë si QObject (Natyrisht klasa jonë duhet të trashëgohet nga QObject). QObject shton disa aftësi në një klasë normale të C++. Sidomos kur marrja e emrit të klasës dhe i slot-ëve mund të bëhet kur aplikacioni është duke funksionuar. Po ashtu është e mundur marrja e tipeve të të dhënave që janë si parametër e një slot-i, dhe të bëhet thirrja e tij kur aplikacioni është në funksionim e sipër.

Linja 13 deklaron një slot quit(). Kjo bëhet shumë lehtë e mundur duke përdorë slots macron. Tani slot-i quit() mund të lidhet me sinjale të cilët kanë nënshkrim të njëjtë (apo ndonjë sinjal që nuk pranon parametra).
Në vend se ta bëjmë lidhjen në funksionin kryesor (funksionin main()), ne tani e përdorim konstruktorin e Notepad aplikacionit.

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

Si e patë në definimin e klasës, ne përdorim treguesit (pointerët) në QObject-et tona textEdit dhe quitButton. Si rregull ju duhet gjithmonë ti alokoni QObject-et në heap dhe asnjëherë ti kopjoni ato.
Ne tani përdorim tr() funksionin rreth cdo stringu i cili është i dukshëm. Ky funksion është i domosdoshëm nëse dëshironi që ta shpërndani aplikacionin tuaj në më shumë se një gjuhë (për shembull, në Gjuhën Shqipe, Angleze apo Kineze). Nuk do të thellohemi në detale këtu por ju mund të ndiqni linkun e Qt Linguist nga tabela mëso më shumë.

Mëso më shumë
Rreth: tr() dhe internacionalizimi
Këtu: Qt Linguist Manual [doc.qt.nokia.com], Writing Source Code for Translation [doc.qt.nokia.com], Hello tr() Example [doc.qt.nokia.com], Internationalization with Qt [doc.qt.nokia.com]

Rreth: QObjects dhe Qt modeli i objekteve (Kjo është e domosdoshme për ta kuptuar Qt)
Këtu: Object Model [doc.qt.nokia.com]

Rreth: qmake dhe Sistemi i ndërtimit në Qt
Këtu: qmake Manual [doc.qt.nokia.com]

Krijimi i .pro skedarit

Në vend se ta përdorim –project zgjedhjen e qmake programit, në këtë shembull ne do ta shkruajmë vetë një .pro skedar.

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

Komandat në vazhdim do ta ndërtojnë shembullin tonë.

  1. qmake
  2. make

Përdorimi i QMainWindow

Shumë aplikacione përfitojnë nga përdorimi i QMainWindow, i cili ka faqosjen e tij në të cilin mund të shtoni shirit për meny, dok widget-ë, shirita për vegla dhe shirit të gjendjes. QMainWindow ka një zonë në qendër e cila mund të jetë i zënë nga çdo lloj i widget-ëve. Në shembullin tonë, ne do ta vendosim edituesin e tekstit në qendër të QMainWindow.

pamja4

Le të shikojmë definimin e ri të klasës Notepad.

  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.          };

Ne i përfshijmë dy slot-e më shumë të cilat përdoren për ruajtjen dhe hapjen e dokumenteve. Zbatimi i tyre do të bëhet në seksionin në vazhdim.

Shpesh në një dritare kryesore, thirrja e një slot-i të vetëm duhet të bëhet nga disa widget-ë të ndryshëm.

Shembujt janë elementet e menysë dhe butonët në shiritin e veglave. Për ta bërë këtë më të lehtë, Qt siguron disa veprime që janë objekte të QAction, të cilët mund të jepen tek disa widget-ë përnjëherë, dhe mund të kenë lidhje në slot-e të ndryshme. Për shembull, njëlloj QMenu dhe QToolBar mund të krijojnë elemente në meny dhe butonë të veglave që shfaqen në QToolBar, dhe të gjitha këto mund të krijohen veprime të QAction dhe të përdoren në të dy pjesët. Do të shohim se si punojnë këto shumë shpejtë. Sikur më heret, ne përdorim konstruktorin e Notepad aplikacionit tonë për ta ngritur pamjën grafike (GUI).

  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.          }

QAction objektet janë krijuar me një tekst i cili duhet të shfaqet ne widget-ë në të cilat ne i shtojmë ato (në rastin tonë, në elementet e menysë). Nëse kishim kërkuar që ti shtojmë të njëjtat objekte të QAction në një shirit për vegla, ne do ti kishim dhënë aksioneve tona edhe ikonë. Kur një element i menysë tani klikohet, elementi do ta shkaktojë aksionin dhe thirrja e slot-it përkatës do të bëhet.

Mëso më shumë
Rreth: Dritareve kryesore dhe klasat e dritareve kryesore
Këtu: Application Main Window [doc.qt.nokia.com], Main Window Examples [doc.qt.nokia.com]

Rreth: Aplikacionet MDI
Këtu: QMdiArea [doc.qt.nokia.com], MDI Example [doc.qt.nokia.com]

Ruajtja dhe Ngarkimi

Në këtë shembull, ne do ta zbatojmë funksionalitetin e open() dhe save() slot-ëve që i kemi shtuar në shembullin paraprak.

pamja5

Ne do të fillojmë me slot-in open():

  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.          }

Hapi i parë është pyetja e përdoruesit për emrin e skedarit të cilin ai dëshiron ta hapë. Qt vjen me QFileDialog, i cili është dialog prej të cilit përdoruesi mund të bën zgjedhjen e skedarit. Imazhi më lartë tregon dialogun në Kubuntu. Funksioni static getOpenFileName() shfaq një dialog modal i cili është i orientuar për hapjen e skedareve dhe nuk kthehet deri sa përdoruesi të bën zgjedhjen e skedarit. Funksioni kthen rrugën e skedarit në disk për skedarin e zgjedhur nga përdoruesi, apo një string të zbrazët nëse përdoruesi ka anuluar dialogun.

Pasi që ta kemi emrin e skedarit, ne provojmë ta hapim atë skedar me funksionin open() i cili e kthen një vlerë true nëse skedari është hapur me sukses. Ne nuk do ta përfshijmë trajtimin e gabimeve këtu, por ju mund ti ndiqni linqet nga seksioni mëso më shumë. Nëse skedari nuk mund të hapet, ne përdorim QMessageBox klasën për ta shfaqur një dialog me një mesazh gabimi (shih QMessageBox përshkrimin e klasës për detale të më tutjeshme).

Leximi i skedarëve është i lehtë duke përdorë funksionin readAll(), i cili i kthen të gjitha të dhënat e skedarit në QByteArray format. constData() kthen të gjitha të dhënat në varg si const char*, për të cilin QString përmban konstruktor. Përmbajtja mund të shfaqet në edituesin e tekstit. Pastaj ne e bëjmë thirrjen e funksionit close() për ta kthejmë përshkruesin e skedarit tek sistemi operativ.

Tani le të vazhdojmë me slotin për ruajtje, të cilin e kemi emërtuar save().

  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.          }

Kur ne dëshirojmë ta shkruajmë përmbajtjen e edituesit të tekstit në skedar, ne e përdorim klasën QTextStream, i cili mbështjell QFile objektin. Vargu i tekstit (text stream) mund të shkruaj përmbajtjen e QString drejtpërdrejt në skedar, QFile pranon vetëm të dhëna të papërpunuara (char*) me funksionet ërite() të QIODevice.

Mëso më shumë
Rreth: Skedarëve dhe I/O pajisjeve
Këtu: QFile [doc.qt.nokia.com], QIODevice [doc.qt.nokia.com]