Leszek Klich
PROGRAMOWANIE OBIEKTOWE W JZYKU C++
Multiplatformowa biblioteka QT
LICECJA :
Powielanie lub wykorzystywanie jest możliwe tylko w celach edukacyjnych.
Przy wykorzystywaniu dowolnych algorytmów lub kodów zró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
1
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.
2
1. Cel niniejszego opracowania
1. Cel niniejszego opracowania
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 zródłowej. Muszę w
tym miejscu dodać, że opisywane w niniejszej pracy metody programistyczne nie są
standardowe. Unikam tutaj wykorzystywania nowoczesnych narzędzi typu RAD1 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 zródłowym aplikacji, co jest nie bez znaczenia dla
programistów wywodzących się z ruchu Open Source2 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 zró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 zródłowego.
3
Linuksa. W tym systemie otrzymujemy ją wraz z kodem zró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.
4
2. Zarys historii i ewolucji języka C i C++
2. Zarys historii i ewolucji języka C i 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łego3.
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. Ritchie4 z Bell Laboratories w New Jersey. Język ten opierał się
na języku B opracowanym dwa lata wcześniej przez Kena Thompsona5. 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
5
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 Stroustrup6 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ózniej 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
6
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óznym 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++.
7
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ów7.
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
8
3. Narzędzia i kompilator
3. Narzędzia i kompilator
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.
9
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
10
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 zródłowych
11
4. Graficzny interfejs użytkownika (GUI)
4. Graficzny interfejs użytkownika (GUI)
Dzisiejsze komputery wyposażone są w doskonałe karty graficzne, dużą ilość
pamięci RAM, duże pojemności dysków twardych, przestrzenny dzwię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 GUI8, 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)
12
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, zró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.
13
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.
14
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
zró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.
15
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 zródeł na okienka Microsoftu i to z dużym
powodzeniem, czego najlepszym przykładem jest sztandarowy program graficzny GIMP9 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.
16
w C++ czy Perlu.
Biblioteka QT
Biblioteka napisana została przez grupę Trolltech10 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
zródłowym (powyższe kwestie reguluje odpowiednia licencja). Od pewnego czasu, dzięki
naciskowi linuksowej społeczności, dostępny jest kod zródłowy tej biblioteki na platformę
Linuks. Dzięki temu, nie ma realnej grozby 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 znalezć 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
17
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ć zródła w języku C++.
18
KDevelop
Jest to zintegrowane środowisko programistyczne wzorowane na Visual Studio
(rysunek 7). Wspiera tworzenie standardowych aplikacji konsolowych, aplikacji QT i KDE11
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
19
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++.
20
5. Przykłady widoków biblioteki QT
5. Przykłady widoków biblioteki QT
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.
21
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.
22
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.
23
Rysunek 13
PopUpMenu, czyli menu podręczne znane z programów pod Windows ilustruje rysunek 14.
Rysunek 14
24
ROZDZIAA I
ROZDZIAA I
Podstawy programowania obiektowego
Podstawy programowania obiektowego
25
I.1 Programowanie obiektowe
I.1 Programowanie obiektowe
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:
strojenie
regulację siły głosu
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
26
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ć:
zmienne typu int, float, char[30]
funkcje składowe
inne klasy (obiekty)
27
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ę.
28
I.2 Składniki klasy
I.2 Składniki klasy
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 wskaznik 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.
29
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;
30
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 przypadku małych funkcji wewnątrz definicji klasy
class radioodbiornik
{
...
void stroj(float jaka_czestotliwosc)
{
strojenie = jaka_czestotliwosc;
}
...
}
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.
31
I.3 Metody ochrony składników klasy
I.3 Metody ochrony składników klasy
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:
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);
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);
32
I.4 Składniki statyczne
I.4 Składniki statyczne
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 ;
33
I.5 Tablice obiektów
I.5 Tablice obiektó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),
....
};
34
I.6 Funkcje zaprzyjaznione
I.6 Funkcje zaprzyjaznione
Funkcje zaprzyjaznione, to funkcje, które choć nie są składowymi danej klasy
mają dostęp do zmiennych prywatnych danej klasy. Aby utworzyć klasę zaprzyjaznioną,
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ę zaprzyjaznioną. Mamy wtedy możliwość
dostępu do zmiennych prywatnych oraz funkcji składowych w wielu klasach.
35
I.7 Konstruktory i destruktory
I.7 Konstruktory i destruktory
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ęć.
radioodbiornik::~radioodbiornik (void)
radioodbiornik::~radioodbiornik (void)
{
....
}
Obiekt lokalny dynamiczny to obiekt zdefiniowany wewnÄ…trz bloku.
....
{ // otwarcie bloku
...
samochod kr30780; // obiekt powołany do życia
...
} // zamknięcie bloku, obiekt przestaje istnieć
...
Konstruktor takiego obiektu jest uruchamiany w momencie, gdy program napotyka
jego definicjÄ™.
Destruktor jest uruchamiany, gdy program opuszcza blok.
36
I.8 Dziedziczenie
I.8 Dziedziczenie
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:
przybliżenie sztuki programowania do życia codziennego
jest wielkim ułatwieniem w programowaniu zespołowym
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 {
37
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ć:
dodatkowe dane składowe
dodatkowe funkcje składowe
nowe definicje funkcji składowych już zdefiniowanych w klasie macierzystej
38
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{
....
}
39
ROZDZIAA II
ROZDZIAA II
Programowanie interfejsu użytkownika
Programowanie interfejsu użytkownika
40
II.1 Okno główne programu
II.1 Okno główne programu
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
2. #include
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.
41
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
42
II.2 Przyciski
II.2 Przyciski
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 wskaznika obiektu
" utworzenie nowego obiektu
" ewentualne zabiegi zmieniające niektóre właściwości obiektu
// LISTING 2
#include
#include
#include
#include
class MojeOkno : public QWidget
{
public: MojeOkno();
1. private: QPushButton *przycisk_koniec;
};
MojeOkno::MojeOkno()
{
setGeometry(300,200,300,160);
43
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 wskaznik do widoku macierzystego. Wskaznik 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
44
Nazwa opis
clicked() Naciśnięto przycisk
pressed() Przycisk wciśnięty
released() Zwolniono przycisk
Qpixmap pixmap( piksmapa.xmp );
Etykieta graficzna przycisku
setPixmap(pixmap)
setText( Tekst ) Definiuje etykietÄ™ przycisku
setAccel( CTRL + 'P' Przypisuje skrót klawiaturowy do przycisku
text() Zwraca etykietÄ™ przycisku
Przycisk domyślny. Jest to przycisk, który zostanie
setDefault()
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 // dla przycisków
#include // 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);
45
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
46
II.3 Etykiety tekstowe
II.3 Etykiety tekstowe
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
#include
#include
#include
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);
}
47
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 zródłowy oraz zrzut ekranu (rysunek 18) obrazujący wygląd widoku.
//LISTING 4
#include
#include
#include
class MojeOkno : public QWidget
{
public: MojeOkno();
private: QLCDNumber *numerek;
};
MojeOkno::MojeOkno()
{
setGeometry(300,200,180,100);
numerek = new QLCDNumber(this);
numerek->setGeometry(10,10,150,80);
48
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
49
II.4 Pole wejściowe
II.4 Pole wejściowe
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
#include
#include
#include
#include
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 tekst",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();
}
50
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.
51
II.5 Listy
II.5 Listy
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 wyobraznią programisty. Warto więc przyjrzeć się bliżej temu obiektowi.
//Listing 6
#include
#include
#include
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();
}
52
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();
53
II.6 Pola kombi
II.6 Pola kombi
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
podobny12.
//LISTING 7
#include
#include
#include
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
54
II.7 Ramki grupujÄ…ce
II.7 Ramki grupujÄ…ce
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
#include
#include
#include
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, char **argv)
{
QApplication okienko(argc,argv);
MojeOkno plum;
okienko.setMainWidget(&plum);
plum.show();
return okienko.exec();
}
Rysunek 22
55
7.8 Suwaki i pola przewijane
7.8 Suwaki i pola przewijane
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
#include
#include
#include
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
56
II.9 Tworzenie menu programu
II.9 Tworzenie menu programu
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 UNIX14. 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ędzi15.
Procedura tworzenia menu polega na dołączeniu dwóch plików nagłówkowych
niezbędnych przy definiowaniu linii zawierającej menu.
#include
#include
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
57
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.
58
II.10 Predefiniowane okna dialogowe
II.10 Predefiniowane okna dialogowe
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
#include
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.
59
Rysunek 25
60
II.11 Okna informacyjne
II.11 Okna informacyjne
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ę
61
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
62
ROZDZIAA III
ROZDZIAA III
Zdarzenia
Zdarzenia
63
III.1 Mechanizm obsługi zdarzeń
III.1 Mechanizm obsługi zdarzeń
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".
64
III.2 Zalety mechanizmu sygnał gniazdo
III.2 Zalety mechanizmu sygnał gniazdo
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 wyraznie
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.
65
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.
66
III.3 Gniazda predefiniowane
III.3 Gniazda predefiniowane
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.
67
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.
68
III.4 Gniazda niestandardowe
III.4 Gniazda niestandardowe
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++.
69
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 zró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
70
ROZDZIAA IV
ROZDZIAA IV
Przykłady programów w bibliotece QT
Przykłady programów w bibliotece QT
71
IV.1 Generacja i sortowanie elementów listy
IV.1 Generacja i sortowanie elementów listy
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. Znalezć 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.
72
yródło programu: Plik nagłówkowy main.h
//-----------------------------------------------------------
// Program sortujÄ…cy listÄ™ typu QMultiLineEdit
// autor: Leszek Klich
// ----------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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();
};
73
yró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);
74
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 for (int j=0; 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; ivalue(); 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();
}
}
75
IV.2 Program obliczajÄ…cy silniÄ™
IV.2 Program obliczajÄ…cy silniÄ™
Poniżej przedstawiam prosty program obliczający silnię. Zmienna n oznacza
liczbę naturalną. Program silnia jest często wykorzystywany do nauki programowania
i stanowi prosty przykład algorytmu.
n! = 1 Å" 2 Å" 3 Å" ... Å" Å" (n - 1) Å" n, n > 1
Iloczyn kolejnych liczb naturalnych od 1 do n,
0! = 1 oraz 1! = 1
n n!
0 1
1 1
2 2
3 6
4 24
5 120
6 720
7 5 040
8 40 320
9 362 880
10 3 628 800
11 39 916 800
..... ....................
20
2430 Å"10^15
Tabela 7 Tablica wartości silni
Rysunek 31
76
Oto kod zródłowy aplikacji z rysunku 31.
yródło programu: plik main.h
//-----------------------------------------------------------
// Program przedstawiajÄ…cy obliczanie silni
// autor: Leszek Klich
// ----------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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();
};
yró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);
77
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();
78
IV.3 Symulacja ruchu po paraboli
IV.3 Symulacja ruchu po paraboli
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/s2. 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 = 90o. 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ść.
79
Rysunek 32
yródło programu: plik main.h
//----------------------------------------------------------------
// Program ilustrujący ruch upadającej piłki wg ruchu po paraboli
// autor: Leszek Klich
// ---------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class Parabola : public QWidget
{
Q_OBJECT
public:
Parabola();
80
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();
};
yró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,370);
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);
81
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();
82
IV.4 Szyfrowanie metodÄ… XOR
IV.4 Szyfrowanie metodÄ… XOR
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.
83
Rysunek 33
yródło programu: plik main.h
#include
#include
#include
#include
#include
#include
#include
#include
#include
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();
};
84
yró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);
85
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();
}
86
ZAKOCCZENIE
ZAKOCCZENIE
87
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
88
Wykaz skrótów
Wykaz skrótó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
Bibliografia merytoryczna:
Bibliografia merytoryczna:
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, Aódz 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.
Inne materiały (Strony internetowe):
Inne materiały (Strony internetowe):
www.troltech.no
www.qtlibrary.glt.pl
www.intercon.pl/~sektor/cbx/
http://free.of.pl/q/qtmoux/index.php
89
Wyszukiwarka
Podobne podstrony:
2006 02 Qt ISO Maker–moja pierwsza aplikacja w Qt [Programowanie]
2001 12 Geometry Classes Under Qt Programming
2007 02 Programowanie równoległe z Qt [Programowanie]
2008 02 Polowanie na wirusy – GUI dla programu ClamAV [Programowanie]
LAB 10 INF Ĺšrodowisko programowania QT student
2001 11 Programming with Qt
zestawy cwiczen przygotowane na podstawie programu Mistrz Klawia 6
Międzynarodowy Program Badań nad Zachowaniami Samobójczymi
CSharp Introduction to C# Programming for the Microsoft NET Platform (Prerelease)
Instrukcja Programowania Zelio Logic 2 wersja polska
Program wykładu Fizyka II 14 15
roprm ćwiczenie 6 PROGRAMOWANIE ROBOTA Z UWZGLĘDNIENIEM ANALIZY OBRAZU ARLANG
io port programming 3ogqzy3bscrrpgv753q3uywjfexgwwoiiffd46a 3ogqzy3bscrrpgv753q3uywjfexgwwoiiffd46a
2009 12 Metaprogramowanie algorytmy wykonywane w czasie kompilacji [Programowanie C C ]
Podstawy Programowania Wersja Rozszerzona
więcej podobnych podstron