C GUI QT programowanie

background image

1

Leszek Klich

PROGRAMOWANIE OBIEKTOWE W JĘZYKU C++

Multiplatformowa biblioteka QT

LICECJA :

Powielanie lub wykorzystywanie jest możliwe tylko w celach edukacyjnych.
Przy wykorzystywaniu dowolnych algorytmów lub kodów źródłowych z
niniejszego opracowania należy podać imię i nazwisko autora. Wyrażam zgodę
na wykorzystywanie niniejszych kodów do celów komercyjnych pod warunkiem
podania w informacji o programie imienia i nazwiska autora.

Jeśli chcesz wykorzystać dowolny tekst z opracowania na swojej stronie www –
skontaktuj się ze mną

leszek-klich@wp.pl

background image

2

W niniejszym opracowaniu moim głównym celem było zwrócenie uwagi na

alternatywne narzędzie programistyczne. Jednym z najlepszych, według mnie, jest właśnie biblioteka
QT firmy Trolltech. Pisane za pomocą jej programy są praktycznie przenośne pomiędzy wieloma
systemami operacyjnymi (Windows/Linux/Unix/Mac OS). Biblioteka jest bardzo znana w systemie
Linux, lecz w systemie Windows większość programistów wykorzystuje narzędzia typu RAD w stylu
Visual Basic czy Delphi. Nie staram się przez to powiedzieć, że narzędzia te są niepraktyczne. Pragnę
jedynie zwrócić uwagę na istnienie narzędzi, które oferują pracę w zbliżonym do ideału dialekcie
obiektowego języka C++ a to jest w czasie obecnym niemal niespotykane.

background image

3

1

1

.

.

C

C

e

e

l

l

n

n

i

i

n

n

i

i

e

e

j

j

s

s

z

z

e

e

g

g

o

o

o

o

p

p

r

r

a

a

c

c

o

o

w

w

a

a

n

n

i

i

a

a

Praca ta jest skierowana dla wszystkich znających podstawy programowania

proceduralnego w języku C++. Jej treść obejmuje wstępne zagadnienia i teorię

programowania obiektowego oraz praktyczne wykorzystanie wiadomości do budowania

okienkowych aplikacji. W pierwszej kolejności pozwala zapoznać się z mechanizmem i

filozofią programowania obiektowego. W kolejnych rozdziałach opisuje jedną z metod

programowania aplikacji przeznaczonych dla środowisk Windows oraz XWindow.

Krok po kroku opisuje najbardziej przydatne klasy i metody wybranej biblioteki

programowania a opis każdej z nich kończy się praktycznym przykładem. Do każdej z klas

dostępne są także tabele wraz z właściwościami obiektu oraz najważniejszymi metodami.

Dzięki takiemu podejściu niniejsza praca może służyć jako podręczny przewodnik po

elementach biblioteki programisty wykorzystywany podczas pisania programów.

Do niniejszej pracy dołączyłem także płytę CD. Umieściłem na niej bibliotekę QT,

niezbędne narzędzia oraz wszystkie programy w wersji binarnej oraz źródłowej. Muszę w

tym miejscu dodać, że opisywane w niniejszej pracy metody programistyczne nie są

standardowe. Unikam tutaj wykorzystywania nowoczesnych narzędzi typu RAD

1

służących

do budowy aplikacji i interfejsów użytkownika. Zamiast narzędzi tego typu skupiłem się na

„ręcznym” pisaniu szkieletu aplikacji. Takie podejście wymaga od programisty większego

zaangażowania i wiedzy, ale dzięki takiej metodzie można szczegółowo kontrolować rozwój

projektu. Oprócz kwestii typowo praktycznych, takie podejście programisty umożliwia

bezpośredni kontakt z kodem źródłowym aplikacji, co jest nie bez znaczenia dla

programistów wywodzących się z ruchu Open Source

2

i Linuksa.

W niniejszej pracy przedstawiłem bibliotekę języka C++ umożliwiającą pisanie

przenośnych aplikacji opartych o wygodny dialog z użytkownikiem za pomocą myszki oraz

klawiatury. Zdecydowałem się na bibliotekę QT rozprowadzaną wraz z kodem źródłowym,

gdyż w inne tego typu biblioteki, w obecnej fazie rozwoju nie pozwalają na wygodną i

stabilną pracę w systemach Windows. Jako dodatkowe kryterium, którym się kierowałem

była przenośność kodu pomiędzy platformami Windows oraz Linux. Biblioteka QT jest

dostępna bezpłatnie dla wszystkich. Jest standardowo instalowana w większości dystrybucji

1

RAD – (Rapid Application Development) oznacza program służący do szybkiego budowania aplikacji.

Przykładem może być pakiet Borland C++ Builder.

2

Open Source – idea polegająca na dostarczaniu wraz z aplikacją pełnego kodu źródłowego.

background image

4

Linuksa. W tym systemie otrzymujemy ją wraz z kodem źródłowym. W środowisku

Windows biblioteka jest dostępna bezpłatnie pod warunkiem, że nie będzie wykorzystywana

do celów komercyjnych.

Poprzez tę pracę pragnę zgłębić sztukę programowania obiektowego w języku C++

oraz zapoznać się z możliwością jej wykorzystania w przyszłości. Niestety, choć do

produktów takich gigantów jak Borland czy Microsoft dostępne jest wiele wyczerpujących

opisów, książek oraz kursów, to w przypadku pozostałych pozostaje przeszukiwanie sieci

Internet, czytanie list dyskusyjnych i składanie w całość często anglojęzycznej i niepełnej

dokumentacji.

Moje badania dotyczą tylko jednej z wielu dostępnych bibliotek, co było

podyktowane ograniczonym rozmiarem pracy i ogromem niniejszego tematu. Na początku

starałem się przedstawić w skrócie model programowania obiektowego. Następnie

skoncentrowałem się na tworzeniu aplikacji za pomocą wybranej przeze mnie biblioteki.

Opisując ją będę starał się przedstawić jej zalety i wady, przydatność dla programisty oraz

względną stabilność w środowiskach Windows oraz Linux, co jest nie bez znaczenia przy

zastosowaniach komercyjnych. Oczywiście skupię się jedynie na najważniejszych cechach

tej biblioteki i postaram się scharakteryzować najważniejsze z dostępnych klas i metod. W

dalszej części pracy napisałem kilka programów za pomocą opisywanej biblioteki.

background image

5

2

2

.

.

Z

Z

a

a

r

r

y

y

s

s

h

h

i

i

s

s

t

t

o

o

r

r

i

i

i

i

i

i

e

e

w

w

o

o

l

l

u

u

c

c

j

j

i

i

j

j

ę

ę

z

z

y

y

k

k

a

a

C

C

i

i

C

C

+

+

+

+

Od początku powstania komputerów, w wielu ośrodkach badawczych prowadzono

badania nad językiem programowania, który łączył by w sobie cechy języka doskonałego

3

.

Miał to być język jak najwyższego poziomu, w którym łatwo definiować algorytmy oraz

który przejawiał niskie zapotrzebowanie na pamięć i moc obliczeniową. Z drugiej strony

musiał być językiem na tyle niskiego poziomu, aby tworzone algorytmy były wykonywane

szybko i optymalnie. Z uwagi na wiele dostepnych modeli komputerów należało opracować

język uniwersalny, niezależny od maszyny, na którzym jest wykonywany. Uniezależnienie

języka od konkretnej maszyny gwarantowałoby sukces produktu. Niestety w praktyce

szybko przekonano się, że napisanie takiego języka nie jest w ogóle możliwe. W latach

siedemdziesiątych pojawił się jednak język, który bardzo zbliżył się do tego ideału. Językiem

tym był produkt o nazwie „C”. Pierwszy kompilator tego języka powstał w roku 1972.

Zdefiniował go Dennis M. Ritchie

4

z Bell Laboratories w New Jersey. Język ten opierał się

na języku “B” opracowanym dwa lata wcześniej przez Kena Thompsona

5

. Język B zaś

wywodził się z języka BCPL i powstał w University Mathematical Laboratories w

Cambridge w roku 1969. Język B i Asembler były językami, za pomocą których napisano

pierwszy system operacyjny UNIX. Choć język C ewoluował, to jednak pozostał tym samym

językiem programowania, co jego pierwowzór. Język ten znajdziemy w każdej dystrybucji

systemu UNIX oraz jego darmowych klonów takich jak LINUX. Jest to z założenia język

ogólnego przeznaczenia. Początkowo jednak miał służyć do pisania kompilatorów

i systemów operacyjnych. Nowsze kompilatory tego języka były dostarczane z bibliotekami,

które sprawiły, iż C może służyć w wielu dziedzinach informatyki. Choć na rynku były

dostępne inne języki programowania, to jednak przejrzyste struktury danych oraz prostota

wyrażeń sprawiła, że język ten długo dzierżył palmę pierwszeństwa na rynku kompilatorów.

Programowanie w samym języku C było jednak na tyle trudne, że nieliczne grono

programistów, znających język C, było wręcz rozchwytywane i doskonale opłacane przez

wielkie firmy. Inne języki programowania dostępne wówczas na rynku takie jak: Basic,

Fortran czy Pascal, choć prostsze w zastosowaniu, były zbyt mało uniwersalne. Działo się

tak dlatego, że przy ich tworzeniu kierowano się określonym z góry zastosowaniem.

3

B. W. Kernighan, D. Ritchie, Język C, Warszawa 1988, str. 9

4

Tamże, str. 9

5

Tamże, str. 9

background image

6

Zarówno C jak i inne języki wykorzystywały programowanie strukturalne. Ten model

programowania opierał się na dzieleniu programu na funkcjonalne bloki, które miały za

zadanie wykonywanie określonych czynności. Dzięki temu program stawał się bardziej

czytelny w przypadku bardzo dużych i rozbudowanych programów. Programowanie

proceduralne miało jednak wady; uniemożliwiało łatwe przekształcanie już istniejących

programów w inne, a w przypadku gdy zaszła konieczność modyfikacji lub rozbudowy

istniejącego oprogramowania konieczne było pisanie nowych funkcji, zamiast efektywnego

wykorzystywania tych gotowych. Zaczęto więc poszukiwać innych, bardziej efektywnych,

technik programowania.

Istotną zmianę i zwrot w tej dziedzinie wprowadził Bjarne Stroustrup

6

pracujący w

Computer Science Research Center of AT&T Bell Labs in Murray Hill. Opracował on nowy

standard języka C i zmienił tym samym podejście do pisania programów. Tym standardem

było programowanie zorientowane obiektowo (Object Oriented Programming). Swój język

nazwał C++ wskazując, że jest to ulepszony język C. Model obiektowy zrewolucjonizował

pisanie programów komputerowych. Dzięki programowaniu zorientowanemu obiektowo

można było łatwo wykorzystywać stare programy do nowych funkcji przy pomocy

dziedziczenia czy polimorfizmu. Programy stały się bardziej odporne na błędy programisty

i tym samym bezpieczniejsze dzięki wykorzystaniu metod prywatnych. Składnia języka

obiektowego jest co prawda trudniejsza w implementacji, ale prostsza w wykorzystaniu

i dodatkowo bardziej czytelna. Programowanie obiektowe nakłada na programistę

konieczność myślenia o programach jako o typach danych, a później o operacjach

(metodach) specyficznych dla poszczególnych typów danych. Języki proceduralne zaś

odwracają priorytety i stosują szczególny nacisk na procedury, a dopiero potem na dane, na

których te procedury operują.

Sama koncepcja techniki programowania obiektowego pojawiła się w języku Simula

67. Był to język zaprojektowany z myślą o symulacji różnych procesów. Prawdopodobnie

programowanie obiektowe istnieje dzięki grupie naukowców, którzy pracowali nad

symulacjami zachowania się statków. Mieli oni kłopoty z opanowaniem wszystkich

zależności, jakie wywierały na siebie nawzajem wszystkie parametry statków podczas

symulacji. Wtedy wpadli na pomysł, aby pogrupować typy statków w różne klasy obiektów,

a każda z klas sama odpowiadałaby za określanie własnych danych i zachowań. Nowa

6

B. Stourstrup, Język C++, Warszawa 2002

background image

7

koncepcja podejścia do programowania dopracowana w języku Smalltalk, stworzonym w

języku Simula w firmie Xerox PARC. Programiści zaprojektowli jednak w pełni dynamiczny

system, w którym obiekty mogą być tworzone i modyfikowane "w locie", a nie system

oparty na statycznych programach.

Programowanie obiektowe zyskało status techniki dominującej w połowie lat 80-tych,

głównie ze względu na wpływ C++, stanowiącego rozszerzenie języka

C

. Dominacja C++

została utrwalona przez wzrost popularności graficznych interfejsów użytkownika (GUI), do

tworzenia których programowanie obiektowe nadaje się szczególnie dobrze.

Najważniejsze cechy programowania obiektowego to:

Abstrakcja

Każdy obiekt w systemie służy jako model abstrakcyjnego "wykonawcy", który może

wykonywać pracę, opisywać i zmieniać swój stan oraz komunikować się z innymi

obiektami w systemie bez ujawniania, w jaki sposób zaimplementowano dane cechy.

Procesy, funkcje lub metody mogą być również abstrahowane, a kiedy tak się dzieje,

konieczne są rozmaite techniki rozszerzania abstrakcji.

Enkapsulacja (ukrywanie implementacji, hermetyzacja)

Zapewnia, że obiekt nie może zmieniać stanu wewnętrznego innych obiektów w

nieoczekiwany sposób. Tylko wewnętrzne metody obiektu są uprawnione do zmiany

jego stanu. Każdy typ obiektu prezentuje innym obiektom swój "interfejs", który

określa dopuszczalne metody współpracy. Pewne języki osłabiają to założenie,

dopuszczając pewien poziom bezpośredniego (kontrolowanego) dostępu do

"wnętrzności" obiektu. Ograniczają w ten sposób poziom abstrakcji.

Polimorfizm

Referencje i kolekcje obiektów mogą dotyczyć obiektów różnego typu, a wywołanie

metody dla referencji spowoduje zachowanie odpowiednie dla pełnego typu obiektu

wywoływanego. Jeśli dzieje się to w czasie działania programu, to nazywa się to

późnym wiązaniem lub wiązaniem dynamicznym. Niektóre języki udostępniają

bardziej statyczne (w trakcie kompilacji) rozwiązania polimorfizmu – na przykład

szablony i przeciążanie operatorów w C++.

background image

8

Dziedziczenie

Porządkuje i wspomaga polimorfizm i enkapsulację dzięki umożliwieniu

definiowania i tworzenia specjalizowanych obiektów na podstawie bardziej

ogólnych. Dla obiektów specjalizowanych nie trzeba redefiniować całej

funkcjonalności, lecz tylko tą, której nie ma obiekt ogólniejszy. W typowym

przypadku powstają grupy obiektów zwane klasami, oraz grupy klas zwane

drzewami. Odzwierciedlają one wspólne cechy obiektów

7

.

Większość nowoczesnych języków programowania posiada zaimplementowaną

możliwość wspierania programowania obiektowego. Nawet takie produkty, znane głównie

z prostoty i proceduralności jak BASIC czy Pascal, dobrze radzą sobie z obiektami. Niestety

jednak samo dodanie obiektowości do języków, które pierwotnie nie były do niej

przystosowane zrodziło szereg problemów z kompatybilnością. Z kolei "czysto" obiektowym

językom brakowało cech, z których programiści przyzwyczajeni byli korzystać. By zapełnić

tę lukę podejmowano liczne próby stworzenia języków obiektowych dostarczających

jednocześnie "bezpiecznych" elementów proceduralności. Eiffel Bertranda Meyera był

wczesnym przykładem w miarę udanego języka spełniającego te założenia; obecnie został on

w zasadzie całkowicie zastąpiony przez język Java, głównie za sprawą pojawienia się

Internetu, dla którego język ten dostarcza szeregu użytecznych funkcji. Dzisiaj chyba trudno

wyobrazić sobie sieć bez wsparcia tego języka. Muszę dodać, że Java, jak większość

dzisiejszych języków programowania, wywodzi się z języka C, co można zauważyć w ich

bardzo zbliżonej składni.

7

Encyklopedia Wikipedia, pl.wikipedia.org

background image

9

3

3

.

.

N

N

a

a

r

r

z

z

ę

ę

d

d

z

z

i

i

a

a

i

i

k

k

o

o

m

m

p

p

i

i

l

l

a

a

t

t

o

o

r

r

Przygotowanie pakietu MS Visual C++ do współpracy z QT.

Przed kompilacją programów napisanych przy pomocy biblioteki QT, należy

przygotować kompilator do korzystania z dodatkowych plików nagłówkowych oraz

bibliotek. Z menu Tools należy wybrać opcję Options. Z listy rozwijanej Show directories

for: wybrać Include files. Konfiguracja polega na dodaniu do listy katalogów z plikami

nagłówkowymi: C:\Qt\include

Następnie należy podać katalogi bibliotek. Z listy rozwijanej Show directories for: wybrać

Library files i dodać katalogi: C:\Qt\lib Po zakończeniu należy klikniąć klawisz OK, aby

zapisać zmiany. Mamy już możliwość kompilacji programów do pliku pośredniego *.obj.

Aby móc korzystać ze zintegrowanych makr QT należy z menu Tools wybrać opcję

Customize.

Rysunek 1

W oknie Customize należy zaznaczyć opcję QmsDev Developer Studio-Add-In i kliknąć

przycisk Close. Szczegóły konfiguracji ilustruje Rysunek 1.

Rysunek 2

Po zakończeniu konfiguracji na pasku narzędzi pojawią się nowe opcje widoczne na obrazku

nr 2.

background image

10

Kompilator Borland C++

Pakiet instalujemy w wybranym katalogu, np. c:\Bcc55. Następnie do zmiennej

środowiskowej PATH należy dodać ścieżkę dostępu do katalogu c:\Bcc55\bin. W przypadku

systemu Windows 9x, w pliku Autoexec.bat nalezy dopisać na końcu:


set PATH=%PATH%;c:\bcc55\bin

W przypadku Windows 2000/XP we właściwościach systemu (Właściwości systemu ->

Zaawansowane -> Zmienne środowiskowe): należy dodać zmienną środowiskową dla

aktualnego użytkownika:


Nazwa zmiennej: PATH
Wartość zmiennej: %PATH%;c:\bcc55\bin

Konfiguracja pakietu
Aby Borland C++ Compiler 5.5 poprawnie funkcjonował potrzebne są tekstowe pliki

konfiguracyjne: bcc32.cfg i ilink.cfg wskazujące położenie plików nagłówkowych

i bibliotek.


Oto ich przykładowa zawartość:
bcc32.cfg

-I"c:\Bcc55\include"
-L"c:\Bcc55\lib;c:\Bcc55\lib\psdk"

ilink32.cfg

-L"c:\Bcc55\lib;c:\Bcc55\lib\psdk"

Pliki te należy utworzyć w dowolnym edytorze tekstu i zapisać w katalogu bin. Oczywiście

w miejsce przykładowych katalogów należy wstawić właściwe. Standardowo Borland C++

5.5 generuje kod wykonywalny, przeznaczony dla Windows 2000. Z uwagi na istniejące

różnice pomiędzy systemami Windows 95/98/Me a systemem Windows 2000, tak

wygenerowany kod może nie działać pod Windows 95/98/Me. Aby uniknąć tego problemu,

wystarczy przy kompilacji użyć parametrów:


-DWINVER=0x0400
-D_WIN32_WINNT=0x0400

background image

11


Dla ułatwienia pracy najlepiej powyższe linie dopisać do utworzonego pliku

konfiguracyjnego bcc32.cfg.

W przypadku dołączenia dodatkowych bibliotek lub plików nagłówkowych w katalogach

innych niż powyższe, zawartość plików konfiguracyjnych należy oczywiście zmienić. Na

tym kończy się konfiguracja kompilatora - program jest gotowy do pracy.

Standardowa kompilacja wygląda tak:

bcc32 plik1.cpp plik2.cpp plik3.cpp
bcc32 plik1.cpp biblioteka.lib

Poniżej przedstawiam wybrane parametry bcc32:

-tW - tworzenie pliku docelowego EXE jako aplikacji w API WIN32

-tWC - tworzenie pliku docelowego EXE jako aplikacji konsolowej

-tWD - tworzenie biblioteki DLL
-3 - kompilowanie z użyciem instrukcji 80386 (domyślnie)

-4 - kompilowanie z użyciem instrukcji 80486

-5 - kompilowanie z użyciem instrukcji Pentium
-6 - kompilowanie z użyciem instrukcji Pentium Pro

-Ixxx - dodanie ścieżki z plikami nagłówkowymi

-Lxxx - dodanie ścieżki z plikami bibliotek
-c - tylko kompilacja plików

-exxx - nazwa docelowego pliku wykonywalnego

-Ox - wybór poziomu optymalizacji kodu wynikowego
-P - kompilacja programu w C++, bez względu na rozszerzenia plików źródłowych

background image

12

4

4

.

.

G

G

r

r

a

a

f

f

i

i

c

c

z

z

n

n

y

y

i

i

n

n

t

t

e

e

r

r

f

f

e

e

j

j

s

s

u

u

ż

ż

y

y

t

t

k

k

o

o

w

w

n

n

i

i

k

k

a

a

(

(

G

G

U

U

I

I

)

)


Dzisiejsze

komputery

wyposażone są w doskonałe karty graficzne, dużą ilość

pamięci RAM, duże pojemności dysków twardych, przestrzenny dźwięk, napędy optyczne

oraz szereg innych urządzeń. Jednak nie moc obliczeniowa a coraz łatwiejsza obsługa

komputera sprawiła, że stały się one tak popularne. Już wcześniej budowano

superkomputery, które służyły uniwersytetom i wielkim firmom. Niestety jednak obsługa ich

była na tyle skomplikowana, że informatycy je obsługujący uważani byli za komputerowych

guru. Obsługa takiego komputera odbywała się w trybie znakowym, za pomocą mnóstwa

poleceń wspieranych przez wiele dodatkowych parametrów. Sprawiało to, że praktycznie

nikt z ulicy nie miał dostępu do komputera i nie robiono wiele w celu poprawy tej sytuacji.

Z czasem pojawienia się języków obiektowych, zaczęto tworzyć programy zawierające

przyjazne interfejsy komunikacji z użytkownikiem. Programy dalej działały w trybie

znakowym, ale ich obsługa stawała się intuicyjna i nie trzeba było uczyć się już składni

poleceń. Parametry wywołania programu zastąpiono funkcyjnymi elementami zwanymi

widokami. Widoki zaś umieszczane są na formatkach. Formatki zaś, to po prostu dzisiejsze

okienka.

Widoki można podzielić na kilka tych najważniejszych:

•menu
•paski narzędzi
•pole edycyjne
•etykieta
•pole wyboru
•przełączniki radiowe
•pola tekstowe

Można powiedzieć, że GUI

8

, to wizualne sterowanie programem oraz wizualizacja wyników

tego programu. Gdy zdano sobie sprawę z możliwości i potęgi GUI, wiele firm wyposażało

swoje komputery w systemy operacyjne oparte o tryby graficzne. Korzenie graficznego

interfejsu użytkownika sięgają lat 50. W praktyce został on jednak zbudowany w latach 70.

8

Graficzny Interfejs Użytkownika (ang. Graphical User Interface)

background image

13

przez grupę naukowców z Palo Alto Research Center (PARC) Xeroksa zbudowała pierwszy

komputer o nazwie Alto działający pod kontrolą takiego graficznego interfejsu. W 1979 r.

ośrodek PARC odwiedził Steve Jobs, który w Alto dostrzegł przyszłość komputerów

osobistych. Choć spora część interfejsu w komputerach Lisa i Macintosh wykorzystywała

dokonania dokonano z ośrodka PARC to duża część Mac OS-a powstała jeszcze przed

wizytą Jobsa w laboratoriach Xeroksa. Jako przykład dużej roli jaką w naszym życiu

odgrywa owe tajemnicze GUI przypomnę, że zarówno firma Apple (MacOS), jak i Microsoft

(Windows) zaczerpnęły ideę graficznego interfejsu od Xeroksa. Choć niektórzy pamiętają

jeszcze system operacyjny DOS i jego polecenia, to jednak pamiętanie jego poleceń nie jest

już konieczne, szczególnie w dobie Windows XP, gdzie powłoka DOS jest jedynie

emulowana.

W niniejszej pracy jednak nie będę się skupiał na jednym systemie

operacyjnym, ale zajmę się także systemem Linux, ze względu na to, że właśnie na tę

platformę powstało wiele bezpłatnych lub tanich, co nie oznacza gorszych, bibliotek GUI.

Mnie interesowały biblioteki, które cechuje uniwersalność oraz przenośność kodu pomiędzy

platformami. Poniżej przedstawiam zrzuty ekranu przedstawiające interfejsy użytkownika z

dwóch bibliotek: GTK, QT.


Gtk+
Niezwykle popularną biblioteką w systemie Linuks jest GTK+. Choć aplikacje napisane za

pomocą tej biblioteki standardowo pracują w macierzystym systemie Linux, to jednak, w

obecnej wstępnej co prawda fazie rozwoju, źródła można skompilować w systemie Windows

w języku C. Biblioteka GTK jest napisana w języku C jako obiektowo zorientowane API.

Biblioteka oparta jest na bibliotece GDK (Gimp Drawing Kit), która z kolei operuje na

najniższym poziomie XLib w przypadku XWindow. Biblioteka zyskała rozgłos dzięki

Window Managerowi GNOME (the GNU Network Model Environment), który powstał

na podstawie bibliotki.

background image

14

Rysunek 3

Biblioteka oprócz standardowych funkcji oferuje także narzędziami służącymi do

wizualnego tworzenia interfejsów użytkownika. Jednym z najlepszych jest Glade (rysunek

4), który udostępnia wygodny interfejs.

background image

15

Rysunek 4

Narzędzie Glade pomaga w łatwy i szybki sposób zaprojektować okna

dialogowe. Zastosowane zostały tutaj rozwiązania podobne do tych znanych z wielu narzędzi

typu RAD dla Windows – standardowa paleta z narzędziami, pełna kontrola nad

właściwościami każdego obiektu, edycja każdego parametru, itp. Oprócz tworzenia kodu

źródłowego dla przygotowanych okien dialogowych Glade pozwala też oczywiście na

przygotowanie całego projektu z niezbędnymi dodatkami w rodzaju standardowego pliku

makefile czy configure. Biblioteka GTK ma wiele specyficznych cech. Jedną z nich jest to,

że nie wykorzystuje standardowych bibliotek API z danego systemu operacyjnego

i rodzimych widgetów. Zamiast tego biblioteka oferuje własny interfejs graficzny. Dzięki

temu można dowolnie zmieniać wygląd programu napisanego w GTK poprzez stosowanie

tzw. tematów (ang. themes). Jest to nieco podobne do interfejsu graficznego systemu

Windows XP, z tą różnicą, że GTK jest dużo bardziej elastyczna. Architekturę biblioteki Gtk

przedstawia rysunek 5.

background image

16

Rysunek 5

Biblioteka Gtk jest standardowo przystosowana do obsługi wielu języków.

Dzięki internacjonalizacji pisanie wielojęzycznych aplikacji jest bardzo łatwe. Niestety

posiada bardzo poważną wadę. W obecnej wersji przeznaczonej dla Windows, podczas

kompilacji występuje wiele problemów. Standardowa kompilacja nie powiedzie się, jeśli nie

dołączymy do linkera dodatkowych bibliotek gtk oraz gdk. Dodatkowo, do poprawnej pracy

programu wykonywalnego niezbędny jest rozbudowany pakiet uruchomieniowy (tzw.

runtime), który należy zainstalować przed uruchomieniem aplikacji napisanej przy uzyciu

Gtk. Programowanie przy pomocy tej biblioteki w środowisku Linuks jest niezwykle proste

i przejrzyste.

W przypadku systemu Windows jest na dzień dzisiejszy problematyczne. Jest to

biblioteka, która powstała w środowisku Linuks i dla tego środowiska jest przeznaczona.

Wielu programistów pracuje nad przeniesieniem źródeł na okienka Microsoftu i to z dużym

powodzeniem, czego najlepszym przykładem jest sztandarowy program graficzny GIMP

9

do

niedawna dostępny tylko w systemie Linuks, teraz dostępny także dla użytkowników

Windows. Przyszłość biblioteki wydaje się być stabilna i niezachwiana. Szybki jej rozwój

spowodował opracowanie specjalnych, bibliotek umożliwiających pisanie programów

9

Gimp (GNU Image Manipulation Program). Darmowy program graficzny podobny do Adobe Photoshopa.

background image

17

w C++ czy Perlu.

Biblioteka QT

Biblioteka napisana została przez grupę Trolltech

10

w wersjach dla Windows,

Linuxa, Solarisa, HP-UX oraz innych odmian Unixa. Wśród użytkowników Linuksa zyskała

rozgłos dzięki Windows Menagerowi KDE doskonale emulującemu wizualnie okienka znane

z Windows. Biblioteka jest dostępna na licencji komercyjnej, wymagającej opłat

licencyjnych za jej wykorzystanie. Jednak firma Troll Tech, zainteresowana

rozpowszechnianiem swojego produktu, umożliwiła nieodpłatne wykorzystanie biblioteki w

Wolnym Oprogramowaniu. Dzięki nowoczesnej architekturze, przystępnym warunkom

rozpowszechniania i wsparciu firmy komercyjnej, QT zyskała dużą popularność

w środowisku systemu Linuks. W chwili obecnej można wykorzystywać ją bezpłatnie

w zastosowaniach edukacyjnych lub do tworzenia aplikacji dostępnych wraz z kodem

źródłowym (powyższe kwestie reguluje odpowiednia licencja). Od pewnego czasu, dzięki

naciskowi linuksowej społeczności, dostępny jest kod źródłowy tej biblioteki na platformę

Linuks. Dzięki temu, nie ma realnej groźby konieczności płacenia za tworzenie czy też

używanie aplikacji stworzonych za jej pomocą. Biblioteka QT jest przystosowana

do obiektowego języka C++.

W pakiecie oprócz biblioteki dostajemy do ręki silne narzędzie QT Designer,

które służy do budowy interfejsu użytkownika i zarządzania zdarzeniami opartymi w tym

przypadku o technikę sygnał – slot. Dodatkowo w pakiecie można znaleźć generator plików

konfiguracyjnych dla kompilatora qmake, która zwalnia nas z pisania skomplikowanych

plików Makefile. Biblioteka Qt jest zaprojektowana przy pomocy metod obiektowych

i zaimplementowana w języku C++.

10

www.trolltech.no – strona domowa producenta biblioteki QT

background image

18

Elementy interfejsu graficznego są implementowane jako klasy w języku C++

i komunikują się przez mechanizmy sygnałów i slotów. Sygnały są emitowane przez

elementy interfejsu w związku ze zdarzeniami, jakie w nim zachodzą. Przykładowo,

naciśnięcie przycisku powoduje wyemitowanie sygnału zawierającego informację o tym, jaki

przycisk został naciśnięty.

Sloty są funkcjami (metodami), które są wywołane w przypadku przechwycenia

zdefiniowanych dla danego slotu sygnałów. Wraz z biblioteką otrzymujemy narzędzie

o nazwie QTDesigner (rysunek 6).

Rysunek 6

Aplikacja jest bardzo przydatna do projektowania formatek.

Po zaprojektowaniu metodą przenieś i upuść potrafi w prosty sposób wygenerować kod

XML, z którego można już tworzyć źródła w języku C++.

background image

19

KDevelop

Jest

to

zintegrowane

środowisko programistyczne wzorowane na Visual Studio

(rysunek 7). Wspiera tworzenie standardowych aplikacji konsolowych, aplikacji QT i KDE

11

oraz GTK+. Jest to graficzna nakładka na inne narzędzia, takie jak gcc, make, autoconf, Perl

i inne. Ale niewątpliwie znacznie ułatwia tworzenie programu. Tworząc w KDevelop nowy

projekt wykorzystujemy gotowego wizarda, który tworzy za nas wszystkie podstawowe pliki

(READ-ME, IN STALE, LICENCE, automake, autoconf itp.). Pozwala to uniknąć żmudnej

(i niezbyt łatwej) procedury ręcznego ich tworzenia i zapewnia, że nasz projekt od początku

będzie dobrze przygotowany i zgodny z obowiązującymi w środowisku GNU normami

(skrypt ./configure, podstawowe informacje i pliki). KDevelop potrafi też generować

dokumentację oraz tworzyć dystrybucje naszego projektu (w postaci paczek RPM i TGZ).

Zawiera też wsparcie dla tłumaczeń i wspomaga debugowanie programów.

Rysunek 7

KDevelop zawiera przeglądarkę dokumentacji, do której możemy dołączać

katalogi z dodatkową dokumentacją (wykorzystując programy takie jak htdig można tworzyć

indeksy dla wszystkich dołączonych katalogów). Dokumentacja jest dostępna w postaci

11

KDE – Menadżer okien z systemu Linuks

background image

20

wygodnego panelu z drzewkiem po lewej stronie i tekstem po prawej (podobnie jak np. w

MSDN). Niestety to doskonałe narzędzie jest przeznaczone jedynie na systemy operacyjne

Lunux. Jego najbliższym odpowiednikiem w systemie Windows jest MS Visual C++.

background image

21

5

5

.

.

P

P

r

r

z

z

y

y

k

k

ł

ł

a

a

d

d

y

y

w

w

i

i

d

d

o

o

k

k

ó

ó

w

w

b

b

i

i

b

b

l

l

i

i

o

o

t

t

e

e

k

k

i

i

Q

Q

T

T

W systemie Linuks bardzo popularnym tematem biblioteki QT jest Motif lub

Windows. Wszystkie widoki mają zaimplementowane mechanizmy tematów, które

pozwalają na zmianę wyglądu aplikacji. Można np. sprawić, aby aplikacja wyglądem

przypominała program z systemu Windows lub okno programu upodobniło się do aplikacji

z Mac OS.

Rysunek 8

Rysunek 8 przedstawia okno wywodzące się z QMainWindow zawierające na górze pasek
menu QMenuBar, pasek narzędzi QToolBars z zaimplementowanymi przyciskami

Na pierwszym planie występuje główny widok nazywany przestrzenią roboczą aplikacji
Qworkspace.

Poniżej aplikacja w stylu MDI znanego z wielu edytorów tekstu (rysunek 9). Widok edytor
pochodzi z klasy QMultiLineEdit. Na dole aplikacji umieszczono pasek statusu QStatusBar.

background image

22

Rysunek 9

Rysunek 10 przedstawia jedno z predefiniowanych okien służących do wyboru pliku
QFileDialog.

Rysunek 10

Kolejnym okrem predefiniowanym w bibliotece QT jest okno ustawień wydruku widoczne
na rysunku 11.

background image

23

Rysunek 11

Biblioteka QT dostarcza nam także okna informacyjne QMessageBox. Przykładem jest okno
z rysunku 12 przedstawiające komunikat.

Rysunek 12

Okno postępu zadania QprogressDialog widoczne jest na rysunku 13. Obiekt QProgressBar
może być także użyty jako osobny.

background image

24

Rysunek 13

PopUpMenu, czyli menu podręczne znane z programów pod Windows ilustruje rysunek 14.

Rysunek 14

background image

25

R

R

O

O

Z

Z

D

D

Z

Z

I

I

A

A

Ł

Ł

I

I

P

P

o

o

d

d

s

s

t

t

a

a

w

w

y

y

p

p

r

r

o

o

g

g

r

r

a

a

m

m

o

o

w

w

a

a

n

n

i

i

a

a

o

o

b

b

i

i

e

e

k

k

t

t

o

o

w

w

e

e

g

g

o

o

background image

26

I

I

.

.

1

1

P

P

r

r

o

o

g

g

r

r

a

a

m

m

o

o

w

w

a

a

n

n

i

i

e

e

o

o

b

b

i

i

e

e

k

k

t

t

o

o

w

w

e

e

Programowanie obiektowe ma za zadanie podzielić pisany program na

funkcjonalne bloki. Podobnie jak w programowaniu proceduralnym, z tym, że w podejściu

obiektowym procedury są dodatkowo grupowane i przypisywane obiektom. Procedury te

często deklarowane jako prywatne, przypisane jedynie klasie macierzystej. Dzięki temu

metody i zmienne obiektu są od siebie odseparowane i niewidoczne.

Obiekt jest grupą zawierającą zmienne oraz metody publiczne i prywatne.

Mówiąc obiekt mam na myśli jednolitą, wydzieloną część funkcyjną programu. Na przykład

obiekt: radioodbiornik można traktować jako obiekt. Chodzi o to, że nie trzeba wnikać w

jego budowę ani szczegóły konstrukcji aby go używać. Typowy odbiornik FM składa się

z następujących bloków: głowicy w.cz., mieszacza, wzmacniacza pośredniej częstotliwości,

detektora częstotliwości oraz wzmacniacza m.cz. Jednak jaku użytkownik myślę o nim jako

o całym przedmiocie. Wiem, że przedmiot ten służy do odbierania stacji radiowych.

Mój radioodbiornik posiada opcje dostępne dla użytkownika:

w

strojenie

w

regulację siły głosu

w

wyłącznik.

Jak wynika z wyższego opisu, jest to samodzielna, jednolita jednostka

spełniająca określoną funkcję. Tak też dzieje się w przypadku programowania obiektowego.

Korzystając z gotowych obiektów wystarczy wykorzystać je jako swego rodzaju budulec,

umiejętnie wykorzystując ich metody.

Tak

więc konkretny radioodbiornik nie zaistnieje jeśli nie zostanie wcześniej

złożony z części. Pracownik zakładów produkujących odbiorniki nie myśli o samym

radioodbiorniku jako o tworze zbudowanym z setek części. On widzi w radioodbiorniku

kilkanaście bloków oraz najwyżej kilkadziesiąt podzespołów, które muszą sprawnie działać

jeśli radio ma działać. O tym, że bloki czy podzespoły składają się z jeszcze mniejszych

elementów myśli się dopiero wtedy, gdy radioodbiornik przestaje działać i trzeba go

background image

27

naprawić. Konieczne wówczas staje się poznanie zasady budowy oraz działania

poszczególnych części radioodbiornika. Podobnie postępujemy w programowaniu

obiektowym. Przy programowaniu obiektowym o rozwiązaniu problemu myślimy

w kategoriach obiektów.

Aby

utworzyć obiekt wykorzystujemy klasę. Jest to abstrakcyjna definicja

jeszcze nie istniejącego obiektu, określająca jakie cechy charakteryzują dany obiekt i jakim

operacjom obiekt ten będzie można poddawać. Po zdefiniowaniu klasy, powołujemy

do życia obiekty podając konkretne wartości cech jakie dany egzemplarz wyróżniają. Na

bazie jednej klasy na ogół powołujemy do życia wiele obiektów.

Definicja tworzenia nowej klasy wygląda następująco:

class

nazwa_klasy

{
metoda_1;

zmienna_1;

...

};

Aby utworzyć klasę radioodbiornik napiszemy więc:

class

radioodbiornik

{
char nazwa[30];

int glosnosc;

int wlaczone;

float strojenie;

};

Wewnątrz klasy mogą się znajdować:

w

zmienne typu int, float, char[30]

w

funkcje składowe

w

inne klasy (obiekty)

background image

28

Naszą przykładową klasę radioodbiornik można wyposażyć w takie oto funkcje:

class

radioodbiornik

{
char nazwa[30];

int glosnosc;

int wlaczone;

float strojenie;

void wlacz_radio(int czy_wlaczone);
void zmien_glosnosc(int jaka_glosnosc);

void zmien_czestotliwosc(float jaka_czetotliwosc);

};

Tak

oto

zadeklarowaliśmy niezbędne do działania klasy funkcje składowe oraz

zmienne klasy. Jednak funkcje te są tylko zadeklarowane, co oznacza, że należy jeszcze

je napisać. Deklaracja oznacza jedynie, że takie zmienne czy funkcje są wewnątrz klasy.

Jeśli jednak funkcje te są bardzo krótkie, to w miejscu deklaracji można wstawić od razu

całą definicję.

background image

29

I

I

.

.

2

2

S

S

k

k

ł

ł

a

a

d

d

n

n

i

i

k

k

i

i

k

k

l

l

a

a

s

s

y

y


Sama definicja klasy jest jedynie receptą na obiekt jako na nowy typ danych.

Nie jest to więc samodzielny obiekt. Aby utworzyć nowy obiekt dowolnej klasy należy użyć

deklaracji obiektu. W programach można używać dowolnej ilości takich deklaracji.

radioodbiornik panasonic;

radioodbiornik sony;

Aby

zmodyfikować element składowy obiektu musimy odwołać się do niego

za pomocą nazwy obiektu, kropki rozdzielającej oraz nazwy składowej. Poniżej ustalam

wartość głośności obiektu radioodbiornika panasonic oraz stroję na częstotliwość RMF FM

radioodbiornik o nazwie sony.

panasonic.glosnosc = 40;

sony.stojenie = 88.2;

Jeśli utworzymy wskaźnik do obiektu, stosujemy operator ->

radioodbiornik *moj_odbiorniczek=&panasonic;
moj_odbiorniczek->glosnosc=40;

Do przechowywania składników obiektów w pamięci kompilator przydzieli

odpowiedni jej fragment dla każdego z nich. Funkcje i zmienne znajdujące się wewnątrz

klasy są dostępny tylko w obrębie tej klasy. Jest to właściwość zwana hermetyzacją lub

kapsułowaniem (ang. encapsulation). Jednak niektóre z nich muszą być jednak udostępnione

na zewnątrz klasy, aby można było ich użyć lub po prostu zmienić ich wartości w przypadku

zmiennych.

background image

30

Wszystko to, co znajduje się wewnątrz klasy dzielimy na:

private (prywatne)

zmienne i funkcje składowe klasy, które powinny zostać

niedostępne dla użytkownika, gdyż ich modyfikacja może przynieść

nieobliczalne skutki.

protected

jak wyżej, ale dodatkowo jest dostępny dla innych klas

wywodzących się z tej klasy

public

elementy składowe dostępne dla wszystkich bez ograniczeń. Dzięki

metodom w sekcji public, po odpowiednim zaprogramowaniu

funkcji możemy operować na części private bez ryzyka błędu.

Dzięki takiemu podziałowi możemy określić, do których składników klasy

będzie miał program. Przytaczając jako przykład klasę radioodbiornik, chcemy na pewno,

aby istniał dostęp do metody strojenia. Jednak nie koniecznie chcemy, aby program miał

pezpośredni dostęp do zmiennej strojenie, gdyż zrobi to za nas bezpieczna funkcja

stroj_radio(). Definiując składniki klasy, już na fazie jej projektowania można zdecydować,

które z metod i zmiennych zadeklarować jako prywatne a które udostępnić jako publiczne.

Ogólna zasada podręcznikowa mówi, aby zmiennych publicznych było możliwie

jak najmniej. Sprawi to, że program stanie się bezpieczniejszy, a jego rozbudowa

w przyszłości nie będzie pociągała za sobą ryzyka kolizji zmiennych czy funkcji.

class radioodbiornik

{
private:
char nazwa[30];

int glosnosc, wlaczony;

float strojenie;

background image

31

public:

void wlacz_radio(int jaki_stan);

void stroj_radio(float jaka_czestotliwosc);

};

Aby dobrze zaprojektować klasę, należy pamiętać aby udzielać dostępu typu

public tylko nielicznym funkcjom i zmiennym składowym. Należy tworzyć jak najwięcej

zmiennych typu private, które zapewnią bezpieczeństwo programu.

Funkcje składowe klasy możemy tworzyć dwoma sposobami:

w

w przypadku małych funkcji – wewnątrz definicji klasy

class radioodbiornik

{
...
void stroj(float jaka_czestotliwosc)

{

strojenie = jaka_czestotliwosc;

}

...
}

w

najczęstszą metodą, czyli poza definicją klasy wykorzystując operator zakresu ::

void radioodbiornik::strojenie(float jaka_czestotliwosc)

{

strojenie = jaka_czestotliwosc;

}

W

powyższym przykładzie wykorzystany został operator zakresu. Jest on

przedstawiony w postaci podwójnego dwukropka “::” Nazwa przed dwukropkiem oznacza

nazwę klasy a po prawej stronie nową funkcję składową klasy.

background image

32

I

I

.

.

3

3

M

M

e

e

t

t

o

o

d

d

y

y

o

o

c

c

h

h

r

r

o

o

n

n

y

y

s

s

k

k

ł

ł

a

a

d

d

n

n

i

i

k

k

ó

ó

w

w

k

k

l

l

a

a

s

s

y

y

Jeśli chcemy chronić dostęp do danych występujących w definicji klasy mamy

do wyboru dwie drogi nadawania im wartości początkowych:

w

projektujemy odpowiednie funkcje składowe klasy

void radioodbiornik::inicjuj(char *a_nazwa,

float czestotliwosc,
int glos,

int wylacznik)
{

strcpy(nazwa,a_nazwa);
strojenie = czestotliwosc;

glosnosc = glos;
wlaczony = wylacznik;
}

...
radioodbiornik radyjko;

radyjko.inicjuj("Panasonic",88.2,40.1);

w

Można także utworzyć konstruktor obiektu

radioodbiornik::radioodbiornik(char *jaka_nazwa,

float jaka_czestotliwosc,

int jaka_glosnosc)

{

strcpy(nazwa,jaka_nazwa);
strojenie = jaka_czestotliwosc;

glosnosc = jaka_glosnosc;
}

...
radioodbiornik radio1 = radioodbiornik("SONY",88.2,40);
radioodbiornik radio2("SONY",88.2,40);

background image

33

I

I

.

.

4

4

S

S

k

k

ł

ł

a

a

d

d

n

n

i

i

k

k

i

i

s

s

t

t

a

a

t

t

y

y

c

c

z

z

n

n

e

e

Wewnątrz definicji klasy znajdują się składniki dynamiczne przypisane

do danego obiektu. Składnikiem statycznym może być np. zmienna dowolnego typu.

Zmienna ta jest wspólna dla wszystkich obiektów danej klasy. Jest wiele programów, które

jako niezbędne do swojego działania potrzbują zmiennych typu static. Do składnika tego

typu odwołujemy się jak do standardowego. Warunkiem utworzneia statycznej składowej

klasy jest poprzedzenie jej słowem static oraz utworzeniu zmiennej globalnej,

odpowiadającej dzielonej składowej klasy.

class

radioodbiornik

{

....

static int nazwa_stacji;
....
}

Możemy także odnieść się do składnika poprzez nazwę klasy i operator zakresu

radioodbiornik::nazwa_stacji=”RMF FM”;

background image

34

I

I

.

.

5

5

T

T

a

a

b

b

l

l

i

i

c

c

e

e

o

o

b

b

i

i

e

e

k

k

t

t

ó

ó

w

w

Za

pomocą tablic można tworzyć tablice obiektów danej klasy. Utworzymy

w ten sposób swego rodzaju bazę danych obiektów.

radioodbiornik sony[20];

W momencie definiownia takiej tablicy można też przeprowadzić inicjalizację

radioodbiornik sony[20] = {

radioodbiornik("RMF FM",88.2,40),
radioodbiornik"Radio ZET",105.3,40),

....

};

background image

35

I

I

.

.

6

6

F

F

u

u

n

n

k

k

c

c

j

j

e

e

z

z

a

a

p

p

r

r

z

z

y

y

j

j

a

a

ź

ź

n

n

i

i

o

o

n

n

e

e

Funkcje

zaprzyjaźnione, to funkcje, które choć nie są składowymi danej klasy

mają dostęp do zmiennych prywatnych danej klasy. Aby utworzyć klasę zaprzyjaźnioną,

należy ją zadeklarować w danej klasie.

class radioodbiornik

{

.......
friend void zmien_glos(radioodbiornik&jakie_radyjko);

};

.........
void zmien_glos(radioodbiornik &jakie_radyjko)

{

jakie_radyjko.strojenie=88.2;

}

Z klasą można skojarzyć więcej niż jedną funkcję zaprzyjaźnioną. Mamy wtedy możliwość

dostępu do zmiennych prywatnych oraz funkcji składowych w wielu klasach.

background image

36

I

I

.

.

7

7

K

K

o

o

n

n

s

s

t

t

r

r

u

u

k

k

t

t

o

o

r

r

y

y

i

i

d

d

e

e

s

s

t

t

r

r

u

u

k

k

t

t

o

o

r

r

y

y

Konstruktory są specyficznymi funkcjami składowymi, służącymi do

inicjalizacji i nadawania wartości początkowych obiektom. Konstroktor nosi nazwę klasy

i jego zadaniem jest przypisanie do pamięci początkowych wartości obiektu. Warto

pamiętać, że tworzenie konstruktorów nie jest konieczne. W tym przypadku kompilator

stworzy go za nas w postaci

nazwa_klasy(void)

Konstruktor utworzony przez kompilator nazywamy

konstruktorem domniemanym

Destruktory – funkcje składowe, których zadaniem jest wykonanie jakiegoś

zadania przed usunięciem obiektu z pamięci. Destruktory nazywamy tak jak konstruktory,

z tym, że przed nazwą umieszczamy tyldę ~ Klasa nie musi posiadać destruktora.

Jeśli jednak obiekt wykorzystywał w sobie operator przydziału pamięcie new, to dobrze jest,

gdy destruktor użyje instrukcji delete zwalniającego tę pamięć.

r

r

a

a

d

d

i

i

o

o

o

o

d

d

b

b

i

i

o

o

r

r

n

n

i

i

k

k

:

:

:

:

~

~

r

r

a

a

d

d

i

i

o

o

o

o

d

d

b

b

i

i

o

o

r

r

n

n

i

i

k

k

(

(

v

v

o

o

i

i

d

d

)

)

{

....

}

Obiekt lokalny dynamiczny to obiekt zdefiniowany wewnątrz bloku.

....
{ // otwarcie bloku
...

samochod kr30780; // obiekt powołany do życia
...

} // zamknięcie bloku, obiekt przestaje istnieć
...

w

Konstruktor takiego obiektu jest uruchamiany w momencie, gdy program napotyka

jego definicję.

Destruktor jest uruchamiany, gdy program opuszcza blok.

background image

37

I

I

.

.

8

8

D

D

z

z

i

i

e

e

d

d

z

z

i

i

c

c

z

z

e

e

n

n

i

i

e

e

Dziedziczenie

jest

jedną z największych zalet programowania obiektowego

w C++. Ułatwia programowanie i skraca czas tworzenia programu. Ideą programowania jest

wielokrotne wykorzystywanie tego samego kodu. Technika ta pozwala definiować nowe

klasy przy wykorzystaniu klas już istniejących. Mając napisaną klasę rtv, możemy na jej

bazie utworzyć nową klasę o nazwie telewizor, który odziedziczy cechy i metody klasy –

rodzica. Nowo utworzona klasa, która dziedziczy cechy rodzica, posiada własne cechy oraz

inne składowe, ale oprócz tego posiada także cechy i składowe klasy rodzica. Klasę, z której

dziedziczymy nazywamy rodzicem, klasą bazową lub klasą macierzystą. Klasę dziedziczącą

nazywamy klasą pochodną.

Podsumowując: stosując mechanizm dziedziczenia definiujemy tylko różnice

pomiędzy obiektami, a nie wszystkie obiekty od nowa. Nie musimy także znać kodu klasy

podstawowej, wystarczy gdy wiemy jak funkcjonuje. Oddzielamy też od siebie to, jak dana

klasa jest zrealizowana, od tego, jak się nią posługiwać – nawet w celu dziedziczenia.

Aby użyć klasy do tworzenia klas pochodnych, nie musimy wiedzieć dokładnie za pomocą

jakich kruczków oblicza się w klasie konkretne dane. Programowanie obiektowe umożliwia:

w

przybliżenie sztuki programowania do życia codziennego

w

jest wielkim ułatwieniem w programowaniu zespołowym

w

jest nieocenione przy dużych projektach, bo pozwala pracować lokalnie: nie musimy

przez cały czas pamiętać wszystkich szczegółów i martwić się o nie

Aby utworzyć nową klasę telewizor na bazie starej klasy agd piszemy

class rtv {
private:

float strojenie;
public:
void jaka_czestotliwosc(float czestotliwosc);
}

void

rtv::jaka_czestotliwosc(float czestotliwosc);

{
strojenie = czestotliwosc;

}

class telewizor : public rtv {

background image

38

private:

int glosnosc;
public:
glos_i_stroj(int volume, int freq);
}

void

telewizor::glos_i_stroj(int volume, int freq);

{

glosnosc = volume;
strojenie = freq;

}

Klasa rtv jest dla klasy telewizor klasą podstawową, albo inaczej klasą macierzystą.

Uwaga: konstruktorów i destruktorów w C++ się nie dziedziczy !

Klasa pochodna zawiera wszystkie składniki klasy macierzystej. Ponadto klasa pochodna

może zawierać:

w

dodatkowe dane składowe

w

dodatkowe funkcje składowe

w

nowe definicje funkcji składowych już zdefiniowanych w klasie macierzystej

background image

39

Warto

podkreślić, że dziedziczenie może występować wielokrotnie. Oznacza to,

że klasa, która posiada przodka, sama może być klasą bazową dla innej klasy. W tym

przypadku mamy do czynienia z dziedziczeniem kilkupokoleniowym.

class rtv {

....
}


class radio: public rtv{

....
}


class telewizor: public radio{
....

}

class : dvd telewizor{
....

}

background image

40

R

R

O

O

Z

Z

D

D

Z

Z

I

I

A

A

Ł

Ł

I

I

I

I

P

P

r

r

o

o

g

g

r

r

a

a

m

m

o

o

w

w

a

a

n

n

i

i

e

e

i

i

n

n

t

t

e

e

r

r

f

f

e

e

j

j

s

s

u

u

u

u

ż

ż

y

y

t

t

k

k

o

o

w

w

n

n

i

i

k

k

a

a

background image

41

I

I

I

I

.

.

1

1

O

O

k

k

n

n

o

o

g

g

ł

ł

ó

ó

w

w

n

n

e

e

p

p

r

r

o

o

g

g

r

r

a

a

m

m

u

u

Programowanie za pomocą biblioteki QT przypomina budowę programu z klocków.

Z dostępnych w bibliotece elementów GUI wybieramy niezbędne nam elementy formatki

i dołączamy je do projektu. Poniżej znajduje się przykład prostego programu napisanego za

pomocą języka C++ oraz biblioteki QT.

Pierwszym programem jest pusty formularz. Jest to zarazem najprostszy program,

jaki można napisać w QT. Na nim w dalszej części będziemy umieszczać dodatkowe

elementy aplikacji. Nazywany oknem głównym (ang. main window), stanowi szkielet

dla innych, bardziej rozbudowanych projektów.

// LISTING 1

1.

#include

<qapplication.h>

2.

#include

<qwidget.h>

3.

class

MojeOkno :

public

QWidget

{

4.

public

: MojeOkno();

};

5.

MojeOkno::MojeOkno()

{

6.

setGeometry(300,200,300,200);

}

7.

int

main(

int

argc,

char

**argv)

{

8.

QApplication

okienko(argc,argv);

9.

MojeOkno

plum;

10.

okienko.setMainWidget(&plum);

11.

plum.show();

12.

return

okienko.exec();

}

Aby

przykład był bardziej czytelny, zastosowałem numerowanie linii.

Oczywiście podczas przepisywania programu numerowanie należy pominąć.

W liniach 1 oraz 2 dołączamy do projektu niezbędne pliki nagłówkowe. QApplication jest

przodkiem wszystkich klas w QT. QWidget jest głównym widokiem, z którego pochodzą

wszystkie inne klasy – widoki będące jego potomkami.

background image

42

W linijce 3 opisujemy klasę i tworzymy składowe klasy. Nie musimy już deklarować

wszystkich metod, gdyż są one automatycznie dziedziczone z klasy QWidget. Linia 4

zawiera udostępnienie klasy MojeOkno jako publiczne.

W linii 5 tworzymy konstruktor klasy, w którym definiujemy różne właściwości klasy.

Tu w następnych programach deklarowane będą gniazda niestandardowe.

Linia 6 jest odpowiedzialna za rozmieszczenie i rozmiar okna aplikacji. W miejsce tej linii

można wstawić wpis: setMinimumSize(X,Y) oraz setMaximumSize(X,Y), co spowoduje,

że okno będzie można powiększać w określonym zakresie wartości pomiędzy Min oraz Max.

Główna pętla programu zaczyna się od linii 7. Tworzy ona obiekt QApplication, ustawia

obiekt QApplication jako główny widok programu (linia 10), pokazuje widok na ekranie

(linia 11) oraz przekazuje sterowanie do mechanizmu obsługi zdarzeń QT. Uruchomiony

program przedstawiony jest na rysunku 15.



Rysunek 15

background image

43

I

I

I

I

.

.

2

2

P

P

r

r

z

z

y

y

c

c

i

i

s

s

k

k

i

i


Postaram

się teraz rozbudować program z listingu 1 o dodatkowy element –

przycisk (ang. push button). Przyciski z regóły nie wymagają wyjaśnienia a ich stosowanie

jest niezbędne niemal w każdym przypadku. Warto jedynie nadmienić, że wyróżniamy

3 typy przycisków:

standardowe (ang. Push button)

radiowe (ang. Radio button)
pola wyboru (ang. Check button)

Przyciski standardowe to zwykłe przyciski spotykane w większości programów.

Przyciski radiowe są używane wtedy, gdy trzeba umożliwić użytkownikowi wybranie jednej

z kilku opcji. Przyciski tego typu mają dwa stany – zaznaczony i odznaczony. Przyciski

radiowe są podobne, ale umożliwiają wybranie tylko jednego stanu. Przyciski radiowe

można dowolnie grupować.

Poniżej przedstawiam przykład przycisku zwykłego. Jest to nieco rozbudowany

program z listingu 1 uzupełniony o dodatkowy element – przycisk. Choć przykład ten nie

rożni się wiele od poprzedniego, to niektóre polecenia wymagają dodatkowego objaśnienia.

Sam schemat działania poczynając od tego przykładu będzie podobny. Dołączenie nowego

obiektu do projektu wymaga:

dołączenia plików nagłówkowych do projektu
zarezerwowania pamięci dla wskaźnika obiektu
utworzenie nowego obiektu
ewentualne zabiegi zmieniające niektóre właściwości obiektu

// LISTING 2

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qpushbutton.h>

#include

<qfont.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

1.

private

: QPushButton *przycisk_koniec;

};

MojeOkno::MojeOkno()

{

setGeometry(300,200,300,160);

background image

44

2.

przycisk_koniec = new QPushButton("Koniec programu",this);

3.

przycisk_koniec->setGeometry(20,20,260,100);

4.

przycisk_koniec->setFont(QFont("Times",18,QFont::Bold));

}

int main(

int

argc,

char

**argv)

{

QApplication okienko(argc,argv);

MojeOkno plum;

okienko.setMainWidget(&plum);

5.

plum.show();

return

okienko.exec();

}

Opis

działania programu przedstawia się następująco: linia oznaczona jako

1 lokalizuje pamięć dla nowego przycisku. Jako że przyciski i wszelkie inne elementy

formularza są częścią wewnętrzną klasy, to nie wymagają ingerencji z zewnątrz. Wszystkie

tego typu zmienne (w tym przypadku QpushButon) ustalamy jako prywatne.

Zaczynając od linii 2, tworzymy nowy przycisk. Jako parametry dołączamy etykietę

przycisku oraz wskaźnik do widoku macierzystego. Wskaźnik this w tym przypadku

wskazuje właśnie na widok nadrzędny w stosunku do przycisku.

Linia 3 definiuje miejsce położenia obiektu w oknie macierzystym oraz jego rozmiary.

Metoda setGeometry występuje niemal w każdym wizualnym widgecie komunikacji

z użytkownikiem. Linia 4 jest dodatkiem opcjonalnym i niekoniecznym, ale bardzo

praktycznym. Dzięki metodzie setFont można dowolnie ustawiać czcionkę widoku, co czyni

go bardziej czytelnym. Ciekawostką jest linia 5. W poprzednim przykładzie wyświetlała ona

widok na ekranie. W tym przypadku jednak nie jest to konieczne, gdyż obiekt macierzysty

robi to automatycznie. Dzięki temu nie musimy „ręcznie” wyświetlać każdego z elementów

na ekranie. Rysunek 16 przedstawia skompilowany i uruchomiony kod z listingu 2.

Rysunek 15

background image

45

Nazwa opis

clicked()

Naciśnięto przycisk

pressed() Przycisk

wciśnięty

released() Zwolniono

przycisk

Qpixmap pixmap(“piksmapa.xmp”);

setPixmap(pixmap)

Etykieta graficzna przycisku

setText(“Tekst”) Definiuje

etykietę przycisku

setAccel( CTRL + 'P'

Przypisuje skrót klawiaturowy do przycisku

text() Zwraca

etykietę przycisku

setDefault()

Przycisk domyślny. Jest to przycisk, który zostanie
wciśnięty, gdy użytkownik naciśnie klawisz Enter.

Tabela 1 - Najważniejsze właściwości i metody widgetu button


Przyciski radiowe

Przyciski radiowe tworzymy z klas QButtonGroup oraz QRadioButton.

Aby skorzystać z przycisków radiowych należy skorzystać z biblioteki QradioButton oraz

Qbutton group deklarując je w sekcji include:

#include <qradiobutton.h> // dla przycisków
#include <qradiogroup.h> // dla ramki grupującej (opcjonalnie)

oraz w sekcji private zadeklarować pamięć dla obiektów:

private

:

QButtonGroup

*grupa;

QRadioButton *wybor1;

QRadioButton

*wybor2;

QRadioButton *wybor3;


lub po prostu:

private

:

QButtonGroup

*grupa;

QRadioButton *wybor1, *wybor2, *wybor3;


następnie w definicji widgetu tworzymy obwódkę oraz przyciski:

grupa = new QButtonGroup("Wybierz opcje", this);
grupa->setGeometry(

10,10,180,180

);

wybor1 = new QRadioButton("Wybieram 1", grupa);
wybor1->move(

10,20

);

wybor2 = new QRadioButton("Wybieram 2", grupa);
wybor2->move(

10,50

);

wybor3 = new QRadioButton("Wybieram 3", grupa);

wybor3->move(

10,80

);


//Poniżej definiujemy przycisk pola wyboru

wybor5 = new QCheckBox("Pole wyboru !", grupa);
wybor5->setChecked(true);

wybor5->move(

10,130

);

background image

46

grupa->insert(wybor1);

grupa->insert(wybor2);

grupa->insert(wybor3);

grupa->insert(wybor5);


Dodatkowo

powyżej dodałem jeden przycisk pola wyboru – CheckButton.

Nazwałem go przycisk 5 a w linii setChecked(true) sprawiłem, że jest on domyślnie

włączony. Geometria przycisków jest ustalana za pomocą funkcji move.

Celowo zrezygnowałem z opcji setGeometry, gdyż nigdy nie wiemy, jaką szerokość

i wysokość powinny mieć te przyciski. Na rysunku 16 przedstawiłem gotowy program

demonstrujący użycie przycisków radiowych oraz pól wyboru. Dodatkowym elementem jest

ramka grupująca obiekty. Ramka oprócz roli czyto estetycznej sprawia, że program staje się

bardziej czytelny.

Rysunek 16

nazwa opis

clicked()

Naciśnięto przycisk

pressed() Przycisk

wciśnięty

released() Zwolniono

przycisk

setPixmap(const QPixmap)

Etykieta graficzna przycisku

setText(“Tekst”) Definiuje

etykietę przycisku

setAccel( CTRL + 'P'

Przypisuje skrót klawiaturowy do przycisku

text() Zwraca

etykietę przycisku

isChecked() Zwraca

true,

jeśli jeśli przycisk jest włączony

setChecked() Włącza lub wyłącza przycisk

Tabela 2 – Najważniejsze właściwości oraz metody przełączników oraz przycisków radiowych

background image

47

I

I

I

I

.

.

3

3

E

E

t

t

y

y

k

k

i

i

e

e

t

t

y

y

t

t

e

e

k

k

s

s

t

t

o

o

w

w

e

e

Etykiety tekstowe (ang. labels) są niezbędną częścią każdego programu. Pełnią one

zwykle funkcję informacyjną. Mogą być statyczne lub zmieniać się w trakcie wykonywania

programu. Etykiety najczęściej służą do opisywania działania czy przeznaczenia innych

widoków. Za zawartość etykiety odpowiada metoda setText(„tekst”), w której jedynym

argumentem jest stała lub zmienna tekstowa. W poniższym przykładzie dodatkowo

zastosowałem wyrównywanie tekstu (linia 2). Zamiast AlignCenter można posłużyć się inną

zdefiniowaną stałą. Ich lista znajduje się w tabeli nr 3.

Funkcja Opis

działnia

AlignTop

Wyrównanie do góry obiektu QLabel

AlignBottom

Wyrównanie do dołu obiektu QLabel

AlignLeft

Wyrównanie do lewej obiektu QLabel

AlignRight

Wyrównanie do prawej obiektu QLabel

AlignHCenter

Tekst dodany w poziomej pozycji środkowej obiektu QLabel

AlignVCenter

Tekst dodany w pionowej pozycji środkowej obiektu QLabel

AlignCenter

AlignHCenter – AlignVCenter razem wzięte

WordBreak Automatyczne

dzielenie

wyrazów

ExpandTabs

QLabel rozwija tabulatory

Tabela 3 – Właściwości etykiety


funkcję setAlignment można uzywać łącząc powyższe zmienne:

setAlingment(AlignHCenter | AlignVCenter);

// LISTING 3

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlabel.h>

#include

<qfont.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

: QLabel *moja_etykieta;

};

MojeOkno::MojeOkno()

{

setGeometry(300,200,350,160);

moja_etykieta = new QLabel(this);

moja_etykieta->setGeometry(20,20,300,80);

1.

moja_etykieta->setText("To jest etykieta (ang. Label)");

moja_etykieta->setFont(QFont("Times",18,QFont::Bold));

2.

moja_etykieta->setAlignment(AlignCenter);

}

background image

48

int

main(

int

argc,

char

**argv)

{

QApplication

okienko(argc,argv);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}


Efekt działania programu z listingu 3 możemy podziwiać na rysunku 17

Rysunek 17


Etykieta specjalna LCD

Poniżej przedstawiam jeszcze jedną etykietę dostępną w bibliotece QT –

LCDNumber. Jest to wyświetlacz umożliwiający efektowne wyświetlanie wyników

lub parametrów na ekranie.

W praktyce jest on bardzo przydatny do obrazowania wszelkich procesów. Poniżej znajduje

się cały kod źródłowy oraz zrzut ekranu (rysunek 18) obrazujący wygląd widoku.

//LISTING 4

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlcdnumber.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

p

rivate

: QLCDNumber *numerek;

};

MojeOkno::MojeOkno()

{

setGeometry(300,200,180,100);

numerek = new QLCDNumber(this);

numerek->setGeometry(10,10,150,80);

background image

49

numerek->display(2004);

}

int

main(

int

argc,

char

**argv)

{

QApplication

okienko(argc,argv);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}

Rysunek 18



Obiekt LCDNumber posiada także interesujące funkcje składowe. W tabeli nr 4 zawarłem
najważniejsze z nich.

Funkcja Opis

działnia

setNumDigits() Ustawia

liczbę cyfr do wyświetlania

SetBinMode() Tryb

binarny

setDecMode() Domyślny tryb dziesiętny

setSegmentStyle() Wygląd cyfr (przekazując jak parametr: Outline, Filled, Flat)

checkOverFlow() Przekroczenie zakresu to sygnał jaki emituje obiekt w przypadku

przekroczenia zakresu.

Tabela 4 – właściwości i metody etykiety LCD

background image

50

I

I

I

I

.

.

4

4

P

P

o

o

l

l

e

e

w

w

e

e

j

j

ś

ś

c

c

i

i

o

o

w

w

e

e


Pole

wejściowe (ang. input text) jest jednowierszowym komponentem służącym

do pobrania linii tekstu. Za jego pomocą można pobrać od użytkownika dowolną zmienną

tekstową lub numeryczną. W drugim przypadku jednak niezbędna będzie dodatkowa

konwersja zmiennych, gdyż pole tekstowe zawsze zwraca łańcuch znakowy. Dzięki

niewielkiej modyfikacji może służyć jako dyskretne pole do wpisywania hasła.

// LISTING 5

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlineedit.h>

#include

<qlabel.h>

#include

<qfont.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

:

QLineEdit *linia_edycyjna;

QLabel *etykieta;

};

MojeOkno::MojeOkno()

{

setGeometry(

300,200,300,100

);

etykieta = new QLabel(this);

etykieta->setGeometry(

10,20,60,30

);

etykieta->setText("Wpisz dane:");

linia_edycyjna = new QLineEdit("

Tu wpisz teks

t",this);

linia_edycyjna->setGeometry(

72,25,160,20

);

}

int

main(

int

argc

,

char

**argv

)

{

QApplication

okienko(argc,argv);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}

background image

51

Rysunek 19


Obiekt pole tekstowe ma także kilka ciekawych właściwości.

Wpisując linia_edycyjna->setMaxLenght(8); ograniczymy ilość wpisanego tekstu.

Z kolei linia_edycyjna->setEchoMode(QLineEdit::Password); możemy sprawić, że zamiast

wpisywanego tekstu pojawią się gwiazdki. Ta opcja jest niezbędna w przypadku wpisywania

haseł dostępu. Aby pobrać wpisany tekst z okna wystarczy wpisać: linia_edycyjna->text().

Aby ograniczyć ilość wpisywanych znaków można zastosować metodę:
linia_edycyjna->maxLength(ilosć_znaków);

Linia edycyjna emituje przydatne sygnały: returnPressed() i textChanged().

Sygnał returnPressed() jest emitowany, gdy przyciśnięty zostanie przycisk Enter. Sygnał

textChanged() zaś, jest emitowany za każdym razem, gdy wpisany tekst się zmienia. Sygnał

ten zawiera również nowy tekst. Rysunek 19 obrazuje przykład użycia pola tekstowego.

background image

52

I

I

I

I

.

.

5

5

L

L

i

i

s

s

t

t

y

y


Listy (ang. lists) to skomplikowane komponenty języka C++. Stanowią one

bardzo ważny element w programowaniu. Na szczęście QT dostarcza nam gotowe obiekty

gotowe do użycia a ich wykorzystanie jest niezwykle proste. Listę stosujemy zawsze, jeśli

trzeba dać użytkownikowi wybór jednej lub wielu opcji. W listach wyświetlamy także

np. zawartośc bazy danych czy elementy wyboru. Pełne zastosowanie list ograniczone jest

tak naprawdę tylko wyobraźnią programisty. Warto więc przyjrzeć się bliżej temu obiektowi.

//Listing 6

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlistbox.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

: QListBox *lista;

};

MojeOkno::MojeOkno()

{

setGeometry(

300,200,180,100

);

lista = new QListBox(this);
lista->setGeometry(

10,10,150,80

);

lista->insertItem("Opcja nr 1");

lista->insertItem("Opcja nr 2");

lista->insertItem("Opcja nr 3");

}

int main(int

argc

, char

**argv

)

{

QApplication

okienko(

argc

,

argv

);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}

background image

53

Rysunek 20


Aktualnie wybraną pozycję listy można pobrać przy pomocy funkcji
lista->currentItem();

uzyskany w ten sposób index elementu można przekazać do funkcji

lista->test();

Pozwoli to otrzymać aktualnie zaznaczony tekst lub rysunek w przypadku
lista->pixmap();

background image

54

I

I

I

I

.

.

6

6

P

P

o

o

l

l

a

a

k

k

o

o

m

m

b

b

i

i

Pola kombi są bardzo podobne do list i mogą je w pewnych przypadkach

zastępować gdy na formatce brakuje miejsca. Także sam sposób działania jest bardzo

podobny

12

.

//LISTING 7

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qcombobox.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

: QComboBox *moje_combo;

};

MojeOkno::MojeOkno()

{

setGeometry(

300,200,180,100

);

moje_combo = new QComboBox(this);
moje_combo->setGeometry(

10,10,150,20

);

moje_combo->insertItem("

Opcja nr 1

");

moje_combo->insertItem("

Opcja nr 2

");

moje_combo->insertItem("

Opcja nr 3

");

}

int

main(

int

argc

,

char

**argv

)

{

QApplication

okienko(argc,argv);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}

Rysunek 21

12

Daniel Solin, Programowanie przy użyciu biblioteki QT w 24 godziny, Warszawa 2001, str. 119

background image

55

I

I

I

I

.

.

7

7

R

R

a

a

m

m

k

k

i

i

g

g

r

r

u

u

p

p

u

u

j

j

ą

ą

c

c

e

e


Ramki

grupujące (ang. group box), jak sama nazwa wskazuje służą

do grupowania przeróżnych elementów. Ramki powodują, że formatka staje się bardziej

czytelna a program nabiera estetyki. Staje się rónież łatwiejszy i bardziej przejrzysty.

//LISTING 7

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlabel.h>

#include

<qgroupbox.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

: QGroupBox *moja_ramka;

QLabel *etykietka;

};

MojeOkno::MojeOkno()

{

setGeometry(

300,200,180,100

);

moja_ramka = new QGroupBox(this);
moja_ramka->setGeometry(

10,10,150,80

);

moja_ramka->setTitle("

Ramka grupujaca

");

etykietka = new QLabel(this);
etykietka->setGeometry(

30,30,120,20

);

etykietka->setText("

To jest przyklad ramki

");

etykietka->setAlignment(AlignCenter);

}

int

main(

int

argc

, c

har

**argv

)

{

QApplication

okienko(

argc

,

argv

);

MojeOkno

plum;

okienko.setMainWidget(&plum);

plum.show();

return

okienko.exec();

}

Rysunek 22

background image

56

7

7

.

.

8

8

S

S

u

u

w

w

a

a

k

k

i

i

i

i

p

p

o

o

l

l

a

a

p

p

r

r

z

z

e

e

w

w

i

i

j

j

a

a

n

n

e

e


Suwak (ang. slider) służy do ustalania wartości numerycznych określonych

zmiennych lub stanów wewnętrznych innych widgetów. Operacje zmiany wartości

przeprowadza się poprzez przeciąganie gałki za pomocą myszy. Tę samą rolę pełni pole

przewijane (ang. spin box) jednak jego obsługa polega na przyciskaniu klawiszy góra/dół

13

.

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qslider.h>

#include

<qspinbox.h>

class

MojeOkno :

public

QWidget

{

public

: MojeOkno();

private

: QSlider *suwaczek;

QSpinBox *pole;

};

MojeOkno::MojeOkno()

{

setGeometry(

300,200,180,100

);

suwaczek =

new

QSlider(

0,100,10,50

,Horizontal,this);

suwaczek->setGeometry(

10,10,150,20

);

suwaczek->setTickmarks(QSlider::Below);

pole =

new

QSpinBox(

0,100,1

,this);

pole->setGeometry(

10,60,50,20

);

}


int

main(int

argc

, char

**argv

)

{

// Pętla główna…

}

Rysunek 23

13

Tamże, str. 127

background image

57

I

I

I

I

.

.

9

9

T

T

w

w

o

o

r

r

z

z

e

e

n

n

i

i

e

e

m

m

e

e

n

n

u

u

p

p

r

r

o

o

g

g

r

r

a

a

m

m

u

u

Przez lata rozwijania GUI programiści opracowali standard wyglądu i sposobu

działania graficznych aplikacji. Standard ten dotyczy szczególnie systemu Windows, ale

ostatnio jest on coraz częściej stosowany również na platformach UNIX

14

. Menu jest

komponentem umieszczanym niemal w każdym programie. Z nieocenioną pomocą

przychodzi klasa QmainWindow, która pozwala w łatwy i przystępny sposób dodawać do

programu nie tylko menu, ale również paski narzędzi

15

.

Procedura tworzenia menu polega na dołączeniu dwóch plików nagłówkowych

niezbędnych przy definiowaniu linii zawierającej menu.

#include

<qmenubar.h>

#include

<qpopupmenu.h>


Następnie w deklaracji klasy musimy zdefiniować elementy:

private

:

QPopupMenu *pliki, *inne_menu;

QMenuBar

*menu;

pliki = new QPopupMenu();

pliki->insertItem("

Komunikat numer 1

",this,SLOT (funkcja()));

pliki->insertItem("

Komunikat numer 2

");

pliki->insertItem("

Zakonczenie programu

",qApp,SLOT(quit()));

...tutaj umieszczamy pionowe opcje (popupy)

menu = new QMenuBar(this);
menu->insertItem("

Menu rozwijane

",pliki);

menu->insertItem("

Zakoncz program

",qApp,SLOT(quit()));

...tutaj umieszczamy poziome grupy opcji

14

Tamże, str. 85

15

Tamże, str. 85

background image

58

Należy pamiętać, że dla każdego nowego popup-a w menu trzeba zadeklarować nową

zmienną QPopupMenu *nowypopup;

Rysunek 24

Nowo utworzone menu widoczne jest na obrazku o numerze 24. Choć program

tak na prawdę nie robi nic, to jednak, doskonale ilustruje używanie paska menu. Posiada

także zaimplementowane zdarzenie na opcji Zakoncz program, które kończy działanie

naszego przykładu.

background image

59

I

I

I

I

.

.

1

1

0

0

P

P

r

r

e

e

d

d

e

e

f

f

i

i

n

n

i

i

o

o

w

w

a

a

n

n

e

e

o

o

k

k

n

n

a

a

d

d

i

i

a

a

l

l

o

o

g

g

o

o

w

w

e

e

Biblioteka QT jest wyposażona w kilka przydatnych okien dialogowych.

Znajdują się tutaj okna do wybierania pliku, definicji koloru, okna wyboru fontu oraz okno

postępu.


Okno wyboru koloru

Klasa QColor służy do wyboru koloru. Aby skorzystać z okna dialogowego

wystarczy jedynie dołączyć nagłówek poprzez

#include

<qcolordialog.h>

#include

<qcolor.h>


zadeklarować pamięć dla obiektów

private:

QColorDialog *moj_dialog;

QColor kolor;


oraz wywołać funkcję składową getColor(). Funkcja ta zwraca obiekt QColor reprezentujący
ten kolor.

kolor = moj_dialog->getColor(QColor(0,0,0));


w zmiennej kolor otrzymamy obiekt QColor. Rysunek 25 przedstawia okno wyboru koloru.

background image

60

Rysunek 25

background image

61

I

I

I

I

.

.

1

1

1

1

O

O

k

k

n

n

a

a

i

i

n

n

f

f

o

o

r

r

m

m

a

a

c

c

y

y

j

j

n

n

e

e

Niezwykle przydatnym komponentem jest komunikat (ang. message box).

Jest to okienko zawierające ważną informację dla użytkownika, lub ostrzegające go

o zaistniałej sytuacji. Dodatkowo okienko z komunikatem może być wyposażone

w predefiniowaną piksmapę. Okna komunikatów mogą także wymagać od użytkownika

działania w postaci naciśnięcia przycisku.

QMessageBox::information(this, "Tytul okna",

"To jest informacja dla Ciebie\n"

"A to jest druga linia" );

Rysunek 26

istnieje także możliwość zdefiniowania własnych przycisków:

informacja =

new

QmessageBox("Informacja",

"To jest informacja dla Ciebie !",

QMessageBox::Information,

QMessageBox::Yes | QMessageBox::Default,

QMessageBox::No | QMessageBox::Escape

QMEssageBox::Cancel);

informacja->show();


Rysunek 27

switch

(QMessageBox::information(this,"Nazwa okna",

"Czy chcesz dowiedziec sie wiecej?\n","Nacisnij odpowiedni przycisk",

"&Chce", "&Nie chce", "&Anuluj",

0, // Enter == klaisz 0

2 ) ) { // Escape == klawisz 2

case

0: // Naciśnięto Chcę, skrót Alt-C lub Enter.

// Tutaj operacje odnośnie chcę

background image

62

break;

case

1: // kliknięto Nie lub skrót Alt-D

// tutaj operacje nie chcę

break;

case

2: // Kliknięto Anuluj albo klawisz Ecs

// Tutaj nie wychodzimy

break

;

}


Rysunek 28

Funkcja Opis

działnia

QMessageBox::NoIcon

Brak jakiejkolwiek ikony

QMessageBox::Information Ikonka

informacji

QMessageBox::Warning Ikonka

ostrzegawcza

QMessageBox::Critical Wyświetla ikonę krytycznej decyzji lub informacji

Tabela 5 – Właściwości okna komunikatów

Funkcja Opis

działnia

QMessageBox::Ok

Przycisk OK.

QMessageBox::Cancel

Przycisk zrezygnuj z operacji

QMessageBox::Yes

Tak

QMessageBox::No

Nie

QMessageBox::Abort

Anuluj operację

QMessageBox::Retry

Ponów operację

QMessageBox::Ignore

Ignoruj

Tabela 6 – predefiniowane przyciski okna komunikatów

background image

63

R

R

O

O

Z

Z

D

D

Z

Z

I

I

A

A

Ł

Ł

I

I

I

I

I

I

Z

Z

d

d

a

a

r

r

z

z

e

e

n

n

i

i

a

a

background image

64

I

I

I

I

I

I

.

.

1

1

M

M

e

e

c

c

h

h

a

a

n

n

i

i

z

z

m

m

o

o

b

b

s

s

ł

ł

u

u

g

g

i

i

z

z

d

d

a

a

r

r

z

z

e

e

ń

ń


Programowanie GUI jest aspektem, w którym jednym z najbardziej znaczących

elementów są obiekty i ich stany cząstkowe. Nie jest ich wiele mniej w innych rodzajach

programów, ale: po pierwsze, wiele programów ma jednak jakiś interfejs użytkownika, a po

drugie nawet programy, które go nie mają, mają też jakąś strukturę złożoną z odpowiedniej

ilości stanów cząstkowych.

Ponieważ jednak synchronizacja stanu i w ogóle stany cząstkowe są chyba

najpoważniejszym problemem w programowaniu GUI, zatem porządniejsze biblioteki

graficzne, takie jak Qt, zostały wyposażone w najlepszą technologię do synchronizacji

stanów cząstkowych. Takim rozwiązaniem zaimplementowanym w QT jest technologia

sygnałów i slotów.

Opiszę tutaj, na czym ten mechanizm polega. Otóż jeden obiekt definiuje sobie

jakieś sygnały. Następnie na rzecz obiektu wywołuje się jakieś metody, które dokonują

zmiany jego stanu. Na przykład: system przekazał informację obiektowi o zdarzeniu, które

go dotyczy i jest ważnym zdarzeniem czy też również spowodowało zmianę jego stanu.

Metoda obsługująca zdarzenie na znak, że coś takiego się stało, wysyła sygnał. Co się wtedy

dzieje? Z punktu widzenia obiektu nic. Dopóki nie podłączymy pod taki sygnał konkretnego

gniazda, też nic się nie wydarzy.

Biorąc dla przykładu inny obiekt, który definiuje w sobie slot. Slot to nic innego

jak po prostu funkcja, czy też metoda, której przeznaczeniem jest wykonać się na

odpowiednie zawołanie. Slot służy do podłączenia do niego sygnału. Załóżmy więc, że ten

obiekt ma slot i podłączył sobie do niego sygnał. W takim przypadku gdy następuje

"wysłanie" (emisja) sygnału, to w odróżnieniu od poprzedniej sytuacji, coś konkretnego 1

się już stanie. Wykona się "slot".

background image

65

I

I

I

I

I

I

.

.

2

2

Z

Z

a

a

l

l

e

e

t

t

y

y

m

m

e

e

c

c

h

h

a

a

n

n

i

i

z

z

m

m

u

u

s

s

y

y

g

g

n

n

a

a

ł

ł

g

g

n

n

i

i

a

a

z

z

d

d

o

o

Mechanizm

obsługi zdarzeń wykorzystywany w bibliotece QT jest tym,

co odróżnia tę bibliotekę od innych. Programiści, tworzący tę bibliotekę już podczas

projektowania położyli szczególny nacisk na łatwość interakcji z użytkownikiem oraz

elastyczność i prostotę programowania. Inne biblioteki GUI do tworzenia zdarzeń

wykorzystują funkcje zwrotne (ang. callback function). Mechanizm ten jest jednak

skomplikowany oraz niezwykle trudny do zrozumienia. Biblioteka QT realizuje mechanizm

zdarzeń poprzez tak zwany system sygnał – gniazdo (ang. signal – slot). Jest to jasny i prosty

w implementacji system, polegający na przyporządkowaniu do każdego sygnału

emitowanego przez widget – gniazda będącego zwykłą funkcją. Dzięki tej metodzie

połączenie emitowanego zdarzenia z kodem wykonywalnym można wykonać w jednej linii

programu. Do połączenia sygnału z gniazdem służy funkcja connect. Poniżej wyjaśnię użytą

tu terminologię oraz opiszę wykorzystywanie predefiniowanych oraz niestandardowych

gniazd.

connect(obiekt_emitujący,SIGNAL(zdarzenie)),obiekt_docelowy,SLOT(stan_w)));


Makra SIGNAL i SLOT nie są skomplikowanymi funkcjami. Zamieniają one

podany argument tekst, poprzedzony odpowiednio cyframi 1 lub 2. W Qt istnieje wyraźnie

zaznaczona zasada, że sygnały to metody PRYWATNE klasy. Oznacza to, że nikt, poza

samym obiektem, nie ma prawa wywoływać sygnałów (nawet makro signals jest aliasem

do private).

Zdarzenie – jest to sygnał emitowany przez widgety. Zwykle odnosi się

do tego, co aktualnie robi program. Na przykład, gdy klikniesz jakiś przycisk, ten

wygeneruje zdarzenie clicked(). Problem polega na tym, aby w prosty sposób to zdarzenie

mogło poinformować program, że ów przycisk został wciśnięty. I tutaj przychodzi z pomocą

mechanizm sygnał – gniazdo.

Stan

wewnętrzny – jeśli mowa o sygnałach, to należy tutaj wspomnieć także o

wewnętrznych stanach widgetów. Oznacza on, że pewne cechy widgetu uległy zmianie pod

wpływem określonego zdarzenia.

background image

66

Gniazdo – jest zwykłą funkcją połączoną z sygnałem za pomocą specjalnego

polecenia. Wykonuje kod przypisany do sygnału. Na przykład, jeśli chcemy, aby

po kliknięciu przycisku program się zakończył, to musimy przypisać sygnał clicked()

do gniazda quit().

Sygnał – to także funkcja składowa. Jeśli coś wewnątrz widgetu ulegnie

zmianie, to obiekt ten może wyemitować sygnał. Jeśli sygnał połączymy z gniazdem,

otrzymamy w pełni funkcjonalny mechanizm obsługi zdarzeń. Można połączyć wiele gniazd

z jednym sygnałem. Wówczas wykonywanie gniazd odbywa się jedno po drugim,

w przypadkowej kolejności.

background image

67

I

I

I

I

I

I

.

.

3

3

G

G

n

n

i

i

a

a

z

z

d

d

a

a

p

p

r

r

e

e

d

d

e

e

f

f

i

i

n

n

i

i

o

o

w

w

a

a

n

n

e

e

Niemal wszystkie widgety posiadają swoje wewnętrzne stany oraz emitują

określone sygnały. Na przykład wspomniany wcześniej przycisk (butt1) posiada sygnał

clicked(). Poniższa linia sprawi, że po kliknięciu (wygenerowaniu) sygnału clicked() program

wykorzysta predefiniowaną metodę quit() aby się zakończyć.

connect(butt1, SIGNAL(clicked(), qApp, SLOT(quit()));


Rozpatrzmy bardziej rozbudowany przykład. Jest to prosta aplikacja

demonstrująca wykorzystanie gniazd predefiniowanych zawartych w obiektach. Przesuwając

suwak, zmienia się wartość wyświetlacza LCD.

Rysunek 29

suwak=

new

QSlider(Vertical,this);

suwak->setGeometry(5,5,30,90);

wyswietlacz =

new

QLCDNumber(2,this);

wyswietlacz->setGeometry(50,15,140,70);

connect

(suwak,SIGNAL(valueChanged(

int

)),wyswietlacz, SLOT(display(

int

)));


W tym przypadku funkcja connect() łączy sygnał valueChanged() z gniazdem wyświetlacza

LCD. Gniazdo to nosi nazwę display() i jest wbudowaną funkcją obiektu LCDNumber.

Pomiędzy sygnałem a gniazdem jest przesyłana wartość typu integer. Dzięki temu wartość

suwaka jest bezpośrednio wyświetlana na wyświetlaczu z każdym wyemitowaniem

zdarzenia valueChanged(), czyli przy każdej zmianie wartości. Nie jest przy tym konieczne

określanie klasy, w której znajduje się gniazdo.


background image

68

Jeśli wewnątrz definicji klasy wywołujemy funkcję bez określenia z jakiej klasy ona

pochodzi, to domyślnie C++ zakłada, że pochodzi ona z klasy bieżącej.

W kolejnych rozdziałach niniejszej pracy przedstawię najważniejsze widgety

oraz ich właściwości, gniazda predefiniowane oraz emitowane przez nie sygnały.

background image

69

I

I

I

I

I

I

.

.

4

4

G

G

n

n

i

i

a

a

z

z

d

d

a

a

n

n

i

i

e

e

s

s

t

t

a

a

n

n

d

d

a

a

r

r

d

d

o

o

w

w

e

e

Poprzedni podpunkt odnosił się do gniazd standardowych. W trakcie pisania

programów niezbędne jest jednak wykorzystanie gniazd niestandardowych. Na przykład

wygenerowane zdarzenie clicked() będzie wywoływać funkcję niezaimplementowaną

z innym obiektem. Standardowe połączenie sygnału z gniazdem spowoduję, że kompilator

zwróci błąd. Jest to wada mechanizmu sygnał – gniazdo.

deklaracja przycisku;

connect

(przycisk,SIGNAL(clicked()),wyswietlacz,SLOT(funkcja()));

}

obiekt::gniazdo_niestandardowe()

{

jakiś kod;

{


Powyższy przykład ilustruje wymóg wykorzystania gniazda niestandardowego.

Niestety próba kompilacji takiego programu zakończy się błędem. Niestety C++ nie rozumie

takiego odwołania do gniazda. W tym przypadku musimy skorzystać z narzędzia MOC. Jest

to prekompilator generujący właściwy kod dla kompilatora. Przed tym jednak należy

utworzyć nagłówek aplikacji definiujący gniazdo.

Nagłówek najlepiej jest zapisać jako osobny plik o rozszerzeniu *.h

class

obiekt:

public

QWidget

{

Q_OBJECT

public

: obiekt();

private

: QPushButton *przycisk;

protected slots:

void

gniazdo_niestandardowe();

};


Wszyskie gniazda niestandardowe w bibliotece QT muszą pochodzić z klasy

Q_OBJECT, aby były poprawnie rozpoznawane przez prekompilator MOC.

MOC – prekompilator META OBJECT COMPILER służący do generowania poprawnego

kodu dla kompilatora. Narzędzie to przechwytuje słowa kluczowe specyficzne dla QT (na

przykład emit) i tworzy z nich prawidłowy kod C++.

background image

70


Użycie narzędzia MOC:

moc plik.h plik.moc

Gotowy plik *.moc można już kompilować. Oczywiście w środowiskach typu MS Visual

C++ czy KDevelop prekompila gniazd odbywa się automatycznie podczas uruchomienia

opcji kompilacji. Także program TMAKE automatycznie uaktywnia MOC-a.


W przypadku “ręcznej” kompilacji trzeba wykonać kilka kroków:
moc deklaracja.h deklaracja.moc

następnie należy dołączyć plik moc do kodu źródłowego cpp klasy
#include”deklaracja.moc”

Kolejną czynnością jest kompilacja wraz z głównym plikiem main
g++ -lqt deklaracja.cpp main.cpp -o gotowy
Inną metodą jest prekompilacja do pliku object a następnie konsolidacja z plikami cpp.

moc deklaracja.h deklaracja.moc
g++ -c deklaracja.moc -o deklaracja.o
g++ -c main.cpp -o main.o
g++ -lqt deklaracja.o main.o -o gotowy

background image

71

R

R

O

O

Z

Z

D

D

Z

Z

I

I

A

A

Ł

Ł

I

I

V

V

P

P

r

r

z

z

y

y

k

k

ł

ł

a

a

d

d

y

y

p

p

r

r

o

o

g

g

r

r

a

a

m

m

ó

ó

w

w

w

w

b

b

i

i

b

b

l

l

i

i

o

o

t

t

e

e

c

c

e

e

Q

Q

T

T

background image

72

I

I

V

V

.

.

1

1

G

G

e

e

n

n

e

e

r

r

a

a

c

c

j

j

a

a

i

i

s

s

o

o

r

r

t

t

o

o

w

w

a

a

n

n

i

i

e

e

e

e

l

l

e

e

m

m

e

e

n

n

t

t

ó

ó

w

w

l

l

i

i

s

s

t

t

y

y

Program demonstruje budowę listy elementów oraz jej sortowanie.

Bezpośrednio po uruchomieniu programu, nalezy ustalić suwakiem liczbę generowanych

elementów listy w zakresie od 2 do 50. Domyślnie ilość elementów ustaliłem na 10.

Następnie należy kliknąć przycisk Generuj liczby. Na liście liczby losowe zostanie

wygenerowanych losowo ustalona wcześniej lista liczb. Po wygenerowaniu ciągu liczb,

można przejść do sortowania. W tym celu należy wcisnąć przycisk Sortowanie elementów.

W prawej liście wyświetlone zostaną wszystkie wygenerowane wcześniej liczby

posortowane według wartości.

Rysunek 30

Program pomimo swej prostoty ilustruje wiele interesujących kwestii języka

C++ oraz samej biblioteki QT. Znaleźć tu można wykorzystanie widoku progress dialog

służącego do przedstawiania postępu dowolnego procesu, generację liczb losowych

oraz konwersję liczb na łańcuchy i odwrotnie. Program korzysta także z niestandardowych

gniazd. Wynik działania programu jest pokazany na rysunku 30.

background image

73

Źródło programu: Plik nagłówkowy main.h

//-----------------------------------------------------------

// Program sortujący listę typu QMultiLineEdit

// autor: Leszek Klich

// ----------------------------------------------------------

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlistbox.h>

#include

<qfont.h>

#include

<qlcdnumber.h>

#include

<qlabel.h>

#include

<qslider.h>

#include

<qpushbutton.h>

#include

<qmessagebox.h>

#include

<qprogressdialog.h>

#include

<qstring.h>

#include

<

qmultilineedit.h>

class

Sortowanie :

public

QWidget

{

Q_OBJECT

public

:

Sortowanie();

int

tablica[

50

];


private

:

QMultiLineEdit

*lista;

QMultiLineEdit

*lista2;

QLabel

*etykieta1;

QLabel

*etykieta2;

QLabel

*etykieta3;

QLCDNumber

*numer;

QSlider

*suwak;

QPushButton

*przycisk_generuj;

QPushButton

*przycisk_sortuj;

QPushButton

*przycisk_zakoncz;

QProgressDialog

*pasek_postepu;

QLabel *info1, *info2, *info3;

protected slots

:

void

generacja();

void

Sortowanie1();

};

background image

74

Źródło programu: plik main.cpp

#include

"main.h"

#include

"

stdlib.h"

Sortowanie::Sortowanie()

{

setMaximumSize(

500,350

);

setMinimumSize(

500,350

);

lista = new QMultiLineEdit(this);
lista->setGeometry(

10,35,100,300

);

lista->setReadOnly(TRUE);

lista2 = new QMultiLineEdit(this);

lista2->setGeometry(

110,35,100,300

);

lista2->setReadOnly(TRUE);

etykieta2 = new QLabel(this);
etykieta2->setGeometry(

20,1,80,30

);

etykieta2->setText("

Liczby losowe

");

etykieta2->setFont(QFont("Times",10,QFont::Bold));

etykieta3 = new QLabel(this);
etykieta3->setGeometry(

110,1,100,30

);

etykieta3->setText("

Lista posortowana

");

etykieta3->setFont(QFont("Times",10,QFont::Bold));

numer = new QLCDNumber(2,this);
numer->setGeometry(

380,10,70,30

);

numer->display(10);

etykieta1 = new QLabel(this);
etykieta1->setGeometry(

230,10,150,30

);

etykieta1->setText("

Ilosc elementow listy:

");

etykieta1->setFont(QFont("Times",12,QFont::Bold));

suwak = new QSlider(

2,50,10,10

,Horizontal,this);

suwak->setGeometry(

230,60,220,20

);

connect(suwak,SIGNAL(valueChanged(int)),numer,SLOT(display(int)));

przycisk_generuj = new QPushButton("

Generuj liczby

",this);

przycisk_generuj->setGeometry(

230,100,100,30

);

connect(przycisk_generuj,SIGNAL(clicked()),this,SLOT(generacja() ));

przycisk_generuj = new QPushButton("

Sortowanie

",this);

przycisk_generuj->setGeometry(

360,100,130,30

);

connect(przycisk_generuj,SIGNAL(clicked()),this,SLOT(Sortowanie1() ));

przycisk_zakoncz = new QPushButton("

Koniec progr.

",this);

przycisk_zakoncz->setGeometry(

280,150,150,30

);

connect(przycisk_zakoncz,SIGNAL(clicked()),qApp,SLOT(quit()));

info1 = new QLabel(this);
info1->setGeometry(

230,190,270,30

);

info1->setText("

Demonstracja sortowania elementów

");

info1->setFont(QFont("Times",12,QFont::Bold));

info2 = new QLabel(this);

background image

75

info2->setGeometry(

265,215,270,30

);

info2->setText("

autor: Leszek Klich (2004)

");

info2->setFont(QFont("Times",12,QFont::Normal));

}

void Sortowanie::generacja()

{

lista->clear();

pasek_postepu = new QProgressDialog("

Trwa generowanie

",

"

Anuluj wykonywanie

",suwak->value(),this, "pasek_postepu",TRUE);

pasek_postepu->show();

int licznik, liczba = 0;

for(licznik=1; licznik<=suwak->value(); licznik++) {

liczba = rand() %

1000

;

QString pozycja;

pozycja = pozycja.setNum(liczba);

tablica[licznik-1]=liczba;

lista->insertLine(pozycja);

pasek_postepu->setProgress(licznik);

} }

void Sortowanie::Sortowanie1()

{

if (lista->numLines()<2)

QmessageBox::warning(this,"

Niepowodzenie

","

Najpierw wygeneruj liczby

");

else {

lista2->clear();

int i,n;
n=suwak->value();

for (i=0; i<n; i++){

for (int j=0; j<n-1; j++){
if (tablica[j]>tablica[j+1]){

int tmp;
tmp = tablica[j];

tablica[j] = tablica[j+1];

tablica[j+1] = tmp; }}}

int liczba;
for (i=0; i<suwak->value(); i++){

liczba=tablica[i];

QString pozycja;

pozycja = pozycja.setNum(liczba);

lista2->insertLine(pozycja);

}}}


int

main(int

argc

, char

**argv

)

{

QApplication okienko(

argc

,

argv

);

Sortowanie start;

okienko.setMainWidget(&start);

start.show();
return

okienko.exec();

}

}

background image

76

I

I

V

V

.

.

2

2

P

P

r

r

o

o

g

g

r

r

a

a

m

m

o

o

b

b

l

l

i

i

c

c

z

z

a

a

j

j

ą

ą

c

c

y

y

s

s

i

i

l

l

n

n

i

i

ę

ę


Poniżej przedstawiam prosty program obliczający silnię. Zmienna n oznacza

liczbę naturalną. Program silnia jest często wykorzystywany do nauki programowania

i stanowi prosty przykład algorytmu.

n! = 1

2 3 ... (n - 1) n, n > 1

Iloczyn kolejnych liczb naturalnych od 1 do n,

0! = 1 oraz 1! = 1

n

n!

0

1

1

1

2

2

3

6

4

24

5

120

6

720

7

5 040

8

40 320

9

362 880

10

3 628 800

11

39 916 800

.....

....................

20

2430

⋅10^15

Tabela 7 – Tablica wartości silni

Rysunek 31

background image

77

Oto kod źródłowy aplikacji z rysunku 31.

Źródło programu: plik main.h

//-----------------------------------------------------------

// Program przedstawiający obliczanie silni

// autor: Leszek Klich

// ----------------------------------------------------------

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlistbox.h>

#include

<qfont.h>

#include

<qlabel.h>

#include

<qpushbutton.h>

#include

<qmessagebox.h>

#include

<qgroupbox.h>

#include

<qspinbox.h>

#include

<qstring.h>


class

silnia : public QWidget

{

Q_OBJECT


public

:

silnia();


private

:

QLabel *info1, *info2, *info3;

QGroupBox *ramka1, *ramka2, *ramka3;

QSpinBox

*regulacja;

QButton

*przycisk_oblicz;

QLabel

*etykieta_wynik;

QButton

*przycisk_koniec;


protected slots

:

void

oblicz_silnie();

};

Źródło programu: plik main.cpp

#include

"main.h"

silnia::silnia(){

setMaximumSize(

200,260

);

setMinimumSize(

200,260

);

ramka1=new QGroupBox(this);
ramka1->setGeometry(

2,2,194,70

);

info1 = new QLabel(this);
info1->setGeometry(

35,10,140,30

);

info1->setText("

Obliczanie SILNI

");

info1->setFont(QFont("Times",12,QFont::Bold));

info2 = new QLabel(this);

background image

78

info2->setGeometry(

20,33,170,30

);

info2->setText("

autor: Leszek Klich

");

info2->setFont(QFont("Times",12,QFont::Normal));

ramka2=new QGroupBox(this);
ramka2->setGeometry(

2,74,194,70

);

ramka2->setTitle("

Ustaw parametr

");

regulacja= new QSpinBox(

2,33,1

,this);

regulacja->setGeometry(

37,100,120,30

);

regulacja->setPrefix("

n =

");

przycisk_oblicz = new QPushButton("

Oblicz silnie

",this);

przycisk_oblicz->setGeometry(

26,150,150,30

);

connect(przycisk_oblicz,SIGNAL(clicked()),this,SLOT(oblicz_silnie()));

ramka3=new QGroupBox(this);
ramka3->setGeometry(

2,185,194,30

);

etykieta_wynik = new QLabel(this);
etykieta_wynik->setGeometry(

50,188,100,20

);

etykieta_wynik->setText("

Wynik n!=

");

etykieta_wynik->setFont(QFont("Times",12,Qfont::Bold));

etykieta_wynik->setAlignment(AlignCenter);

przycisk_koniec = new QPushButton("

Zakoncz program

",this);

przycisk_koniec->setGeometry(

24,222,150,30

);

connect(przycisk_koniec,SIGNAL(clicked()),qApp, SLOT(quit()));

}

void silnia::oblicz_silnie(){

if (regulacja->value()<1)

QmessageBox::information(this,"

Niedobrze

","

Musisz podac n wieksze od 1

");

else

{

int i, wynik=1;

for (i=2; i<=regulacja->value(); i++)

wynik=wynik*i;

QString wyn;

wyn = wyn.setNum(wynik);

etykieta_wynik->setText(wyn);

}}

int main(int

argc

, char

**argv

)

{

QApplication

okienko(

argc

,

argv

);

silnia

start;

okienko.setMainWidget(&start);

start.show();

return

okienko.exec();

background image

79

I

I

V

V

.

.

3

3

S

S

y

y

m

m

u

u

l

l

a

a

c

c

j

j

a

a

r

r

u

u

c

c

h

h

u

u

p

p

o

o

p

p

a

a

r

r

a

a

b

b

o

o

l

l

i

i

Poniższy przykład ilustruje ruch punktu po krzywej. Najbardziej zbliżonym do

rzeczywistości torem jest ruch po paraboli. Piksel poruszający się wzdłuż paraboli porusza

się także w ruchu ukośnym. Znając prędkość początkową S i kąt A można obliczyć zasięg R

i największą wysokość HT toru lotu jako:

R = S * S * SIN(2 * A)/G

HT = (S * SIN(A))^2)/2 * G

G jest przyśpieszeniem ziemskim wynoszącym 980 cm/s

2

. Warto dodać, że największy

zasięg otrzymamy przy kącie 45 stopni. Wysokość HT jest największa, gdy obiekt

wyrzucimy w górę, czyli A = 90

o

. Pozycję punktu wzdłuż krzywej otrzymujemy zmieniając

X od 0 do R i obliczając wartości Y z równania:

Y = C1 * X^ 2 + C2 * X + Y0

Y0 jest w tym przypadku wybranym przez nas punktem ekranu. Stała C1 oraz C2 są

określone jako:

C1 = G/(2 * (S * COS(A))^ 2)

C2 = – TAN(A)

W poniższym programie zastosowałem oscylacje zanikającej amplitudy. Wykorzystałem

w tym przypadku funkcję SIN. W celu obliczenia zmieniającej się amplitudy zastosowałem

funkcję EXP.

Po uruchomieniu programu należy skorygować wartości startowe: wysokość, z jakiej upada

punkt oraz odległość.

background image

80

Rysunek 32

Źródło programu: plik main.h

//----------------------------------------------------------------

// Program ilustrujący ruch upadającej piłki wg ruchu po paraboli

// autor: Leszek Klich

// ---------------------------------------------------------------

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qlistbox.h>

#include

<qfont.h>

#include

<qlabel.h>

#include

<qpushbutton.h>

#include

<qgroupbox.h>

#include

<qspinbox.h>

#include

<qstring.h>

#include

<qpainter.h>

#include

<qmessagebox.h>

#include

<math.h>

#include

<stdlib.h>


class

Parabola : public QWidget

{

Q_OBJECT


public

:

Parabola();

background image

81

private

:

QSpinBox *wys, *tlum;

QGroupBox *ramka1, *ramka2;

QLabel *info1, *info2;

QButton

*przycisk_start;

QPainter

*paint;

QLabel *about1, *about2;


protected slots

:

void

paintEvent(QPaintEvent*);

void

Rysuj();

};


Źródło programu: plik main.cpp

#include

"main.h"

void Parabola::paintEvent(QPaintEvent*)

{

paint

=

new

QPainter;

paint->begin(this);

paint->setPen(QPen(black, 4, QPen::SolidLine));

paint->drawLine(

10,370,490,37

0);

paint->end();

}

Parabola::Parabola()

{

setMaximumSize(

500,400

);

setMinimumSize(

500,400

);

ramka2=new QGroupBox(this);
ramka2->setGeometry(

2,5,498,50

);

ramka2->setTitle("

Parametry wstepne

");

info1 = new QLabel(this);
info1->setGeometry(

10,23,70,20

);

info1->setText("

Wysokosc:

");

wys= new QSpinBox(

50,400,30

,this);

wys->setGeometry(

70,26,60,20

);

wys->setPrefix("

H =

");

info2 = new QLabel(this);
info2->setGeometry(

150,23,70,20

);

info2->setText("

Odleglosc:

");

tlum= new QSpinBox(

30,60,5

,this

);

tlum->setGeometry(

205,26,60,20

);

tlum->setPrefix("

O =

");

przycisk_start = new QPushButton("

Rysuj animacje

",this);

przycisk_start->setGeometry(

280,26,100,20

);

connect(przycisk_start,SIGNAL(clicked()),this,SLOT(Rysuj()));

about1 = new QLabel(this);
about1->setGeometry(

300,63,170,15

);

background image

82

about1->setText("

Symulacja ruchu upadajacej pilki.

");

about2 = new QLabel(this);
about2->setGeometry(

300,78,100,10

);

about2->setText("

autor: Leszek Klich

");

}

void Parabola::Rysuj()

{

float

W=3.14159/tlum->value();

float

D=90*3.14159/180;

float

K=0.01;

int

XM=490;

int

YM=370;

int

XN,YN,Y,X;

int

H=wys->value();

paint->begin(this);

paint->setPen(QPen(rand()%200, 2, QPen::SolidLine));

for (XN=0; XN<=XM-10; XN=XN+1) {

YN=H*sin(W*XN+D) * exp(-K * XN);

YN=YM-abs(YN)-3;

paint->drawLine(XN+10,YN,XN+11,YN+1);

X=XN+10;

Y=YN; }

paint->end();

}


int

main(int

argc

, char

**argv

)

{

QApplication okienko(

argc

,

argv

);

Parabola start;

okienko.setMainWidget(&start);

start.show();
return

okienko.exec();

background image

83

I

I

V

V

.

.

4

4

S

S

z

z

y

y

f

f

r

r

o

o

w

w

a

a

n

n

i

i

e

e

m

m

e

e

t

t

o

o

d

d

ą

ą

X

X

O

O

R

R

Szyfrowanie jest procesem przetwarzającym dane wejściowe w określony

sposób. W ten sposób czynimy je nieczytelne dla osób niepowołanych. Istnieje wiele

sposobów kodowania danych. Jednym z prostszych, co nie oznacza nieskutecznych jest

metoda XOR. Do kodowania potrzebny jest ciąg znaków będący kluczem. Użycie do

kodowania funkcji XOR znacznie upraszcza kod programu, ponieważ funkcja kodująca

to także XOR.

(XOR(A,XOR(A,X)=X)

Ponowne “przepuszczenie” zakodowanych danych przez funkcję XOR –

odkoduje ciąg danych. Jakość i bezpieczeństwo kodowania jest liniowe i zależy od długości

łańcucha hasła – klucza. Największe niebezpieczeństwo deszyfrowania danych pojawia się

wtedy, gdy znana jest przynajmniej część danych wejściowych. Analizując zależności

pomiędzy ciągiem zakodowanym i zdekodowanym, można określić klucz kodujący.

Niebezpieczeństwo takie jest realne, ponieważ różne pliki tego samego typu mają np. takie

same nagłówki. Tak więc gdy wiemy, że ktoś szyfruje plik XLS, odzyskanie hasła nie jest

trudne. Program przedstawiony jest na rysunku 33. Po uruchomieniu należy załadować plik

do zakodowania. W tym celu należy kliknąć przycisk Plik do zakodowania / dekodowania.

Domyślnie można szyfrować pliki typu TXT, ale po wpisaniu w miejsce typu

pliku *.*, można szyfrować dowolne typy plików, nie wyłączając plików wykonywalnych.

background image

84

Rysunek 33


Źródło programu: plik main.h

#include

<qapplication.h>

#include

<qwidget.h>

#include

<qfont.h>

#include

<qlabel.h>

#include

<qpushbutton.h>

#include

<qmessagebox.h>

#include

<qgroupbox.h>

#include

<qstring.h>

#include

<qfiledialog.h>


class

szyfrowanie : public QWidget

{

Q_OBJECT


public

:

szyfrowanie();


private

:

QGroupBox

*obwodka;

QLabel *tytul, *info, *autor;

QButton *przycisk_koniec, *przycisk_koduj;

QFileDialog

*fdialog;

QString

plik;

char

*key;


protected slots

:

int

szyfruj();

};

background image

85

Źródło programu: plik main.cpp

#include

"main.h"

szyfrowanie::szyfrowanie() {

key="

LeSzEkKlIcHtOjestKlucz

";

setMaximumSize(

350,180

);

setMinimumSize(

350,180

);

obwodka=new QGroupBox(this);
obwodka->setGeometry(

2,2,343,170

);

tytul = new QLabel(this);
tytul->setGeometry(

48,10,250,30

);

tytul->setText("

Kodowanie/dekodowanie tekstu XOR

");

tytul->setFont(QFont("Times",12,QFont::Bold));

info = new QLabel(this);
info->setGeometry(

10,80,330,30

);

info->setText("

Informacja: oczekiwanie...

");

autor = new QLabel(this);
autor->setGeometry(

58,140,250,30

);

autor->setText("

autor: Leszek Klich IwZ III

");

autor->setFont(QFont("Times",11,QFont::Bold));

przycisk_koniec = new QPushButton("

Zakoncz program

",this);

przycisk_koniec->setGeometry(

24,112,300,30

);

connect(przycisk_koniec,SIGNAL(clicked()),qApp,SLOT(quit()));

przycisk_koduj = new QPushButton("

Plik do zakodowania/odkodowania

",this);

przycisk_koduj->setGeometry(

34,50,280,30

);

connect(przycisk_koduj,SIGNAL(clicked()),this, SLOT(szyfruj()));
}


int

szyfrowanie::szyfruj() {

plik = fdialog->getOpenFileName("","*.txt");

if(plik.isNull()==false) {

FILE *in,*out;

unsigned int i_k,i_p;

char bufor;

info->setText(plik);

if((out=fopen(plik,"rb"))==NULL) {

QmessageBox::critical(this,"

Niedobrze

","

Nie moge otworzyc pliku.

");

fclose(in); }

if((in=fopen("

wynik.txt

","wb"))==NULL) {

QmessageBox::critical(this,"

Niedobrze

","

Nie moge otworzyc pliku.

");

fclose(out); }

i_k=0;

fseek(out,0,SEEK_END);
i_p=ftell(out);

fseek(out,0,SEEK_SET);

background image

86

while(i_p>0){

fread(&bufor,1,1,out);
bufor=(bufor^key[i_k]);

i_k++;

if(i_k==strlen(key)) i_k=0;

fwrite(&bufor,1,1,in);
i_p--;

}

QmessageBox::information(this,"Informacja",
"ZOKONCZONO. Zak./Odk. tekst jest w pliku wynik.txt");

} else

QmessageBox::information(this,"Anulowanie","Anulowales wybieranie");

return 1;
}


int

main(int

argc

, char

**argv

)

{

QApplication okienko(argc,argv);

szyfrowanie start;

okienko.setMainWidget(&start);

start.show();
return

okienko.exec();

}

background image

87

Z

Z

A

A

K

K

O

O

Ń

Ń

C

C

Z

Z

E

E

N

N

I

I

E

E

background image

88

W pracy tej nie opisałem wszystkich możliwości i zalet biblioteki QT, gdyż jest

to niemożliwe z racji ogromu niniejszego tematu. Opisałem tylko te komponenty, które

umożliwiają napisanie prostego programu. Dodatkowo, na końcu opracowania dołączyłem

kilka programów przykładowych, z których można zauważyć pewną analogię co do

wykorzystania poszczególnych klas i widoków. Ich inicjalizacja, użycie w praktyczne jest

bardzo do siebie podobne, co może być pomocne w nauce programowania.

Mottem przewodnim niniejszej pracy była jednak przenośność oraz

uniwersalność biblioteki. To właśnie te cechy przeważyły, że zdecydowałem się właśnie na

ten produkt. Warto wspomnieć, że biblioteka świetnie radzi sobie z bazami danych. Posiada

pełne wsparcie dla różnych serwerów SQL. Jest to jednak temat na kolejne opracowanie,

jeszcze bardziej obszerne.

Opracowanie to przekształciłem także w stronę internetową, co ma

spowodować, że temat ten przybliżę większemu gronu programistów, którzy pragną pisać

programy dla systemu Linuks, ale nie chcą rezygnować z programowania w komercyjnych

systemach operacyjnych.

Zapraszam na stronę www.qtlibrary.glt.pl, gdzie znajduje się elektroniczna wersja tego

dokumentu.

Leszek Klich © 2004

background image

89

W

W

y

y

k

k

a

a

z

z

s

s

k

k

r

r

ó

ó

t

t

ó

ó

w

w

API

– Application Programming Interface

CD

– Compact Disc

OOP

– Object Oriented Programming

GUI

– Graphical User Interface

GTK

– GIMP Toolkit Kit

GNOME – GNU Network Object Model Evironment
GDK

– GIMP Drawing Kit

XLib

– XWindow Library

DOS

– Disc Operating System

RAD

– Rapid Application Development

GNU

– GNU's Not Unix

B

B

i

i

b

b

l

l

i

i

o

o

g

g

r

r

a

a

f

f

i

i

a

a

m

m

e

e

r

r

y

y

t

t

o

o

r

r

y

y

c

c

z

z

n

n

a

a

:

:

1. E. Harlow, Linux. Tworzenie Aplikacji, Wrocław 1999.
2. D. Hearn, M. Baker, Grafika mikrokomputerowa, Warszawa 1988.
3. K. Jamsa, C++, Warszawa 1996.
4. K. Jakubczyk, Turbo Pascal i Borland C++ przykłady, Gliwice 2002.
5. B. W. Kernighan, D. M. Richie, Język C, Warszawa 1988.
6. J. Liberty, C++ w 24 godziny, Warszawa 1998.
7. N. Matthew, R. Stones, Linux. Programowanie, Łódź 1999.
8. D. Solin, Programowanie przy użyciu biblioteki QT, Warszawa 2001.
9. B. Stourstrup, Język C++, Warszawa 2002.
10. J. Surażski, Linuksowe C++, “Chip Specjal – Linux”, 1999, nr 14.
11. K Walczak, Nauka programownia obiektowego w języku C++, Warszawa 2002.
12. P. Wróblewski, Algorytmy, struktury danych i techniki programowania, Gliwice 2003.
13. M. Wiącek, Radosna twórczość, “Chip Special – Linux”, 2002, nr 7.
14. M. Wiśniewski, Biblioteka QT 2, “Linux Plus”, 2000, nr 6.

I

I

n

n

n

n

e

e

m

m

a

a

t

t

e

e

r

r

i

i

a

a

ł

ł

y

y

(

(

S

S

t

t

r

r

o

o

n

n

y

y

i

i

n

n

t

t

e

e

r

r

n

n

e

e

t

t

o

o

w

w

e

e

)

)

:

:

www.troltech.no
www.qtlibrary.glt.pl
www.intercon.pl/~sektor/cbx/
http://free.of.pl/q/qtmoux/index.php


Wyszukiwarka

Podobne podstrony:
Programowanie GUI id 395885 Nieznany
Programowanie GUI id 396017 Nieznany
python gui programming
Biblioteki Qt Zaawansowane programowanie przy uzyciu C
Programowanie generyczne w Qt mikolajczak
Biblioteki Qt Zaawansowane programowanie przy uzyciu C 2
Biblioteki Qt Zaawansowane programowanie przy uzyciu C
Wstęp do programowania w Qt kuczynski
Biblioteki Qt Zaawansowane programowanie przy uzyciu C bibqtc
Nowy Prezentacja programu Microsoft PowerPoint 5

więcej podobnych podstron