Wprowadzenie do programowania w C++ z użyciem biblioteki Qt4.

Wstęp

Zaczynając swoją przygodę z programowaniem byłem bardzo rozczarowany tym, że w książce o podstawach C++ są opisywane takie rzeczy, jak proste działania matematyczne – dziwiłem się, jak można tworzyć skomplikowane programy graficzne, jak tu uczą od nowa działań arytmetycznych i logicznych i to na prostych funkcjach. Frajda zaczęła się po poznaniu klas i zakończeniu książki. Słyszałem wiele dobrego o bibliotece Qt, więc chciałem spróbować swoich sił. Próżno szukałem polskich artykułów o programowaniu przy użyciu Qt4, o książkach już nie wspomnę. W ostatnim czasie na naszym rynku pojawiła się jedna pozycja traktująca o WxWidgets i Qt4 – z ciekawości ją kupiłem, ale tyle, co w niej jest, to niestety, a może i stety, zdążyłem się nauczyć sam korzystając z dokumentacji biblioteki oraz z jednego rozdziału jakiejś anglojęzycznej książki. Głównie z powodu niedostatku literatury na ten temat, postanowiłem rozpocząć serię tych artykułów.



Dlaczego Qt4?

Qt4 to zestaw bibliotek, dzięki którym programista może stworzyć aplikacje działające na Linuksie, Mac OS X, Windowsie i innych systemach osbsługiwanych przez Qt bez pisania oddzielnego kodu dla poszczególnych systemów – wystarczy napisać jeden kod i skompilować go pod danym systemem. Dzięki Qt można tworzyć zarówno aplikacje konsolowe, jak i graficzne. Aplikacje graficzne posidają obsługę styli, dzięki czemu można łatwo zmieniać ich wygląd. W Qt znajdziemy moduły odpowiadające za poszczególne funkcje. Poniżej zestawienie ważniejszych modułów w formie tabeli:


Nazwa modułuZastosowanieUwagi
QtCorePodstawowa biblioteka do tworzenia aplikacji konsolowych, zawiera podstawowe klasy Qt.Brak.
QtGuiBiblioteka do tworzenia aplikacji z graficznym interfejsem użytkownika. Zawiera klasy odpowiedzialne za różne elementy interfejsu oraz ich rysowanie.Brak.
QtNetworkBiblioteka implementująca obsługę protokołów sieciowych oraz informacji o interfejsach i adresach sieciowych.Brak.
QtOpenGLBiblioteka odpowiedzialna za obsługę OpenGL. Pozwala na tworzenie elementów interfejsu korzystających z OpenGL.Brak.
QtDBusBiblioteka pozwalająca na komunikowanie się aplikacji z Dbus – systemem komunikacji międyprocesowej, dzięki czemu nasza aplikacja może komunikować się z innymi, jak również odwrotnie.Dbus działa na systemach uniksowych, jednakże powstaje także port na system Windows.
QtSvgBiblioteka odpowiedzialna za wyświetlanie wektorowej grafiki SVG.Brak.
PhononBiblioteka odpowiedzialna za obsługę multimediów.Rodzaje obsługiwanych typów plików zależą od zainstaowanego silnika multimedialnego w systemie (w Linuksie Qt4 domyślnie używa Gstreamera).

Wymienione moduły to tylko część z tych, które oferuje Qt. Niewątpliwą dużą zaletą zestawu bibliotek Qt4 jest dobra i szczegółowa dokumentacja, zawierająca wiele przykładów wykorzystania poszczególnych klas oraz graficzne narzędzia takie jak QtDesigner do intuicyjnego tworzenia interfejsu użytkownika, czy QtAssistant służący do wyświetlania wspomnianej dokumentacji. Kolejnym atutem jest kompilator metaobiektów – to ten mechanizm odpowiada za połączenia sygnałów i slotów (gniazd).


Wymagania od użytkownika

Użytkownik powinien znać podstawy (składnię i podstawowe pojęcia) języka C++ oraz podstawy języka angielskiego, ponieważ dokumentacja, jak i środowisko programistyczne, z którego będziemy korzystać dostępne są tylko w tym języku.



Instalacja

W większości dystrybucji Linuksa, Qt4 znajduje się w repozytoriach, tak więc w menedżerze oprogramowania należy wyszukać qt4 i zainstalować pakiet z przyrostkiem dev lub devel (w zależności od dystrybucji). Należy także mieć zainstalowany kompilator języka C++, zwykle GCC (g++). W przypadku Ubuntu i jego pochodnych wystarczy zainstalować pakiety libqt4-dev i build-essential.



O sygnałach i slotach

Cóż to takiego? To bardzo proste – slot (inaczej gniazdo), to metoda klasy, która może odbierać sygnały. Na przykład: Mamy obiekt oknoGlowne, który jest instancją klasy QMainWindow, a w oknie obiekt przyciskZamknik, który jest instancją klasy QPushButton, a który będziemy używać do zamykania okna. Przycisk po kliknięciu emituje sygnał clicked() (ang. kliknięty). Sygnał ten połączymy ze slotem close() (ang. zamknij) obiektu oknoGlowne. Definiowanie połączenia wygląda następująco:

connect( obiektWysyłającySygnał, SIGNAL(nazwaSygnału(argumenty sygnału)), obiektDocelowy, SLOT(nazwaSlotuObiektuDocelowego(argumenty slotu)) );


Tak więc w naszym przypadku połączenie będzie wyglądało następująco:


connect( przyciskZamknik, SIGNAL(clicked()), oknoGlowne, SLOT(close()) );


Proste, nieprawdaż? Przejdźmy jednak do tego, co można, a co nie można:

  1. Jeden sygnał można połączyć z wieloma slotami.

  2. Sygnał można połączyć z innym sygnałem (ten drugi zostanie wtedy natychmiastowo wyemitowany).

  3. Sygnał może posiadać więcej argumentów, niż slot, który ma odebrać sygnał.

  4. Argumenty sygnału i slotu muszą być tego samego typu.

  5. Nie można połączyć dwóch slotów ze sobą.

  6. Sygnał może mieć więcej argumentów, niż slot (lub sygnał) z którym go łączymy.

  7. Nie można przekazać przez sygnał mniej argumentów, niż wymaga ich slot do którego chcemy połączyć ten sygnał – nie jest to możliwe, bo nie wiadomo, jak wywołać slot (lub inny sygnał) nie podając mu argumentu, który jest wymagany.

  8. Nie można zaimplementować ciała sygnału, jak np. metody – sygnał nie jest metodą, jak slot.


Jak definiować sygnały i sloty?

Aby nasza klasa mogła obsługiwać sygnały i sloty, musi ona dziedziczyć po klasie

QObject (lub innej dziedziczącej po QObject) oraz zawierać makro Q_OBJECT. Slot jest zwykłą metodą klasy, którą musimy zaimplementować. Jednak przy modyfikatorze dostępu danej metody należy dopisać magiczne słowo slots. Na przykład:


class MojaKlasa: public QObject
{
Q_OBJECT //niezbędne, by korzystać z sygnałów i slotów
public:
MojaKlasa() {}; //konstruktor

public slots:
void mojSlot(int x)
{
qDebug() << x; // -wypisuje w oknie terminala
wartość argumentu x

emit mojSygnal1(); // -emituje sygnał mojSygnal1

emit mojSygnal2(x); // -emituje sygnał mojSygnal2
z argumentem x
}

signals:
void mojSygnal1();
void mojSygnal2(int jakasLiczba);
};


Rozgrzewka

Dla rozgrzewki stworzymy pierwszy prosty program, a z tej okazji, że dziś Sylwester, nie będzie to klasyczny Hello world, a Happy New Year ;) Do tego wystarczy nam zwykły edytor tekstu. Tak więc do dzieła – tworzymy plik main.cpp o następującej zawartości:


#include <QtGui> // [1]

int main( int argc, char *argv[] )
{
QApplication aplikacja( argc, argv ); // [2]
QPushButton przycisk( "Happy New Year!" ); // [3]

aplikacja.connect( &przycisk, SIGNAL(clicked()),
&aplikacja, SLOT(quit()) ); // [4]
przycisk.show(); // [5]

return aplikacja.exec(); // [6]
}


Objaśnienia (nawiasy kwadratowe w przykładzie):

  1. Załączamy plik nagłówkowy modułu QtGui. Zamiast dołączać nagłówek całego modułu, moglibyśmy dać nagłówki dla QApplication i QPushButton, bo tylko z tych dwóch klas korzystamy.

  2. Tworzymy obiekt aplikacji. Należy zawsze tworzyć ten obiekt, ponieważ bez niego niektóre klasy Qt nie mogą działać, co może skutkować nieokreślonym zachowaniem aplikacji.

  3. Tworzymy obiekt przycisk, który jest instancją klasy QPushButton. Klasa ta ma kilka konstruktorów, w tym jednym z nich jest użyty przez nas, który pobiera jako argument łańcuch znaków (napis), który ma zostać wyświetlony na przycisku.

  4. Wywołaliśmy aplikacja.connect(), ponieważ connect jest metodą klasy QObject, a QApplication po niej dziedziczy. Jeśli chcielibyśmy połączyć sygnały/sloty w naszej klasie, która dziedziczy po QObject, to wtedy używamy bezpośredniego wywołania metody connect(). Metoda ta pobiera adresy do obiektów, a nie same obiekty, dlatego musieliśmy nazwy naszych obiektów poprzedzić znakiem & - jeśli tworzyli byśmy obiekt na stercie (QPushButton *przycisk = new QPushButton( "Happy New Year" );), zmienna przycisk byłaby wskaźnikiem do tego obiektu i wtedy symbol & jest zbędny (oznaczałby wtedy uzyskanie obiektu znajdującego się pod adresem wskazywanym przez wskaźnik).

  5. Metoda show() wyświetla przycisk na ekranie. QPushButton dziedziczy klasie bazowej dla wszystkich widgetów (inaczej kontrolek) – QWidget. To właśnie w klasie QWidget jest zdefiniowana ta metoda.

  6. Zwracamy wartość, która zostanie zwrócona przez metodę exec() naszej aplikacji. Metoda ta uruchamia główną pętlę zdarzeń aplikacji, dzięki czemu program nie zostaje od razu zakończony tak, jak w przypadku gdybyśmy podali wartość np. 0 zamiast wywołać tę metodę klasy QApplication. Pętla ta jest przerywana przez slot quit(), z którym połączyliśmy sygnał kliknięcia przycisku.

Po zapisaniu pliku w nowym folderze, np. MojaAplikacja, otwieramy terminal, w którym przechodzimy do folderu z zapisanym plikiem main.cpp:


cd /sciezka/do/folderu/MojaAplikacja

Po czym wykonujemy kolejno trzy polecenia (qmake-qt4 w niektórych systemach może występować jako qmake – polecenie z przyrostkiem qt4 występuje w Ubuntu i jego pochodnych):


qmake-qt4 -project
qmake
make

Po pomyślnym skompilowaniu powinien pojawić się plik wykonywalny MojaAplikacja, który możemy uruchomić jak zwykły program poprzez podwójne kliknięcie myszą, lub z terminala, będąc w folderze aplikacji poprzez:


./MojaAplikacja


Zakończenie

Mam nadzieję, że wprowadziłem Was w dość przystępny sposób do programowania obiektowego w C++ z użyciem biblioteki Qt4. W następnej części rozpocznę od wprowadzenia do środowiska programistycznego QtCreator oraz kilku prostych przykładów. W kolejnych częściach będziemy się zagłębiać coraz bardziej w tajniki Qt4.

15 komentarze:

Unknown pisze...
31 grudnia 2008 19:01

Bardzo fajne wprowadzenie. Zobaczymy jak będzie dalej :D
Fajnie, że planujesz opisanie programowania przy pomocy QtCreatora, a czy dalej będzie graficzny interfejs tworzony tylko w kreatorze czy planujesz także część rzeczy przedstawić także w postaci kodu?

Unknown pisze...
1 stycznia 2009 03:05

REWELACJA, na to czekałem :D
Trzymam kciuki za następne odcinki :)

Unknown pisze...
1 stycznia 2009 03:10

Mam nadzieje ze nie zrezygnujesz. na taki stuff czekamy :)

PrzemCio pisze...
1 stycznia 2009 03:39

Podłączam się pod poprzedników!

Bardzo dobrze się zapowiada, czekamy na kolejne odcinki :)

Anonimowy pisze...
1 stycznia 2009 11:13

Co tu dużo mówić, czekam na dalsze części - powodzenia i wytrwałości w 2009 roku ;)

Unknown pisze...
1 stycznia 2009 13:38

Super , dobrze że kolejna osoba promuje ten zestaw bibliotek. I super że chcesz omawiać to przy użyciu QTCreator-a. W ogóle to środowisko zapowiada się niezwykle ciekawie.

Powodzenia i wytrwałości w postanowieniu :)

Anonimowy pisze...
2 stycznia 2009 02:15

Yeach. Użrwaj Qt i bądź najwolniejszy na świecie:-(

Anonimowy pisze...
2 stycznia 2009 12:15

"Aby nasza klasa mogła obsługiwać sygnały i sloty [...]"
Czy tylko ja miałem takie dziwne skojarzenie z popularnym serwisem społecznościowym? ;)

Oby jak najwięcej takich artykułów ;)

Unknown pisze...
2 stycznia 2009 14:51

piekna inicjatywa, zycze konsekwencji w podjetym zadaniu.

Unknown pisze...
3 stycznia 2009 14:10

Dzięki bardzo :-)
A tak pytanie przy okazji - wybierasz się może na polibudę w Lbn?

adrian5632 pisze...
3 stycznia 2009 15:08

Dzięki za komentarze. Nie spodziewałem się, że będą aż tak pozytywne ;)

@Paweł: jak na razie w planach mam Poznań :P

oldfield pisze...
29 stycznia 2009 04:51

Kiedy druga lekcja ? ;)

Nie naciskam, ale w tym tempie publikacji to i do 2010 nie załapie podstaw ;)

adrian5632 pisze...
29 stycznia 2009 08:31

Jutro sie ferie zczynaja, wiec bede mial czas, zeby 'powykladac' :P Dodatkowo tym razem bedzie mniej tekstu, ale za to wiecej filmikow :P

pieczara pisze...
29 stycznia 2009 23:30

Fajnie jakby dalo rade troszke czesciej :D Teksty czy filmiki... bez roznicy byle tlumaczenie bylo - po co i na co. Screencasty tez sa ok.

Unknown pisze...
10 czerwca 2009 07:55

i gdzie ten kurs...

Prześlij komentarz