background image

 

 

 

 

 

 

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

 

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

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

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

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

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

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

  

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

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> 1 

Iloczyn kolejnych liczb naturalnych od 1 do n

0! = 1 oraz 1! = 1 

 

 

n! 

 

 

 

 

 

24 

 

120 

 

720 

 

5 040 

 

40 320 

 

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