Programowanie w języku C/C++

Qt4 krótki wstęp

<center>

Qt4 krótki wstęp

</center>
<center>

Dokumentacja, a cóż to takiego?

</center>

        Zanim rozpoczniemy pisanie jakiego kol wiek kodu powinniśmy zajrzeć do dokumentacji. Dostępnej pod adresem http://doc.trolltech.com/
Sama dokumentacja biblioteki qt4 przyda nam się przed i podczas pisania aplikacji. Oprócz udokumentowanych klas znajdują się tak także liczne przykłady,tutoriale czy galeria widgetów. Na wstępie podaje także link do źródeł programu który tu zostaje stworzony http://przeklej.pl/d/0000r262o6jo/qtc1.tar.gz
        Przed rozpoczęciem lektury warto znać pojęcie dziedziczenia ,a także mieć już pewną wprawę w operowaniu C++.
        Okno w qt4 można stworzyć 'ręcznie' bądź przy użyciu specjalnej aplikacji(qtdesigner)
My zaczniemy od stworzenia ręcznie okna. W projekcie użyjemy komponentów takich jak:

  • QWidget
  • QPushButton
  • QTextEdit
<center>

Budujemy Okno!

</center>

        Przystępujemy do tworzenia klasy SimpleApp.
plik simpleapp.h
class SimpleApp:public QMainWindow
{
      Q_OBJECT
 
public:
      SimpleApp(QWidget *parent=0);
      ~SimpleApp();
 
 
private slots:
     void ShowDial();
     void AddText();
 
 
private:
     QWidget * widget;
     QTextEdit * text;
     QPushButton * add_text;
     QPushButton * show_dial;
 
};



        Klasa ta dziedziczy po QMainWindow(to jest dobry moment aby otworzyć dokumentacje qt4),klasa ta zapewnia okno dla aplikacji. Q_OBJECT jest to specjalne makro które umożliwi nam korzystanie z własnych slotów i sygnałów(co to jest niebawem się dowiemy). Jeśli chodzi o QWidget jest to podstawowa klasa interfejsu użytkownika przechwytuje ona mysz,dane z klawiatury. Może być także użyty jako kontener dla innych widgetów itp.
Co do QTextEdit itp. To tu odsyłam do dokumentacji(moim zamiarem nie jest dublowanie dokumentacji) .
<center>

Sloty i Sygnały czyli Who is Who?

</center>
        Sloty i Sygnały są ważnym elementem frameworku qt4.
plik simpleapp.cpp
SimpleApp::SimpleApp(QWidget *parent):QMainWindow(parent){
        /*widget=new QWidget();
        setCentralWidget(widget);
 
        text=new QTextEdit();
        add_text=new QPushButton(tr("Add Text"));
        show_dial= new QPushButton(tr("Show Dial"));
        */
        connect(add_text,SIGNAL(clicked()),this,SLOT(AddText()));
        connect(show_dial,SIGNAL(clicked()),this,SLOT(ShowDial()));
 
        /*QVBoxLayout *mainLayoutt = new QVBoxLayout();
        mainLayoutt->addWidget(text);
        mainLayoutt->addWidget(add_text);
        mainLayoutt->addWidget(show_dial);
        widget->setLayout(mainLayoutt);*/
}
 
SimpleApp::~SimpleApp(){}
 
void SimpleApp::ShowDial(){
        QMessageBox::information(this, tr("jupi"),
                 tr("click ok"));
}
 
void SimpleApp::AddText(){
        text->clear();
        text->setText("Simple Text");
}

Wy komentowałem  to co nas nie interesuje. Sygnały i sloty to sposób komunikacji pomiędzy obiektami, w naszym przypadku znajdujemy się w obrębie jednego obiektu ale oczywiście można się wyobrazić kiedy wykorzystujemy parę obiektów i szukamy sposobu komunikacji między nimi wtedy jest to bardzo wygodne rozwiązanie. Możemy korzystać z wbudowanych slotów i sygnałów,ale możemy także tworzyć własne . Jak to działa? Otóż, Sygnał emitowany gdy wykonamy jakąś czynność np. Klikniemy w przycisk wtedy zostanie wyemitowany sygnał clicked(), przycisk ten będzie  za pomocą metody connect połączony  z odpowiednim slotem. Ogólnie wygląda to tak:
connect(objekt1,sygnał,obiekt2,slot)

W naszym przypadku jest to:

connect(add_text,SIGNAL(clicked()),this,SLOT(AddText()));

Czyli sygnał z obiektu add_text zostaje połączony z slotem AddText() z obiektu w którym obecnie pracujemy. Możemy sobie wyobrazić że zamiast this wstawiamy jakiś inny obiekt np. Stworzyliśmy obiekt pomarancza(ma on slot AddText() i jest on publiczny tzn. W pliku pomarancza.h powinno być sekcja public slots a nie jak w simpleapp.h private slots), czyli połączenie wyglądało by tak
connect(add_text,SIGNAL(clicked()),pomarancza,SLOT(AddText()));

Może zajść potrzeba wyemitowania sygnału prosto z funkcji. Robi się to np. Tak
//plik pomarancza.h
 
//jesteśmy w klasie pomarancza
 
//tworzymy własny slot i sygnał tym razem,nie polegamy na wbudowanych
 
public slots:
void setval(int val);
signals:
void change(int value);
private:
int myval
 
//w pliku pomarancza.cpp
void pomarancza::setval(int val){
        myval = val;
        emit change(val);}
następnie tworzymy gdzieś egzemplarze klasy pomarancza
pomarancza a,b;
connect(&a,SIGNAL(change(int)),&b,SLOT(setval(int)));
a.setval(10);//takie wywołanie ustawi w obiekcie a wartość równą 10 ale także w obiekcie b //ponieważ zostanie wyemitowany sygnał z wartością 10 a zostanie przechwycony przez slot //setval w obiekcie b

Co ciekaw sygnał może być połączony z innym sygnałem
connect(obiekt1, SIGNAL(//odpowiedni sygnał),
        this, SIGNAL(//odpowiedni sygnał));

Sygnały i sloty można rozłączać
disconnect(obiekt1, SIGNAL(//odpowiedni sygnał),
           obiekt2, SLOT(//odpowiedni sygnał));

<center>

Budujemy Dalej!

</center>
  plik simpleapp.cpp
SimpleApp::SimpleApp(QWidget *parent):QMainWindow(parent){
        widget=new QWidget();//1
        setCentralWidget(widget);
 
        text=new QTextEdit();//2
        add_text=new QPushButton(tr("Add Text"));
        show_dial= new QPushButton(tr("Show Dial"));
 
        connect(add_text,SIGNAL(clicked()),this,SLOT(AddText()));//3
        connect(show_dial,SIGNAL(clicked()),this,SLOT(ShowDial()));
 
        QVBoxLayout *mainLayoutt = new QVBoxLayout();//4
        mainLayoutt->addWidget(text);//5
        mainLayoutt->addWidget(add_text);
        mainLayoutt->addWidget(show_dial);
        widget->setLayout(mainLayoutt);//6
}
 
SimpleApp::~SimpleApp(){}
 
void SimpleApp::ShowDial(){
        QMessageBox::information(this, tr("jupi"),
                 tr("click ok"));//7
}
 
void SimpleApp::AddText(){
        text->clear();//8
        text->setText("Simple Text");//9}


1.Tworzymy nowy obiekt QWidget, następnie za pomocą setcentralwidget() ustawiamy nasz obiekt widget jako centralny widget okna(dzięki temu będziemy mogli widzieć efekty naszej pracy,nasz obiekt widget jest w tym przypadku kontenerem dla innych widgetów)
2.Tworzymy pozostałe obiekty
3.Dwa przyciski łączymy z odpowiednimi slotami
4.Tworzymy odpowiedni layout , czym to jest? Różne layouty decydują o ułożeniu innych widgetów(przyciski itp.) na innym widgecie(w naszym przypadku obiekt widget). QVBoxLayout układa nasze widgety pionowo jeden pod drugim.
5.Dodajemy odpowiednie widgety do layoutu
6.ustawiamy naszemu widgetowi odpowiedni layout
7.Tworzymy proste informacyjne okienko które wyskoczy po naciśnięciu przycisku show dial
8.QTextEdit ma slot clear który całkowicie go wyczyszcza z tekstu
9.Teraz dodajemy tekst do naszego obiektu text. Podsumowując po naciśnięciu przycisku Add Text cały tekst w obiekcie text zostaje wyczyszczony a następnie wstawiony inny.

Plik main.cpp jest trywialny:
int main(int argc, char *argv[])
{
 
      QApplication app(argc, argv);
      SimpleApp *aps= new SimpleApp();
      aps->show();
      return app.exec();
}

        Tworzymy obiekt SimpleApp i wywołujemy jego metodę show()(odziedziczona po QMainWindow) i wtedy możemy zobaczyć efekty naszej pracy.
        Po kompilacji ukazuje się naszym oczom o to taki widok


Za pomocą paru linijek kodu stworzyliśmy ten oto dość prosty program. Prawda że szybko?
<center>

QtDesigner w akcji

</center>

        QtDesigner działa na zasadzie drag&drop. Po lewej stronie znajduje się panel widżetów.
Teraz zrobimy taki sam program jak przedtem ale za pomocą qtdesingera(link do kodu źródłowego przykładu z qtdesignera).
        Tworzymy nowe okno klikając w Plik(file) następnie opcja nowy(new), z listy wybieramy Main Window. Następnie usuwamy QMnuBar(nie jest on tu potrzebny),czyli na naszej formie klikamy prawym przyciskiem myszki w miejscu gdzie jest napisane wpisz tutaj.
Następnie z Panela widżetów przerzucamy na forme wrzucamy(klikamy i przeciągamy na formę wybrany widget) Text Edit, i dwa razy Push Button. Następnie klikamy prawym przyciskiem myszki(w skrócie ppm) na dowolne puste miejsce na formie i wybieramy z listy rozmieść a następnie rozmieść w poziomie. Po tej operacji kilkamy ppm na Text Edit i wybieramy z listy zmień nazwę obiektu, nazwę zmieniamy na text_edit, operacje powtarzamy dla dwóch przycisków Push Button, pierwszy nazywamy add_text , a drugi show_dial. Teraz zmienimy napis na przyciskach ,klikamy ppm na pierwszy i wybieramy zmień tekst wpisujemy Add Text, podobnie postępujemy z drugim przyciskiem teraz wpisujemy Show Dial. Zapisujemy teraz w folderze z naszym kodem źródłowym aplikacji (plik nazywamy simple.ui). Po tym musimy dodać do qmake nasz stworzony plik formatki. Jak? Cóż do zależy od środowiska IDE, w moim linuksowym kdevelop jest to w zasadzie robione automatycznie a konkretniej tworze nowy plik z listy wybieram qt4 Main Window wtedy zostaje automatycznie uruchomiony qtdesigner i dodany plik do qmake(w nowym IDE do qt4 od twórców qt4 jak się orientuje procedura jest podobna ale warto wspomnieć że ich środowisko jest cross-platformowe).
        Przechodzimy do kodowania, choć QtDesigner posiada wiele opcji ,m.in edytor slotów itp. My jednak nie skorzystamy z tych opcji. W zasadzie kod będzie podobny do poprzedniego
plik simpleapp.h
#include "ui_simple.h"//1
 
 
 
class SimpleApp:public QMainWindow ,public Ui::MainWindow//2
{
      Q_OBJECT
 
public:
      SimpleApp(QWidget *parent=0);
      ~SimpleApp();
 
 
private slots:
     void ShowDial();
     void AddText();
 
 
private://3
 
};

Plik simpleapp.h skrócił się dość mocno.
1.To jest bardzo ważne. Z pliku simple.ui zostanie utworzony plik ui_simple.h , oczywiście zostanie to wykonane automatycznie, mówiąc ogólnie konstrukcja wygląda tak kiedy mamy jakiś plik xxx.ui stworzony w qtdesignerze i dodany w qmake wtedy w pliku w którym ma być wykorzystane okno stworzone w qtdesignerze wystarczy wpisać #include „ui_xxx.h”.
2.dziedziczymy po odpowiedniej klasie z pliku ui_simple.h, Choć w pliku ui_simple.h znajduje się klasa o nazwie Ui_MainWindow to zawsze mimo to trzeba stosować konstrukcję Ui::nazwaklasy
3.W zasadzie private można usunąć ale zostawiłem to po to aby było widać że nie musimy już nic dodawać, wszystko jest w klasie odziedziczonej z ui_simple.h
plik simpleapp.cpp
SimpleApp::SimpleApp(QWidget *parent):QMainWindow(parent){
        setupUi(this);
        connect(add_text,SIGNAL(clicked()),this,SLOT(AddText()));
        connect(show_dial,SIGNAL(clicked()),this,SLOT(ShowDial()));
 
 
}
 
SimpleApp::~SimpleApp(){}
 
void SimpleApp::ShowDial(){
        QMessageBox::information(this, tr("jupi"),
                 tr("click ok"));
}
 
void SimpleApp::AddText(){
        text_edit->clear();
        text_edit->setText("Simple Text");
}

Jak widać konstruktor znacznie się skrócił. Funkcja setupUi służy do ustawienia interfejsu użytkownika, jako parametr podajemy this(robi się tak zazwyczaj). Teraz warto zajrzeć to pliku ui_simple.h . Gdy tam zajrzymy zobaczymy że add_text i inne widgety są publiczne. Czyli możemy  się do nich nawet z różnych klas odwoływać bezpośrednio(oczywiście jeśli damy taką możliwość innym klasą). Reszta w zasadzie jest taka sama. Mówiąc krótko dzięki qtdesignerowi napisaliśmy mniej kodu :P.

<center>

Podsumowanie

</center>
Oczywiście ledwie lizneliśmy qt4. Zobaczyliśmy jak łatwo i szybko można tworzyć dzięki qt4 aplikacje. Zachęcam do przejrzenia dokumentacji qt4, znajdziemy tam 3 tutoriale i sporo przykładów. Miłego kodowania


3 komentarze

hck 2009-03-21 19:42

Mógłbyś zapodać statyczne liby :D

revcorey 2009-03-12 22:07

gtkmm to chyba biblioteka dla gtk+, czy chodzi o to czy działa w środowisku wyk.gtk? No ale mówisz że ci pod kde buttony nie działały jak mnie pamięć nie myli to i pod gnome i kde wszystko działało
BTW. W ogóle się podoba? Bo być może niedługo coś następnego o qt4 się ukaże, sugestie mile widziane :)

Petrus (newbie 4 ever) 2009-03-12 21:37

Czy to działa pod każdym środowiskiem graficznym, bo jak pisałem w gtkmm to pod kde mi buttony nie działały xD