Kurs Programowania W QT by moux

background image

PROGRAMOWANIE W QT

KURS by moux 2004/2005

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Wstęp: 

Dla kogo jest ten kurs ???

Podstawowe pytanie i prosta odpowiedź. Kurs ten jest dla wszystkich, którzy "wychowali

się" na Windowsie, Delphi albo VBasic'u. Celem autora w wiekszym stopniu jest przekonanie do
programowania   w   Qt,   a   co   za   tym   idzie   na   Linuxa   niż   nauczanie   z   punktu   widzenia
merytorycznego. 

Dlaczego warto ?? Dlaczego Qt ??

Czy nie wydaje się wam, że windows walczy ze śmiercią ?? Czy nie macie przypadkiem,

głęboko   zakorzenionego   przeświadczenia,   że   system   microsoftu   jest   już   totalnie
skompromitowany ?? Przede wszystkim wirusami (blaster, czarnobyl, dialery), ale także całą tą
kampanią   przeszukiwania   mieszkań   (cHowaj   Windows   Do   Piwnicy   :)))).   Czy   nie   macie
przypadkiem takiego gorącego marzenia żeby któregoś dnia przyjść do domu z pracy, czy szkoły,
włączyć komputer i nie bać się niespodzianek tylko mieć pewność, że komputer się włączy i będzie
działał, aż do wyłączenia ?? Czy nie przerażają was ceny komercyjnego  oprogramowania, nie
mówię tu o windowsie bo 400 zł to nie majątek, ale o Visual Studio, Office czy Photoshopie ­
chodzi   o   oprogramowanie,   bez   którego   komputer   jest   tylko   kurzącym   się   meblem   ??   Jeśli
odpowiedź   na   którekolwiek   z   postawionych   tu   pytań   brzmi   twierdząco   to   najwyższy   czas
zaopatrzyć się w system operacyjny LINUX.

Co dalej ?? Mając linuxa na pewno dojdziesz w końcu do punktu, w którym powiesz:

"przydał by mi się program x". No ale niestety nikt jeszcze nie wpadł na to aby napisać x pod
linuxa. Powód do załamania nerwowego ?? Powrót do windowsa?? Ależ skąd moje panie, czas
wziąść się za programowanie...

A jeśli chodzi o Qt, no cóż. KDE powstało na bazie Qt. Wszystkie KMaile, Konquerory czy

Quanty to Qt. Olbrzymią zaletą Qt jest masa komponentów/klas, które sprawiają, że pisząc program
nie   musimy   myśleć   o   pewnych   podstawowych   rzeczach.   Nie   musimy   odkrywać   ameryki,   ani
wywarzać otwartych drzwi. Czytając ten kurs przekonasz się, że Qt posiada wszystko czego można
potrzebować do napisania programu i że jedynym zadaniem programisty jest umiejętne połączenie
tego wszystkiego. 

Forma kursu

Ponieważ biblioteka Qt bazuje na języku C++ wypadało by ten język znać. Jednak ponieważ

autor nie jest zbyt biegły w tym języku, każda lekcja będzie zawierała szczegółowy opis każdego
wpisywanego z palca kawałka kodu.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Przygotowanie

Przygotowanie rozpoczynamy od zaparzenia mocnej kawy Maxwell House :)))). Ponieważ

ten kurs dotyczy programowania w Qt pod linuxem nalezało by się więc zaopatrzyć w ten system
operacyjny. Qt jest dostępne również pod wina jednak pisanie w Qt pod ten system operacyjny nie
jest zbyt dobrym pomysłem. Na wina jest VB albo Delphi, które nadają się do tego o wiele lepiej. 

Wracając   do   tematu:   przygotowanie   powinno   w   normalnych   przypadkach   polegać   na

zainstalowaniu Linuxa z podstawowym pakietem narzędzi programistycznych włączając w to Qt
Designer. Niezbędne będzie więc pakiet qt­devel, przynajmniej na początek.

Narzędzia

Jak już wspomniałem kod będziemy tworzyć w środowisku programistycznym qt designer.

Oprócz tego podstawowym wręcz narzędziem będzie program qmake, który służy do generowania
skryptu   umożliwiającego   łatwą   kompilację   programu.   Oczywiście   niezbędny   będzie   również
kompilator gcc. Instalacja wszystkich tych rzeczy w dzisiejszych wersjach Linuxa jest dziecinnie
prosta więc nie będę się (póki co) nad nią rozpisywał. Dodam tylko, że: "damy radę".

Instalacja

Są przypadki, gdzie będziemy musieli coś dograć. Zassać z sieci i doinstalować. Opiszę

więc dwie zasady jakie należy znać żeby przez to przebrnąc.

Instalacja pakietu rpm
 

Jeśli zassany przez nas plik np. qt­designer będzie pakietem rpm to, żeby móc go używac

należy sobie instalnąć. Jak ?? 

    rpm ­Uvh nazwa_pakietu.rpm

Tips: 
Jeśli instalujemy z Midnight Commandera to mamy do wyboru dwa skróty:

Do pakietu rpm można wchodzić tak jak do katalogu. Po wejściu pokaże nam się plik

*INSTALL ­ wystarczy go odpalić by rozpocząć instalację.
 

Jeśli   plik   rpm   jest   aktualnie   podświetlonym   plikiem   to   można   z   linii   poleceń   wpisac

początek polecenia instalacyjnego rpm ­Uvh , a następnie użyć kombinacji klawiszy CRTL+J żeby
mc automatycznie uzupełnił to czego brakuje w poleceniu.
 

Jeśli instalacja przebiegnie pomyślnie to OK, ale jeśli nie to program rpm wyświetli nam to

czego brakuje. Niestety trzeba będzie to doinstalować według juz opisanego sposobu.
 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Kompilacja i instalacja ze źródeł
 

Bardziej hardkorowa opcja, ale wszystko jest  dla ludzi. Źródła programu lub biblioteki

przeważnie   są   rozpowszechniane   w   postaci   pliku   *.gz,   czyli   po   prostu   spakowanej.   Do
rozpakowywania  słuźy   polecenie   tar   ­zxf   nazwa_pliku.gz.   Jednak   o   wiele   prostsze  jest   uzycie
Midnight   Commandera.   Do   pliku   *.gz   wchodzimy   jak   do   katalogu   i   jego   zawartość   możamy
skopiowac w dowolne miejsce (F5). Potem według bezproblemowego scenariusza postępujemy tak:
polecenia: ./configure, ./make, ./make install
      ... a jeśli będą problemy z instalacją znaczy to, że zostaliśmy wybrani do zgłębienia tajemnicy
kompilacji i wogóle budowy systemu operacyjnego Linux ­ TYLKO SIĘ CIESZYĆ. 

Wymagania

Nie będę obwijał w gazete wymagania są.... zależne od cierpliwości tego kto będzie się z

tymi wymaganiami zmagał. U mnie na PIII600, 256MB z Riva16MB chodzi to wszystko w miarę,
więc wszystko koło tego będzie dobre. 

W dziedzinie softwaru to polecam Linuxa w wersji bardziej idiotoodpornej tj. Aurox może

PLD. Chodzi po prostu o to aby czynności związane z przygotowaniem środowiska nie trwały zbyt
długo   i   nie   były   zbyt   skomplikowane.   Dobry   system   opracyjny   to   taki,   który   pozwoli
użytkownikowi zapomnieć o swoim istnieniu :)))))))). 

Autor korzysta obecnie (w chwili powstawania kursu) z systemu Aurox 9.4 z kernelem 2.4.22 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Qt Designer (3) ­ filozofia pracy

Żeby nie błądzić musimy poznać krok po kroku obsługę tego środowiska tj. np. jak od

formatki przejść do oprogramowania jakiegoś zdarzenia. Nie jest to trudne, ale "trzeba umieć". 

Ponadto w lekcji tej napiszemy pierwszy program w Qt ­ Nieśmiertelne "Hello World".

Tips

Opis   postępowania   zamieszczony   poniżej   należy   wydrukować   i   powiesić   na   ścianie,

ewewntualnie nauczyć się na pamięć. Zawiera on opis "krok po kroku" co trzeba zrobić aby powstał
program.

"Krok po kroku"

1. Odpalamy Qt Designera np. poleceniem "designer".
2. W oknie "Qt Designer ­ New/Open" wybieramy C++ Projekt i "OK"
3.  W oknie "Project Setting", klikamy "...", tworzymy nowy folder w katalogu domowym o nazwie

takiej jak program, np. helloworld. Jako nazwę pliku tez wpisujemy hellowolrd.pro .

4.  Wybieramy "File" ­ > "New" ­ > "Dialog"
5.  Klikamy kombinację klawiszy CTRL+E ­ czyli przełączamy widok na edycję kodu źródłowego.

Na pytanie czy chcesz storzyć plik ui.h odpowiadamy "YES". 

6. Wybieramy "File" ­ > "Save All" i zapisujemy plik opisujący wygląd formatki (form1.ui) w

folderze przeznaczonym na projekt np. o nazwie "helloworld"

7.  Wybieramy "File" ­ > "New" ­ > "C++ Main". "OK".
8.  Wybieramy "File" ­ > "Save All". W tym momencie zapisujemy plik "main.cpp".
9.   Uruchamiamy   terminal   w   katalogu   z   programem   i   wpisujemy   z   palca   po   kolei   qmake,

make,     ./helloworld

 
     Jeśli po wpisaniu tej ostatniej komendy  naszym  oczkom ukaże się pusta  forma znaczy, że
jesteśmy w domu i możemy iść na piwo. Jeśli natomiast polecenie make pokaże jakieś błędy to
najpierw krzyczymy na cały głos, a potem piszemy obraźliwego emaila do autora. Opanowanie
powyższych kroków jest absolutnie najważniejsze, tak więc jeśli jakiś krok jest  niezrozumiały
piszcie do mnie postaram się odpowiadać jak najszybciej.
     Jeśli polecenie qmake zwórci błędy oznaczać to będzie, że albo nie jesteśmy w katalogu z
programem albo nie ma tam pliku "main.cpp". Normalnie pusty projekt przed kompilacją powinien
się składać z trzech plików: form1.ui, helloworld.pro, main.cpp. 

Modyfikacja programu ­ dodajemy bajery

Wybieramy menu "Edit" ­> "Slots" i w okienku, które sie pojawi klikamy "New function".

Opis wypełnienia okna znajduje się na obrazku: 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





 

 

Jako kolejny krok dodajemy przycisk do formy. Jest to stosunkowo proste więc pomijam

zagłębianie się w szczegóły.

 
Następnie: "Edit" ­> "Connections" ­> "New": wypełniamy według następującej kolejości. 
      Sender: pushButton1
      Signal: clicked()
      Receiver: Form1
      Slot: saycheese()

 
 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Jeszcze przed kompilacją mała kosmetyka. Klikamy dwukrotnie na przycisku i wpisujemy w

oknie, które się pojawi np. say cheese. 

Przechodzimy teraz do edycji kodu. Window ­> "Edit Form1". W oknie, które się pojawi

powinna już być implementacja funkcji saycheese (jeśli nie ma wróć do początku). Całość kodu
powinna wyglądać następująco: 
 

#include  <qmessagebox.h>

void Form1::saycheese()
{
        QMessageBox sch ( "My name is first progz", "I say cheese for you",
        QMessageBox::NoIcon,
        QMessageBox::Ok, 0, 0 );
        sch.exec();
}

Po skompilowaniu "make" i odpaleniu "./helloworld" powinien pokazac nam się potworek w

stylu poniższego. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Zabawa ze stringami

Zanim zaczniesz czytać ten rozdział upewnij się, że opanowałeś do perfekcji procedury

związane   z  przygotowaniem  programu, tj.  stworzenie  projektu,  pliku  głównego programu oraz
kompilowanie stworzonego kodu. Rozdział ten pomija już te fragmenty tworzenia oprogramowania
przy pomocy Qt Designera. 

Każdy programista na pewno stanie kiedyś lub już stanął przed trudnym problemem analizy,

zarządzania, rozbijania, scalania i mówiąc bardziej ogólnie modyfikowania stringów. Czym wobec
tego są stringi ? Są to ciągi znaków, które najczęściej są do programu wprowadzane czy to z palca
czy z pliku tekstowego. Cały problem ze stringami polega na tym, że należy je konwertować często
wielokrotnie. Po za tym jest to jeden z najbardziej popularnych formatów danych. 

Do obsługi stringów w Qt służą dwie klasy QChar i Qstring, które obudowują standardowy

typ języka C++ char i tablice znakowe. Klasy te zawierają w sobie wiele przydatnych funkcji,
dzięki którym możemy zapomnieć o takich elementach modyfikowania stringów jak wyciągnięcie
jakiegoś znaku/znaków czy też obliczenia długości itd.

Dla prostego przećwiczenia tego co napisałem powyżej proponuję stworzyć nowy projekt

według przepisu z lekcji drugiej i umieścić na nim jedno pole textEdit, jedno pole lineEdit oraz
przycisk pushButton. Następnie stworzyć slota o nazwie np. obliczrozm(). W następnej kolejności
podpinamy tegoż slota do zdarzenia wciśnięcia przycisku i przełączamy widok do edycji kodu
CTRL+E. W edytorze kodu wystarczy wkleić poniższy kod aby obliczyć ilość znaków w polu
tekstowym:

        void Form4::napiszcos()
        {
                 QString dl = textEdit3­>text();
                 lineEdit3­>setText(  QString::number( dl.length() ) );
        }

Krótkie wyjaśnienie powyższego kodu:

QString dl = textEdit3­>text();

Jak można się domyślić jest to przypisanie do nowo utworzonej zmiennej zawartości pola

textEdit3.   Przy   okazji   wtrącę   małą   dygresję.   Poniższy   kod   można   było   z   takim   samym
powodzeniem   umieścić   w   jednej   linii   jednak   podejście   takie   utrudnia   znajdowanie   błędów   w
programach.

lineEdit3­>setText ( QString::number ( dl.length() );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Po pierwsze za pomocą funkcji setText ładujemy do zmiennej text obiektu lineEdit3 to co

jest w nawiasie. W nawiasie mamy użytą funkcję length() pochodzącą z klasy QString, ponieważ
funkcja ta zwraca wartość liczbową int musimy ją skonwertować do postaci znakowej co umożliwia
nam funkcja numer.

Dociekliwy czytelnik powinien w tym miejscu spytać “O CO CHODZI” z tymi zmiennymi

funkcjami i z tym zapisem raz “::”, a innym razem “.”. Zapis z podwójnym dwukropkiem jest dość
prosty do zrozumienia gdyż  wskazuje nam na to,  że  odwołujemy się bezpośrednio do funkcji
znajdującej się wewnątrz klasy – w tym przypadku QString. Zapis z jedną kropką mówi natomiast,
że wywołujemy funkcję z wewnątrz klasy w której się aktualnie znajdujemy. 

Jednak program, który oblicza długość pola tekstowego to było by trochę mało musimy więc

go trochę rozbudować. Ponieważ jest to dopiero początek kursu, a szkoda by było pisać kolejnego
“hello worlda”, proponuję zrobić programik, który zakoduje nasz adres e­mail, tak abyśmy mogli go
stosunkowo bezpiecznie umieszczać na naszych stronach WWW (chodzi tu o zabezpieczenie anty­
spamowe). Idea programu została zaczerpnięta ze strony internetowej www.nospam­pl.net polega
ona   na   tym,   że   znaki   adresu   internetowego   są   konwertowane   na   swoje   reprezentacje
heksadecymalne   co   nie   robi   różnicy   przeglądarce,   a   jedynie   programom   skanującym   sieć   w
poszukiwaniu zbłąkanych adresów e­mail, na które (i z których) można wysyłać SPAM. 

Do   programu   będziemy   potrzebować   znów   dwóch   pól   tekstowych   jednego   textEdit'a   i

jednego   lineEdit'a   no   i   oczywiście   jakiegoś   ładnego   pushButton'a.   Po   dodaniu   komponentów
musimy   utworzyć   slota,   a   następnie   podpiąć   go   no   zdarzenia   clicked().   Po   tym   wszystkim
proponuję użyć poniższego kodu:

void Form2::doitnow()
{
    textEdit4­>setText( doitnow2(lineEdit4­>text()) );
    // linia ta do pola tekstowego textEdit4 wstawia
    // tekst przetworzony przez funkcję doitnow2() 
}

QString Form2::doitnow2(const QString & input) const
// pierwsze QString mówi o typie zwracanym przez funkcję
    {
        QString r = input;
        // do zmiennej r przypisujemy wartość podaną jako 
        // parametr wywołania funkcji

        int i, x = r.length();
        // deklarujemy zmienne i oraz x 
        // do zmiennej x przypisujemy długość zmiennej r
        // czyli stringa podanego w parametrze wywołania funkcji

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





        r = "";
        // zerujemy zmienną r, tj. przypisujemy do niej pusty string

        for (i=0;i<=x;i++)
        {
            if ( !(int)input[i] == 0) {
            // jeśli znak o numerze [i] ze stringa input jest 
            // różny od 0, to wykonujemy poniższe
                r.append ( "&#" );
            // dodajemy do stringa r początek htmlowy 
                r.append( QString::number( (int)input[i] ) );
            // dodajemy do tegoż stringa numeryczną reprezentację znaku
                r.append ( ";" );
            // zakańczamy stringa
            }

        }
        return r;
    }
// wartość zwracana przez tą funkcję trafia do pola tekstowego.

Całość powinna wyglądać tak:

       Tekst znajdujący się w polu opisanym jako crypted można skopiować i wstawić do strony
WWW. Przeglądarki zinterpretują to prawidłowo jednak programy spamerskie nie dadzą rady.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Program bardziej złożony ­ kalkulator

Za pomocą tego tekstu będzemy mogli przećwiczyć ponownie sposób tworzenia aplikacji w

Qt  Designerze, a także  przećwiczymy tworzenie GUI.  W  tym tekście  stworzymy  sobie prosty
kalkulatorek.

Pierwsze kroki po raz drugi

     Opiszę jeszcze raz sposób tworzenia projektu, szerszy opis znajduje się w poprzednim artykule,
tutaj rozpiszę to tylko dla przypomnienia. 
    1. Gdy Qt Designer się uruchomi w Oknie "New" wybieramy "C++ project"
    2. Zapisujemy projekt do osobnego katalogu "kalk", po nazwą "kalk.pro".
    3. Wybieramy menu "New" i z okienka wskazujemy Widget
    4. Zapisujemy formatkę pod nazwą mFrmCalc.ui
    5. Ponownie wybieramy menu New lecz tym razem wskazujemy main.cpp
    6. Zapisujemy wszystko i jak na razie wystarczy.
 

Tworzenie GUI

Aby nasz kalkulator wyglądał jakoś po ludzku, musimy na wstawiać na niego przyciski z

cyferkami. Wykonuje się to w prosty sposób, klikając najpierw na przycisk w Toolboxie Common
Widget, a następnie na formatce. Sugerowany wygląd zamieszczam ponieżej: 

Przyciski   oznaczone   cyframi   mają   ustawiony   parametr   width   na   50,   cała   reszta   jest

ustawiona na oko. Proponuję jeszcze ustawić parametr ReadOnly komponentu lineEdit na TRUE.
Ponadto ustawimy jeszcze tekst tego komponentu na "0", tak aby zaraz po uruchomieniu nasz
kalkulator się wyzerował. Ustawimy też właściwość hAlign na AlignRight, dla lepszego efektu.

To   co   przed   chwilą   stworzyliśmy   nie   jest   jeszcze   zbyt   dobrym   punktem   wyjścia   do

rozpoczęcia pisania kodu. Musimy jeszcze nadać imiona naszym dzieciom czyli przyciskom, tak
aby   nie   zginąć   zbyt   szybko   w   gąszczu   pushButtonX.   Nasze   przyciski   numeryczne   nazwiemy
według poniższego klucza przycisk 9 pb9, przycisk 8 pb8 itd. Przyciski działań: podzielić pbDiv,
pomnożyć pbMulti,   dodać  pbAdd,  odjąć   pbTa.  Przycisk  kropki  pbDot,  zmiany  znaku  pbChar,
potęga pbPote, pierwiastek pbPierw, no a wynik niech się nazywa pbWynik. Kasowanie niech się
nazywa pbC.

Tworzenie slotów

Jak widać mamy tutaj 19 przycisków, będziemy więc potrzebować 19 slotów. Każdy z nich

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





będzie odpowiadać za kliknięcie wybranego przycisku. Można to oczywiście wykonamy prościej
np. jednym slotem ale dla przećwiczenia lepiej użyć 19. W tym celu wchodzimy w menu edit i
wybieramy slots. W okienku, które sie pojawi dodajemy funkcje i jako nazwy wpisujemy np.
pb1Click(). Powtarzamy tą czynność, aż wszystkie przyciski będą miały swojego slota.

Gdy już wszystkie sloty będziemy mieć potworzone, możemy zabawić się w tworzenie

połączeń między wciśnięciem przycisku, a wywołaniem funkcji slota. W tym celu wykorzystujemy
okienko edit connections. Przy tworzeniu wszystkich połączeń będzie trochę klikologii, aczkolwiek
powyższy sposób jest chyba najszybszym na oprogramowanie takiej ilości funkcji. Gdy uda nam się
stworzyć te połączenia możemy przystąpić do etapu kodowania.

Kodowanie

Na początek zajmiemy się funkcjami do wstawiania cyferek do naszego edita. Posłuży nam

do   tego   następujący   kod,   który   będzie   jednakowy   dla   wszystkich   przycisków   oznaczonych
cyferkami: 

void Form1::pb9Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "9" );
    } else {
        lineEdit1­>setText( "9" );
    }
}

Z powyższego kodu widać, że dopisanie nowej cyferki nastąpi tylko wówczas gdy zawartość

pola będzie równa zero, w przeciwnym wypadku cyfra zostanie dopisana do tego co już w tym polu
się znajduje. 

Funkcja przycisku kropki będzie za to wyglądać w ten sposób: 

void Form1::pbDotClick()
{
    QString string( lineEdit1­>text() );
    int n = string.contains( '.', FALSE );
    
    if (n < 1 ) {
        lineEdit1­>setText( lineEdit1­>text() + "." );
    }
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

W pierwszych dwóch linijkach sprawdzamy czy nasz lineEdit1 posiada już wpisaną kropę, a

następnie jeśli liczba kropek będzie mniejsza od 1 to dopisujemy nam naszą kropę. 

Kasowanie lineEdit1'a

W tym miejscu dochodzimy do zmiennych, o których wcześniej nie wspominałem. Otóż

nasz   program  będzie  sie  posługiwał  trzema  zmiennymi   typu   float.  Deklarację tych  zmiennych
proponuję umieścić na samej górze programu, jeszcze przed deklaracją pierwszego slota. Wyglądać
to powinno mniej więcej w ten sposób:
 
 float a,b,c; 

Zmienne   te   będziemy   wykorzystywali   do   operacji   matematycznych   tj.   dzielenie,   czy

pierwiastkowanie. Dlatego tez funkcja kasowania musi również wyzerować te zmienne. Wyglądać
to będzie tak: 

void Form1::pbCClick()
{
 lineEdit1­>setText( "0" );
 a = 0;
 b = 0;
 c = 0;
}

Funkcja dodawania znaku

void Form1::pbCharClick()
{
    if (lineEdit1­>text() != "0") {    
        QString string( lineEdit1­>text() );
        int n = string.contains( '­', FALSE );
        
        if (n < 1) {
            lineEdit1­>setText( "­" + lineEdit1­>text() );
        } else {
            int a = string.length();
            QString t = string.right( a ­ 1 );
            lineEdit1­>setText( t );
        }
    }

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





}

Najpierw sprawdzamy czy nasze pole jest czymś innym niż zero, a jeśli jest to musimy się

umieć zachować w zależności od tego czy do pola już został znak zapisany. Jeśli został to musimy
go wykasować, a jeśli jeszcze go tam nie ma to go dodajemy. 

Zaczynamy działać

Pierwsze   działanie   jakie   dodamy   do   naszego   programu   będzie   podnosić   liczę   z   pola

edycyjnego do potęgi. Będzie to wymagało od nas dodania dwóch plików nagłówkowych: stdlib.h i
math.h. Gdy je dodamy wykorzystamy poniższy kod:

// potęgowanie
void Form1::pbPotClick()
{
    a = atof(lineEdit1­>text());
    b = a;
    c = a*b;
    lineEdit1­>setText( QString::number(c) );
}

// no i od razu przy okazji pierwiastkowanie
void Form1::pbPierwClick()
{
    a = atof(lineEdit1­>text());
    c = sqrt(a);
    lineEdit1­>setText( QString::number(c) );
}

Funkcje bardziej skomplikowane np. Dodawanie

Komplikacje związane z dodawaniem polegają na tym, iż dodawana liczba trafia jakby do

pamięci   i   czeka   na   swój   dodajnik.   Po   zapisaniu   do   programu   liczby   dodawanej   użytkownik
wprowadza rodzaj działania jaki chce wykonać. Gdy te dwie informacje juz mamy potrzebujemy
już tylko drugiej liczby do dodania i możemy zaprezentować wynik. Aby to wszystko miało ręce i
nogi posłużymy się zmienną np. int, która będzie przechowywała typ działania jakie wykonujemy.
Deklaracja tej zmiennej powinna mieć miejsce tuż pod zmiennymi a,b,c.

Wszystkie funkcje tj. dodawanie, odejmowanie, mnożenie i dzielenie będą się odbywać w

funkcji pbWynikClick, natomiast w funkcjach przypisanych do przycisków np. dodawania będą
tylko instrukcje służące zapisaniu wartości pola do zmiennej float. Całość wyglądać będzie tak: 

//dodawanie

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

void Form1::pbAddClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 1;
     lineEdit1­>setText( "0" );
}

// mnożenie
void Form1::pbMultiClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 3;
   lineEdit1­>setText( "0" );
}

// odejmowanie
void Form1::pbTaClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 2;
    lineEdit1­>setText( "0" );    
}

// dzielenie
void Form1::pbDivClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 4;
    lineEdit1­>setText( "0" );    
}

// wynik
void Form1::pbWynikClick()
{
    b = atof(lineEdit1­>text());
    switch( funkcja ) {
        case 1: c = a + b;
           break;
         case 2: c = a ­ b;
            break;
         case 3: c = a * b;
            break;
         case 4: c = a / b;

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





            break;
    }
    lineEdit1­>setText( QString::number(c) );    
}

jak widać wszystko jest proste i czytelne. Proponuję dla wprawy dodać obsługę błędu dzielenia
przez zero. Na koniec zamieszczam cały kod programu: 

#include <stdlib.h>
#include <math.h>

float a, b, c;
int funkcja;

void Form1::Init()
{
 pbCClick();
 a = 0;
 b = 0;
 c = 0;
 funkcja = 0;
}

void Form1::pb0Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "0" );
    } else {
        lineEdit1­>setText( "0" );
    }
}

void Form1::pb1Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "1" );
    } else {
        lineEdit1­>setText( "1" );
    }
}

void Form1::pb2Click()
{
    if (lineEdit1­>text() != "0") {

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

        lineEdit1­>setText( lineEdit1­>text() + "2" );
    } else {
        lineEdit1­>setText( "2" );
    }
}

void Form1::pb3Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "3" );
    } else {
        lineEdit1­>setText( "3" );
    }
}

void Form1::pb4Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "4" );
    } else {
        lineEdit1­>setText( "4" );
    }
}

void Form1::pb5Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "5" );
    } else {
        lineEdit1­>setText( "5" );
    }
}

void Form1::pb6Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "6" );
    } else {
        lineEdit1­>setText( "6" );
    }
}

void Form1::pb7Click()

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "7" );
    } else {
        lineEdit1­>setText( "7" );
    }
}

void Form1::pb8Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "8" );
    } else {
        lineEdit1­>setText( "8" );
    }
}

void Form1::pb9Click()
{
    if (lineEdit1­>text() != "0") {
        lineEdit1­>setText( lineEdit1­>text() + "9" );
    } else {
        lineEdit1­>setText( "9" );
    }
}

void Form1::pbDotClick()
{
    QString string( lineEdit1­>text() );
    int n = string.contains( '.', FALSE );
    
    if (n < 1 ) {
        lineEdit1­>setText( lineEdit1­>text() + "." );
    }
}

void Form1::pbPotClick()
{
    a = atof(lineEdit1­>text());
    b = a;
    c = a*b;
    lineEdit1­>setText( QString::number(c) );
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

void Form1::pbPierwClick()
{
    a = atof(lineEdit1­>text());
    c = sqrt(a);
    lineEdit1­>setText( QString::number(c) );
}

void Form1::pbAddClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 1;
     lineEdit1­>setText( "0" );
}

void Form1::pbMultiClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 3;
   lineEdit1­>setText( "0" );
}

void Form1::pbTaClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 2;
    lineEdit1­>setText( "0" );    
}

void Form1::pbDivClick()
{
    a = atof(lineEdit1­>text());
    funkcja = 4;
    lineEdit1­>setText( "0" );    
}

void Form1::pbWynikClick()
{
    b = atof(lineEdit1­>text());
    switch( funkcja ) {
        case 1: c = a + b;
           break;
         case 2: c = a ­ b;
            break;

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





         case 3: c = a * b;
            break;
         case 4: c = a / b;
            break;
    }
    lineEdit1­>setText( QString::number(c) );    
}

void Form1::pbCharClick()
{
    if (lineEdit1­>text() != "0") {    
        QString string( lineEdit1­>text() );
        int n = string.contains( '­', FALSE );
        
        if (n < 1) {
            lineEdit1­>setText( "­" + lineEdit1­>text() );
        } else {
            int a = string.length();
            QString t = string.right( a ­ 1 );
            lineEdit1­>setText( t );
        }
    }
}

void Form1::pbCClick()
{
 lineEdit1­>setText( "0" );
 a = 0;
 b = 0;
 c = 0;
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Pliki graficzne

Najprostsza wersja przeglądarki graficznej
1. Tworzymy nowy projekt (“patrz Qt Designer ­ filozofia pracy”) 
2. Układamy na formatce pixmapLabel w zakładce “Display” i przycisk pushButton 
3. Tworzymy slota np. o nazwie load 
4. Tworzymy połączenie między klinięciem przycisku i slotem 

Do   stworzenia   programu   wykorzystamy   kod,   który   będzie   wykorzystywał   okienko

dialogowe Otórz/Zapisz. Za pomocą tego okieka pobierzemy nazwę pliku jaki chcemy załadować
do programu. Funkcja tego okna będzie wyglądała tak: 

#include <qfiledialog.h>

void Form1::load()
{
 QString s = QFileDialog::getOpenFileName(
              "/home",
              "Pliki graficzne ( *.jpg *.gif *.png )",
              this,
              "Otworz plik"
              "Wybierz plik" );
 pixmapLabel2­>setPixmap( s );
}

     Chociaż powyższy kod działa powinniśmy jednak wyposażyć go w obsługę błędów:

#include <qfiledialog.h>

void Form1::load()
{
QString s = QFileDialog::getOpenFileName(
            "/home",
            "Pliki graficzne ( *.jpg *.gif *.png )",
            this,
            "Otworz plik"
            "Wybierz plik" );
 if ( s != NULL ) {
       pixmapLabel2­>setPixmap( s );
  }
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Pliki: zapis, odczyt, QFileDialog.

Chyba najbardziej pocieszną rzeczą jaką można wykonać w każdym języku programowania

jest notatnik. Zanim jednak wykonamy prawdziwy notatnik, taki z prawdziwego zdarzenia musimy
poznać   dwie   klasy   przydatne   przy   obsłudze   plików:   QFile   i   QFileDialog.   W   najprostszym
przykładzie zaczytanie pliku tekstowego będzie wyglądało tak: 

#include <qfile.h>

void Form1::zapis()
{
  QFile fp("/home/moux/plik.txt");
  fp.open( IO_WriteOnly );
  fp.writeBlock( textEdit1­>text(), qstrlen(textEdit1­>text()) );    
  fp.close();
}

void Form1::odczyt()
{
  QFile fp("/home/moux/plik.txt");
  char buff[255];
  fp.open( IO_ReadOnly );
  textEdit1­>clear();
  while (!fp.atEnd() )
  {
      fp.readLine(buff, sizeof(buff));
      textEdit1­>append(buff);
  }
  fp.close();
}

Oczywiście funkcje odczyt i zapis są slotami połączonymi z przyciskami, tekstEdit1 jest

więc komponentem do przechowywania tekstu. Podobieństwo ze standardowym użyciem jest wiele
jednek widać, że wszystkie funkcje użyte do obsługi plików są metodami klasy QFile. W tym
przykładzie zaczytujemy i zapisujemy cały plik za jednym zamachem. Funkcja writeBlock (uwaga
na wielkości znaków ­ muszą być zachowane) zapisuje tekst z komponentu textEdit1, uzyskany
przy   pomocy   funkcji   text().   Długość   tego   tekstu   jest   obliczna   za   pomocą   funkcji   qstrlen.   Do
odczytania danych używamy funkcji readLine, ponieważ nie wiemy ile znaków ma tekst zapisany w
pliku. Zaczytujemy te dane tak długo jak funkcja atEnd (informująca o tym czy osiągnęliśmy
koniec pliku) zwaraca wartość FALSE (definicja tej funkcji bool QFile::atEnd()). Każda odczytaną
linię zapisujemy do bufora buff, a następnie doklejamy do textEdita.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Innym   ciekawym   sposobem   na   dostęp   do   pliku   są   strumienie,   klasy   QTextStream   i

QDataStream.   Odczyt   i   zapis   tego   samego   tekstu   jak   w   pierwszym   przykładzie   przy   użyciu
QTextStream miałby taką postać:

void Form1::zapis2()
{
    QFile fp( "/home/moux/plik.txt" );
    fp.open( IO_WriteOnly );
    QTextStream stream( &fp ); 
    stream >> textEdit1­>text();   
    fp.close();
}

void Form1::odczyt2()
{
    QFile fp( "/home/moux/plik.txt" );
    fp.open( IO_ReadOnly );
    QTextStream stream( &fp ); 
    textEdit1­>setText( stream.read() );
    fp.close();
}

Znaczącą   różnicą   podczas   odczytu,   w   przypadku   tych   dwóch   zastosowań   jest   to,   iż   w

drugim przypadku przy odczycie i zapisie tekstów stosowane jest kodowanie lokalne 8­bitowe. Dla
nas polskich polaków oznacza to nasze ulubione ISO­8859­2, czyli odczytany plik będzie miał
krzaczki. 

QFileDialog

Gdy w obojętnie jakim edytorze napisanym przy użyciu Qt klikamy "Otwórz" albo "Zapisz

jako". Pokazuje się nam okienko, które nie jest niczym innym jak klasą QFileDialog. Do czego to
okienko jest wykorzystywane z punktu widzenia programisty ?? Przede wszystkim do tego, aby dać
użytkownikowi możliwość wskazania pliku (a więc uzyskania jego ścieżki i nazwy) do zapisania
lub   otwarcia.   Wszystkie   metody   tej   klasy   pozwalają   sterować   wyglądem   tego   okienka   i   jego
zawartością. Aby móc korzystać z tej klasy musimy dołączyć plik nagłówkowy qfiledialog.h. W
naszym przykładzie wykorzystanie tej klasy będzie sie przedstawiać następująco:

void Form1::zapis2()
{
 QString fname = QFileDialog::getSaveFileName(

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

                    "/home",
                    "Pliki tekstowe (*.txt)",
                    this,
                    "",
                    "Zapisz jako" );
 if ( fname != NULL) {
     QFile fp( fname );
     fp.open( IO_WriteOnly );
     QTextStream stream( &fp ); 
     stream << textEdit1­>text();   
     fp.close();
 }
}

void Form1::odczyt2()
{
 QString fname = QFileDialog::getOpenFileName(
                    "/home",
                    "Pliki tekstowe (*.txt)",
                    this,
                    ""
                    "Otwórz plik" );
 if ( fname != NULL) {
    QFile fp( fname );
    fp.open( IO_ReadOnly );
    QTextStream stream( &fp ); 
    textEdit1­>setText( stream.read() );
    fp.close();
 }
}

Zaprezentowane powyżej zastosowanie może być użyte wszędzie tam gdzie konieczne jest

szybkie wykorzystanie tego okienka dialogowego. Gdybyśmy jednak chcieli mieć większą nad nim
kontrolę powinniśmy zadeklarować to okno jako osobną zmienną i wykorzystywać jej właściwości
tak jak to robimy w przypadku textEdit'a. Przykład takiego wykorzystania:

void Form1::odczyt3()
{
 QFileDialog *openfd = new QFileDialog( this, "", TRUE );
 openfd­>setMode( QFileDialog::ExistingFile );
 openfd­>addFilter( "Pliki tekstowe (*.txt)" );
 openfd­>setShowHiddenFiles( TRUE );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





 openfd­>setDir("/home");
 openfd­>show();
 if ( openfd­>selectedFile() != NULL ) {
      // jakies operacje
 }
}

Jak   widać   takie   zastosowanie   pozwoliło   nam   dodać   opcję   otwierania   plików   ukrytych.

Pondto widać tutaj dość ciekawą opcję setMode. Pozwala ona zdecydować o tym w jakim trybie ma
pracować nasze okienko. Do wyboru mamy:
 QFileDialog::AnyFile ­ Pozwala wskazanie pliku, który może lecz nie musi istnieć. Ten tryb jest
najbardziej dostosowany do operacji zapisu plików.
 QFileDialog::ExistingFile ­ Pozwala na wskazanie pliku juz istniejącego.
  QFileDialog::Directory ­ Pozwala na wskazanie katalogu. Wyświetlane są zarówno pliki jak i
katalogi.
 QFileDialog::DirectoryOnly ­ Wyświetlane sa tylko katalogi. 
 QFileDialog::ExistingFiles ­ Pozwala na wskazanie wielu plików. 
  Opcje   te   są   porównywalne   do   funkcji   operacji,   które   wykorzystywaliśmy   w   pierwszym
przykładzie. 

Warto   jeszcze  wspomnieć   o  tym,   iż   w  funkcji   addFiler   (podobnie   jak   w  funkcjach   do

pobierenia nazw plików) możemy definiowac wiele masek rozszerzeń. Kolejne maski podaje sie w
sposób następujący np.: ("Pliki png (*.png);;Pliki jpg (*.jpg)"), oddzielając je jak widać dwoma
średnikami.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Komponenty wyboru, checkBox i radioButton

Zanim przejdziemy do utworzenia projektu słowo wstępu. Co to są komponenty wyboru i

jak z nich korzystać ?? Generalnie w programowaniu okienkowym mamy trzy klasy/komponenty,
które   są   z   tym   związane:   CheckBox,   RadioButton   i   ButtonGroup.   Komponent   ButtonGroup
organizuje komponenty jakby w jedną całość. Posiada on zmienną typu integer, która przechowuje
numer   ostatniego   wciśniętego   komponentu.   W   przypadku   gdy   na   ButtonGroup   umieścimy
komponenty RadioButton będziemy dzięki temu mogli w prosty sposób określić, który z nich jest
włączony, a który nie. Komponenty CheckBox pozwalają nam natomiast włączać/wyłączać kilka
opcji. Do grupowania CheckBox'ów lepiej użyć komponentu GroupBox, który jest jedynie ramką z
tytułem.

Do pracy rodacy

Jak zwykle rozpoczynamy od stworzenia projektu, qtcheckb, dodaniu formatki QDialog i

pliku main.cpp. Następnie dodamy komponenty: QButtonGroup, QGroupBox, 3 x QRadioButton,
które umieszczamy na komponencie buttonGroup. Do tego wszystkiego dodamy jeszcze 3 sztuki
QCheckBox, które wcipolimy na QGroupBox.

Kod programu

W tym tekście wykonamy program, który będzie potrafił zmienić kolor swojego tła. Na

początek obsługa QRadioButton. Klasa ta ma w swojej budowie zdefiniowany sygnał clicked(int).
Zmienna int podana przy wywołaniu tej funkcji przechowuje  numer wciskanego radioButtona.
Funkcja/slot, którego możemy używać z tą funkcja wygląda tak:

void Form1::rbClick( int x )
{
    switch ( x ){
    case 0:  setBackgroundColor( "#FF0000" );
        break;
    case 1:  setBackgroundColor( "#00FF00" );
        break;
    case 2:  setBackgroundColor( "#0000FF" );
        break;
    }
 setCaption( backgroundColor().name() );

 checkBox1­>setChecked(FALSE); // zerujemy chceckboxy, 
 checkBox2­>setChecked(FALSE);

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





 checkBox3­>setChecked(FALSE);
}

Połączenie tej funkcji z sygnałem, oczywiście dokonujemy w okienku Connections. 
Z powyższego kodu widać moc wykonawczą zmienne x. 

Obsługa CheckBox

W przypadku checkBox nie jest juz tak prosto. Komponenty te mogą pracować oddzielnie, a

co   za   tym   idzie   ilość   wszystkich   kombinacji   w   przypadku   3   sztuk   wynosi   9.   Jak   dotąd   nie
spotkałem się z komponentem grupującym checkBox'y dlatego do ich obsługi wymyśliłem funkcję:

void Form1::edColor()
{
 QColor cl( backgroundColor() ); 
 int r, g, b;
 r = cl.red();
 g = cl.green();
 b = cl.blue();
 
 int x = buttonGroup1­>selectedId();
 
 if ( checkBox1­>isChecked() ) {
     r = 127; 
 } else {
     if ( x == 0 ) r = 255; else r = 0;
 }
 if ( checkBox2­>isChecked() ) {
     g = 127;
 } else {
     if ( x == 1 ) g = 255; else g = 0;
 }
 if ( checkBox3­>isChecked() ) {
     b = 127;
 } else {
     if ( x == 2 ) b = 255; else b = 0;
 }
 
 setBackgroundColor( QColor( r, g, b ) );
 setCaption( QColor( r, g, b ).name() );
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Funkcja ta najpierw rozdziela kolory na rgb, a następnie zmniejsza lub zwiększa kolor o

połowę :))). Funkcję tą musimy podpiąć do każdego checkBox'a do sygnału clicked(). 

Generalnie sposób działania tej funkcji najlepiej będzie analizować na podstawie działania

programu.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Wywołanie programu z parametrem

Do uruchamiania programu w Qt przygotowano, spejalną świetną funkcję. Sprawia ona, że

obsłużenie programu wywołanego z parametrami to naprawde bajka i frajdziowcha :))

Pierwszą i zasadniczą rzeczą od jakiej zaczniemy jest tzw. help. Normalnie jak program

uruchomimy z parametrem ­­help to pojawi nam się pomoc. Jak to zrobić w Qt ? Zaczniemy od
stworzenia   projektu,   z   dwoma   formami   form1   i   form2.   Nastepnie   zajmiemy   się   edycją   pliku
main.cpp: 

#include <qapplication.h>
#include "form1.h"
#include "form2.h"

int main( int argc, char ** argv )
{
    QApplication a( argc, argv );
    QString opcja = "­­normal";
    if ( argc > 1 ) opcja = argv[1];
                            
    if ( qstrcmp(opcja, "­­normal") == 0 ) {
        Form1 w;
        w.show();
        a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
        return a.exec();
    } else
    if ( qstrcmp(opcja, "­­settings") == 0 ) {
        Form2 w2;
        w2.show();
        a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
        return a.exec();
    } else 
        if ( qstrcmp(opcja, "­­help") == 0 ) {
        qWarning( "Uzycie \n"  
                  "param [opcja]\n"
                  "­­normal \t Uruchamia program w trybie normalnym.\n"
                  "­­settings \t Uruchamia program w trybie ustawien.\n"
                  "­­help \t\t Wyswietla pomoc."
                  );
    } else {
        qWarning( "Nieznana opcja: " + opcja + "\n Sprobuj ­­help" );
    }
    return 0;   
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Jak widać z tego przykładu program posiada cztery możliwości uruchomienia: w trybie

normalnym,   gdzie   uruchamiane   jest   normalne   okienko   programu,   w   trybie   ustawień   gdzie
wyświetlane jest okienko form2 z ustawieniami, w trybie pomocy konslowej, dzie wyświetlane sa
wszystkie dostępne opcje oraz w trybie informujacym o złej komendzie. Pobranie parametrów
odbywa   się   poprzez   standardowe   argv.   Porównanie   natomiast   jest   wykonywane   przy   pomocy
funkcji  porównującej stringi.  Warto zwrócić uwagę na zastosowanie qWarning. Za pomocą tej
funkcji wyświetlamy tekst na konsoli. Oprócz tej funkcji w Qt mamy jeszcze dwie wykonujące
podobne operacje. Są to qDebug, która nie różni się niczym od qWarning i prawie niczym od
qFatal, która po wywołaniu od razu zamyka program. 

Gdybyśmy   chcieli   teraz   wykorzystać   parametr   wywołania   programu   w   jakimś   pliku

formatki możemy to zrobić w następujący sposób:

#include <qapplication.h>

void Form1::init()
{
    if (qstrcmp(qApp­>argv()[2], "­­kill") == 0 )
        qFatal( "Koniec zabawy... \n" );
}

Aby przetestować działanie tego programu należy uruchomić go z konsoli wpisając jego

nazwę poprzedzoną "./". 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Programik wieloformatkowy

Treść tego rozdziału nie jest zbyt skomplikowana, omówione tutaj sposoby wykorzystania

Qt pozwolą na wykorzystywanie w programach kilku form/dialogów. Przedstawione tutaj zostaną
dwa sposoby podejścia do tematu. Pierwszy to tworzenie nowych form przygotowanych wcześniej
w QtDesingerze. Drugi sposób to tworzenie formatek całkowicie w kodzie programu. Ten drugi
sposób z pewnością będzie później przydatny przy innych komponentach takich jak np. buttony. Na
koniec zaprezentuję sposób na przekazywanie danych, zmiennych i funkcji pomiędzy formatkami.

Zaczynamy.   Jak   zwykle   nowy   projekt   np.   o   nazwie   "2form".   Po   zapisaniu   projektu

tworzymy dwie formatki jedną wyposażamy w dwa SpinBoxy i dwa buttony. Drugą pozostawiamy
pustą, z tym że tworzymy jej pusty plik z kodem, którym zajmiemy sie później. Po utworzeniu
formaki form1, tworzymy slota np. o nazwie frm2show_s(). Slota tego podpinamy do pierwszego
buttona. Do slota wklejamy następujący kod:

#include "form2.h"

void Form1::frm2show_s()
{
    Form2 frm2;
    frm2.exec();
}

     Jak widać nic skomplikowanego. Tworzone w ten sposób okienko jest okienkiem modalnym co
znaczy, że blokuje wykonywanie programu i dostęp do formatki głównej. 

Teraz pokażę sposób na tworzenie okienka w sposób dynamiczny. Sposób ten polega na

tym, że okienko jest tworzone w trakcie działania programu po wywołaniu funkcji tworzącej. Takie
podejście pozwala na stworzenie formy na podstawie parametrów, które mogą być różne w różnych
momentach działania programu. Innymi słowy dynamiczne tworzenie obiektów pozwala na dodanie
funkcji do programu, która nie będzie musiała być do niego wkompilowana. W ten sposób działa
np. Quanta gdzie użytkownik ma możliwość dodania własnego przycisku np. wstawiającego jakiś
kod lub wywołującego funkcję do wyboru koloru.

Aby to wszystko uczynić możliwym musimy jak zwykle utworzyć slota frm3show_d() i

podpiąć go do buttona "show dynamic". Funkcja frm3show_d() powinna mieć mniej więcej taki
kod:

void Form1::frm3show_d()
{
  QDialog *frm3 = new QDialog ( this, "",  TRUE, 0);
  frm3­>resize(sbWidth­>value(), sbHeight­>value());

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

  frm3­>exec();
  delete frm3;
}

Dwa fragmenty tego kodu z pewnością wymagają wytłumaczenia: 
          QDialog *frm3 = new QDialog ( this, "", TRUE, 0 );
to konstruktor klasy QDialog o nazwie *frm. Parametry podane przy wywołaniu konstruktora to po
kolei: rodzic (parent), nazwa, czy okienko ma być modalne, oraz typ formatki. Ponieważ dialog jest
formatką raczej uproszczoną może istnieć tylko jako okienko modalne. Jako typ formatki mamy do
wyboru:   WStyle_Customize   |   WStyle_NormalBorder   |   WStyle_Title   |   WStyle_SysMenu.
Testowanie poszczególnych opcji pozostawiam czytelnikowi. Słówko this podane jako parametr
rodzica odwołuje się do programu czyli klasy QApplication. 

Funkcja resize jest chyba zrozumiała, jedyne co należy wiedzieć to to, że jest ona funkcją

klasy QWidget, która jest przodkiem klasy QDialog. Szczegółowe omówienie dziedziczenia w Qt
będzie omówione w innym rozdziale. Wskazuję jedynie na istnienie tegoż. 

Kolejnym bardzo ważnym elementem tej funkcji jest linia:

          delete frm3;
 niszczy ona cały obiekt, po zamknięciu okna. Oszczędza to pamięć. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Singleton pattern

Nie wiem czy określenie zawarte w tytule ma swój polski odpowiednik, jakkolwiek jest to

jeden   z   ważniejszych   elementów   programowania   w   języku   C++   po   linuxem.   Na   czym   rzecz
polega ?? Singleton Pattern jest to pewien sposób na stosowanie zmiennych globalnych, czyli takich
widocznych ­ do odczytu i zapisu w całym programie, w każdym jego miejscu. Używanie klas Qt
dość znacznie  ogranicza  możliwości  używania klasycznych  zmiennych  globalnych.  W  dodatku
mechanizmy   działania   Qt   Designera   nie   dają   nam   zbyt   wielkiego   pola   do   manewrów   w   tym
zakresie. Dlatego też będziemy używać czegoś co po amerykańsku nazywane jest Singleton Pattern.

Określenie pochodzi z książki "Effective C++" Scotta Meyersa. W internecie można znaleźć

setki przykładów użycia tego czegoś, nie ma jednak zbyt wiele na temat użycia Singletona w Qt.
Dlatego też w poniższej części opiszę do bólu prosty przykład. 

Zanim to nastąpi przydało by się małe wprowadzenie do tego całego Singletonu. Co to w

ogóle jest i takie tam ?? Więc singleton to klasa, której podstawową cechą jest posiadanie tylko
jednej instancji. W ten sposób jeśli raz zapiszemy tam jakąś wartość będziemy ją mogli zawsze
odczytać.   Tak   więc   pojedynczość   tej   klasy   sprawia,   że   jest   ona   idealnie   dostosowana   do
przechowywania różnych wartości i zmiennych. W bardziej rozbudowanych programach za pomocą
singletonu można zapisywać i odczytywać konfigurację programu.

     Wytężamy mózgownice i startujemy:
 1.Utwórz projekt o nazwie 2form2.pro
 2.Utwórz dwie formatki QDialog o nazwie form1 i form2
 3.Utwórz plik main.cpp według opisywanego wielokrotnie wzoru.
 4.Utwórz nowy plik nagłówkowy o nazwie zmienne.h
 5.Utwórz nowy plik źródłowy o nazwie zmienne.cpp

Zaczniemy od pliku zmienne.h ­ co powinien on zawierać ?? Z pewnością definicję klasy

oraz   spis   zmiennych   jakie   będziemy   użytkowali.   W   naszym   przykładowym   programie
wykorzystamy tylko jedną zmienna, żeby sposób użytkowania singletonów był bardziej widoczny.
Zmienna będzie typu QString więc musimy dodać plik nagłówkowy: qstring.h. Całość kodu tego
pliku będzie wyglądała tak:

#include <qstring.h>

class Singleton {
// definicja klasy o nazwie Singleton ­ jest 
// to nazwa wlasna :)
  public:
    QString ciapek;     
    // definicja zminnej QString o nazwie ciapek

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

  private:
    Singleton();
    Singleton(const Singleton&);
    // konstruktor klasy 

    friend Singleton& Zmienne();
};

Singleton& Zmienne();

Słówko friend może się wydać dosyć dziwne. Oznacza ono, ze funkcja Zmienne() będzie

miała dostęp do części prywatnej (private) klasy Singleton. 

Do dostępu do zmiennych będziemy używać funkcji Zmienne().

    Plik zmienne.cpp:

#include "zmienne.h"

Singleton::Singleton() {
// konstruktor klasy ­ nie używany :))
};

Singleton& Zmienne() {
// funkcja glowna singletonu
    static Singleton zmienne;
    return zmienne;
};

I na tym praktycznie kończy się cała sztuczka ­ niesamowicie sprytna. Pokażę jeszcze tylko

z   czym   to   się   wszystko   je.   Aby   to   się   dało   jeść   będziemy   potrzebowali   kilku   komponentów
wizualnych, paru slotów i kilku sygnałów wg. poniższej specyfikacji:

Formatka główna form1: 

 ­ trzy przyciski (QPushButton) o nazwach pb1, pb2 i pb3
 ­ jedno pole edycyjne (QLineEdit) o nazwie leCiapek :)
 ­ no i jedna labelka (QLabel), która nam powie co znajduje się w leCiapku :)))

Formaka druga form2:

 ­ dwie labelki (QLabel) o nazwach: tlCiapek i tlOpisLe
 ­ jeden przycisk pb1
 ­ i jedno pole edycyjne (QLineEdit) o nazwie leCiapek 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Koncepcja działania programiku polega na wielokrotnym zapisywaniu różnych wartości do

zmiennej   ciapek.   Wartości   te   będziemy   pobierać   z   pól   edycyjnych   dwóch   różnych   formatek.
Wybrałem ten sposób na opisanie tematu, ponieważ przy dwóch formatkach mamy do czynienia z
dwoma   różnymi   klasami.   W   ten   sposób   można   będzie   dość   łatwo   zauważyć,   że   zmienna   z
singletonu jest ponad klasami. 

     form1.ui.h

#include "form2.h"
#include "zmienne.h"

void form1::init()
{
    Zmienne().ciapek = "1st Step = progz run";
    // w ten sposób zapisujemy wartości do zmiennej
    // ciapek singletonu
    leCiapek­>setText( Zmienne().ciapek );
    // w ten sposób odczytujemy wartości ze zmiennej
    // ciapek singletonu
}

void form1::load_form2()
{
    form2 f2;
    f2.exec();
}

void form1::ciapek_save()
{
    if ( leCiapek­>text() != "" ) {
        Zmienne().ciapek = leCiapek­>text();
    } else {
        Zmienne().ciapek = "Ciapek byl pusty";
    }
}

void form1::ciapek_refresh()
{
    leCiapek­>setText( Zmienne().ciapek );
}

     form2.ui.h

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

#include "zmienne.h"

void form2::init()
{
    tlCiapek­>setText( "Zmienna globalna ciapek :) wyglada tak: " + Zmienne().ciapek );
}

void form2::ciapek_save()
{
    if ( leCiapek­>text() != "" ) {
        Zmienne().ciapek = leCiapek­>text();
    } else {
        Zmienne().ciapek = "Ciapek byl pusty";
    }
    tlCiapek­>setText( "Zmienna ciapek :) wyglada tak: " + Zmienne().ciapek );
    close();
}

Jak widać w ten sam sposób możemy się bawić ciapkiem w zupełnie innej klasie. 

PS:
 Instrukcja obsługi do programu:

1. W polu edycyjnym wpisz dowolny tekst, np. "My name is tom", wciśnij  przycisk zapisz a

następnie nowe okienko

2.  Jak widać treść ciapka została zapisana i zaprezentowana na labelce możesz teraz zmienić jej

treść wpisując w biale pole i zapisując :))) 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Menu, popupmenu

Jak do tej pory tworzone przez nas aplikacje miały raczej charakter małych programików lub

raczej   części   składowych   jakiegoś   większego   programu.   Wszystkie   okienka   i   formatki   były
budowane przy użyciu klasy QDialog co ma pewne ograniczenia: nie pozwala na tworzenie menu.
Dlatego też teraz do stworzenia okienka wykorzystamy klasę QMainWindow.

Po uruchomieniu Qt Designera i stworzeniu projektu np. o nazwie menus.pro wybieramy

"File­>New", a następnie Main Window. Spowoduje to uruchomienie dość dziwnego kreatora,
który automatycznie utworzy  nam edytor tekstu. W omawianym przykładzie wykonamy Menu
własnoręcznie dlatego też proponuję zamknąć kreatora za pomocą przycisku Cancel. Następnie
zapisujemy wszystko.

Teraz trochę klikologii:

Klikamy prawym przyciskiem myszy na formie i wybieramy Add Menu Item:

Zmienimy teraz nazwę pierwszego menu, nadamy mu niezwykle oryginalną nazwę plik.

Aby   tego   dokonać   należy   dwukrotnie   kliknąć   na   napis   "Menu"   i   zacząć  pisać.   Zatwierdzamy
enterem.   Po   zatwierdzeniu   menu   powinno   już   działać   ­   przynajmniej   w   fazie   projektowania.
Możemy teraz dwukrotnie kilknąć na napis new item i wpisać np. zakończ. Będzie to pierwsza
opcja jaką damy użytkownikowi programu. Dodanie nowego pola menu zatwierdzamy enterem.

Dodanie akcji

Jak zapewne niektórzy z was zauważyli po utworzeniu okna QMainWidget pojawiło się nam

nowe okienko ActionEditor. Każdy element końcowy naszego menu będzie tu widoczny jako akcja.
(Dobrym pomysłem będzie w tym miejscu zmiana nazw, akcji na pozbawione polskich krzaczków.)
Za   pomocą   znanego   już   nam   przycisku   (tu   czerwona   strzałka),   który   wywoła   okienko   z
połączeniami   (connections)   dodamy   do   naszego   menusa   akcję.   Proponuję   na   sam   początek
przypisać   funkcję,   która   bez   żadnego   "ale"   zamknie   nasz   program.   W   okienku   połączeń
wybierzemy sygnał activated() i slota close().

Dodajemy plik main.cpp i możemy kompilować (qmake ­> make).

Za pomocą opisanych powyżej metod i kodu z lekcji 5 możemy w prosty sposób utworzyc

edytor tekstu. W tym celu upuszczamy na formę pole textEdit (i zmienimy nazwe na teEdytor) oraz
dodamy do menu Plik opcje otwórz, zapisz i np. nowy. Utworzymy również menu edycja i tu
wstawimy: wytnij, kopiuj, wklej i wstaw date. Aby program wygladał jakoś po ludzku dodamy
również menu pomoc z opcją o programie. Jeśli wszystkie akcje w edytorze akcji mają poprawne
(bez polskich znaków) nazwy to przechodzimy dalej.

Po   pierwsze  dodamy   pliki   nagłówkowe:   qfiledialog.h,   qmessagebox.h   oraz   qdatetime.h.

Następnie przejdziemy do pisania kodu. Pierwszą akcją jaką mamy w menu plik powinien być

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

nowy. Normalnie funkcja ta tworzy nowy projekt/plik w pamięci, pod warunkiem, że zawartość
pola edycyjnego nie zmieniła się. Tak więc musimy to sprawdzić:

void Form2::nowy()
{
    if ( teEdytor­>isModified() &&
        (QMessageBox::question( this,  "Informacja",
                       "Plik zostal zmieniony. Czy chcesz zapisac ??",
                       "&Tak", "&Nie",
                       QString::null, 0, 1 ))  == 1     
        )
    { 
        teEdytor­>setText( "" );
        teEdytor­>setModified(FALSE);
    } 
}

Akcje (sygnały) otwórz i zapisz proponuję skopiować z lekcji czwartej, aby się za dużo nie

mędzyć.   Dobrym   pomysłem   byłoby   dodanie   sprawdzania   czy   zawartość   pola   edycyjnego   się
zmieniła od czasu zapisania, otworzenia lub utworzenia nowego pliku. Ma to olbrzymie znaczenie
gdyż jeśli tego nie zrobimy nasz biedny użytkownik może przez przypadek skasować sobie cały
dzień pracy. Nie będę tutaj opisywał tego z punktu widzenia kodu. Napiszę tylko krótki algorytm
działania naszego programu. 

1. Użytkownik otwiera (lub tworzy nowy) plik. 
2. W polu edycyjnym dokonuje jakichś zmian. 
3. Program pyta się czy zapisać zmiany zanim wykona cokolwiek. 
4. Jeśli user odpowie, że chce to program zapisuje zawartośc pola i wykona wskazaną operację. 
5. Jeśli user odpowie, że nie chce to program skasuje zmiany wykonując wybraną operację. 
6. Po wszystkim ustawimy wskaźnik modyfikacji pola na FALSE 

Zabieramy się za menu edycja. Na początek najprostsze: dzisiajsza data: 

void Form2::date()
{
     QDate date = QDate::currentDate();
     QString aDzis = date.toString ( Qt::LocalDate );
     teEdytor­>append( aDzis );
}

oczywiście, żeby kod zadziałał musimy tego slota podczepić do sygnału kliked pozycji wstaw datę
w menu. Dodanie obsługi copy/paste jest jeszcze prostsze ale polega na wykonaniu czegoś czego

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





jeszcze   nie   robiliśmy.   Chodzi   o   to,   że   do   tej   pory   odbiorcą   wszystkich   sygnałów   w   naszych
programikach była formartka, tym razem odbiorcą będzie edytor. Okienko ze slotami powinno więc
wyglądać mniej więcej tak: 

Na koniec jeszcze dodamy obsługę about. Wstawimy tam, krótkiego messageboxa o autorze

programu: 

void Form2::about()
{
QMessageBox::information( this,  "Informacja",
                  "Autorem programu jestem ja GREAT LINUX PROGRAMMER :)",
                  "&OK",
                  QString::null, 0, 1 );
}

i nasz programik jest gotowy. Dodamy jeszcze mały bajer ­ polskie znaki :)). Zasada wyświetlania
polskich   znaków   diakrytycznych   polega   na   wskazaniu   kodeka   jaki   ma   zostać   użyty   przy
konwertowaniu tablicy znaków *char do QString. Wykonujemy to w funkcji init():

#include <qtextcodec.h> 

void frm1::init()
{
  QTextCodec::setCodecForCStrings( QTextCodec::codecForName("ISO8859­2") );   
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Toolbar

Początki tworzenia toolbara są takie same jak przy menu. W tym przykładzie posłużymy sie

programem z lekcji 10. Po otwarciu projektu w Qt Designerze drugi guzik na formie i Add Toolbar.

Do   wykonania  toolbara   potrzebne   będą   nam   obrazki   na   ikonki.   Proponuje  w   tym   celu

sięgnąc je stąd (images.tar.gz), żeby nie kombinować. Po sciągnięciu należy pliki rozpakować do
katalogu z projekt, tak aby w tym katalogu istniał podkatalog o nazwie .images. 

Kolejną czynnością jaką musimy wykonać jest dodanie obrazków do zasobów programu.

Aby  to  uczynić  podświetlamy  dowolną  akcję  w  edytorze  akcji,   najlepiej  plikNowyAction  i  w
edytorze właściwości tej akcji wybieramy opcję iconSet. Klikamy [...] trzy kropki, następnie add.
Wchodzimy do katalogu .images (lub tam gdzie mamy obrazki do akcji toolbara) i zaznaczmy te,
które   chcemy   dodać.   Po   dodaniu   wskazujemy   ikonkę,   która   ma   być   przypisana   do   akcji
plikNowyAction. Postępujemy w ten sposób tak długo, aż wszystkie akcje, które chcemy umieścić
na   toolbarze   będą   miały   swoją   ikonkę.   Gdy   wszystkie   akcje   będą   miały   ikonki   wystarczy
przeciągnąć je na toolbar metodą drag&&drop.

Nasz notatnik już jest najpiękniejszy na świecie ­ prawda ?? Dodamy mu jeszcze dwie opcje:

cofnij i powtórz. W tym celu w Action Edytorze tworzymy dwie nowe akcje inneCofnijAction oraz
innePowtorzAction. Przypisujemy do nich odpowiednie ikonki, a następnie w oknie dialogowym
connection przypisujemy do nich akcje cofnij ­ undo, powtórz ­ redo. W sposób analogiczny do
tego jaki tworzyliśmy funkcje kopiuj/wklej to jest wskazując jako odbiorcę sygnału pole tekstowe
teEdytor. 

Dobrym pomysłem była by jeszcze zmiana właściwości text tych aktionsów na takie, które

będą reprezentatywne do pełninoych przez nie funkcji. Mówiąc po polsku cofnij to cofnij. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





QTimer

Jedna   z   ciekawszych   i   przyjemniejszych   klas   ()   w   programowaniu   systemów

wielowątkowych.   Użycie   tej   klasy   daje   programowi   możliwość   wykonywania   paru   czynności
niemalże jednocześnie tzn. wykonując jedną czynność program nie musi czekać na jej zakończenie i
może działać dalej. Do omówienia tego tematu posłużymy się prostym przykładem programu, który
aktualną godzinę. Użyjemy w tym celu: formatki (QDialog) o nazwie frmTime, dwóch przycisków
(QPushButton) pbStart i pbStop oraz jednej labelki tekstowej (QTextLabel) tlTime. Użyjemy także
klasy QTimer co zostanie omówione na przykładzie kodu. Całość powinna wyglądać tak:

Sloty. Tworzymy trzy sloty: starttimer(), stoptimer() i timerDo(). Dwie pierwsze łączymy z

analogicznymi przyciskami. Zapisujemy wszystko. Kod źródłowy tego programu:

#include <qdatetime.h>
#include <qtimer.h>

QTimer *timer;
// deklarowanie zmiennych: można tutaj jak i w
// Class Variabless w sekcji public
// w tym miejscu jest bardziej czytelne

void frmTime::init()
{
    timer = new QTimer( this );    
    // inicjujemy zmienną timer, rodzicem będzie aplikacja
    connect( timer, SIGNAL(timeout()), this, SLOT(timerDo()) );
    // kodowe stworzenie połączenia między sygnałem, a funkcją
    // w przypadku timera oznacza to, że za każdym razem jak timer osiągnie
    // timeout czyli zakonczy wywolanie funkcji timerdo() funkcja ta zostanie
    // wywolana.
}
    
void frmTime::timerDo()
{
     QTime time = QTime::currentTime();
     QString aTime = time.toString ( Qt::LocalDate );
     tlTime­>setText( aTime );

     timer­>start( 1000, TRUE ); 
     // powyższa linijka powoduje zapętlenie funkcji timera czyli
     // timeout powoduje wywolanie timerdo
}

void frmTime::starttimer()

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

{
     timer­>start( 1000, TRUE ); 
     // uruchomienie timera
}

void frmTime::stoptimer()
{
     timer­>stop();
     // zatrzymanie timera
}

Przygotowując tą lekcję natknąłem się na pewien problem jaki pewnie każdy będzie miał

przy tworzeniu oprogramowania z użyciem Qt Designera. Chcąc stworzyć jakąś zmienną, która ma
być   widoczna   w   całej   klasie   formaki   naturalnym   odruchem   jest   zainicjowanie   jej   w   funkcji
konstruktora klasy. W przypadku powyższego programu funkcja konstruktora wygląda tak:

frmTime::frmTime( QWidget* parent, const char* name, bool modal, WFlags fl )
    : QDialog( parent, name, modal, fl )
{
    if ( !name )
        setName( "frmTime" );

    pbStop = new QPushButton( this, "pbStop" );
    pbStop­>setGeometry( QRect( 160, 60, 101, 32 ) );

    pbStart = new QPushButton( this, "pbStart" );
    pbStart­>setGeometry( QRect( 30, 60, 101, 32 ) );

    tlTime = new QLabel( this, "tlTime" );
    tlTime­>setGeometry( QRect( 20, 20, 249, 20 ) );
    languageChange();
    resize( QSize(287, 120).expandedTo(minimumSizeHint()) );
    clearWState( WState_Polished );

    // signals and slots connections
    connect( pbStart, SIGNAL( clicked() ), this, SLOT( starttimer() ) );
    connect( pbStop, SIGNAL( clicked() ), this, SLOT( stoptimer() ) );

    // tab order
    setTabOrder( pbStart, pbStop );
    init();
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Funkcja   ta   znajduje   się   w   pliku   form2.cpp   w   katalogu   .ui.   Zaglądając   do   tego   pliku

napotkamy   na   samej   górze   na   napis   ostrzegający,   że   wszystkie   zmiany   w   tym   pliku   zostaną
utracone. Jak więc tworzyć obiekty widziane w całej klasie ?? Znalazłem dwa sposoby: pierwszy
został opisany w rozdziale pt: wymiana danych, drugi natomiast został przedstawiony tutaj. W obu
tych przypadkach wykorzystujemy funkcję init(), która ma za zadanie zastąpić funkcję konstruktora.
Jeśli natomiast chodzi o destruktora, to istnieje funkcja destroy(), która jest jego zamienimikiem. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Grafika od innej strony QPainter

Może się czasami zdarzyć, że będziemy chcieli coś namalować. Namalować linię, wykres,

kółko, obrazek lub może cos bardziej ambitnego np. symulację lotu pocisku. Wszystkie te rzeczy
będziemy mogli wykonać na pomocą bardzo rozbudowanej klasy QPainter. W tym tekście opiszę
jak z niej korzystać.

Na   początek,   jak   zwykle   proponuję   uruchomić   Qt   Designera   i   wykonać   wszystkie

standardowe kroki mające na celu utworzenie projektu z formatką klasy QWidget. Do obsłużenia
wszystkich funkcji slotów jakie przewidziałem w tej lekcji potrzebnych nam będzie 7 przycisków.
Jakkolwiek   można   umieszczać   je   na   formie   w   momentach   gdy   będą   omawiane   poszczególne
elementy klasy QPainter.

DrawEllipse

Teraz będzie kawałek, całkiem  przyjemnego kodu. Zaprezentuję również użycie funkcji

random, z małym tips trikiem. Cały ten tips polega na tym iż domyślnie funkcja random, służąca do
generowania   liczb   pseudolosowych   losuje   liczby   z   zakresu   0..RAND_MAX   (co
najprawdopodobniej oznacza INT_MAX). Wygląda to tak: 

int getRandom(int max)
{
  return rand() / (RAND_MAX / max + 1);
}

Co to znaczy i jak działa ?? To co jest widoczne to to, że w parametrze wywołania tej

funkcji podajemy maksymalną liczbę, która uczestniczy w losowaniu. Wynikiem tej funkcji zawsze
będzie liczba mniejsza lub równa liczbie max. Aby móc korzystać z tej funkcji należy zainkludować
plik   stdlib.h.   Gdy   uda   nam   się   zaimplementować   funkcję   do   generowania   losowych   liczb.
Będziemy mogli nacieszyć wzrok takim oto kodem: 

void Form1::rysuj()
{
    int x, y;
    QPainter paint( this );
    
    for (int i = 0;i<=10000; i++ )
    {
        x = getRandom( width() );       
        y = getRandom( height() );
        paint.setPen( green );
        paint.setPen( SolidPattern );
        paint.setBrush( green );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





        paint.drawEllipse ( x, y, 20, 20 );
    }
}

Działanie tej funkcji są myślę dość proste do zrozumienia. Druga linia QPainter paint(this)

służy do stworzenia zmiennej typu klasy QPainter. Funkcja konstruktora tej klasy wymaga podania
w swoim parametrze nazwę klasy jakiegoś komponentu wizualnego. W tym przypadku będziemy
rysowali na formie dlatego podałem this co w tym przypadku oznacza Form1. Kolejne linie to
pętelka, zapodanie liczb losowych do zmiennych x i y, przy czym jako wartość maksymalna podana
jest szerokość i wysokość okna. Funkcje setPen, setBrush i drawEllipse są raczej zrozumiałe. Jeśli
funkcję   tą   podepniesz   to   sygnału   pushButton   clicked   to   na   ekranie   ujrzysz   przepiękny   pokaz
rozmnażania się pierwotniaków. 

Gradient i skala RGB

Fajnie   jest   machnąć   czasem   gradient,   program   z   gradientem   wygląda   bardziej

profesjonalnie. Widać, że autor się zna na programowaniu. Zrobimy więc teraz parę gradientów: 

void Form1::drawrgb()
{
        QPainter paint( this);
        for (int i=0;i<=255;i++)
        {
            paint.setPen( QColor (0, 0, i ) );
            paint.drawRect ( 10+i, 10, 10, 10 );
            
            paint.setPen( QColor (0, i, 0 ) );
            paint.drawRect ( 10+i, 20, 10, 10 );
            
            paint.setPen( QColor (i, 0, 0 ) );
            paint.drawRect ( 10+i, 30, 10, 10 );
        }
}

Ta sama prosta zasada, która pojawiła się przy elipsie. Tym razem rysujemy kilkadziesiąt

prostokątów,  z  których  każdy  ma   o 1  stopień  jaśniejszy  kolor.   W  ten  sposób   osiągamy  efekt
gradientu. Co jeszcze można wymodzić ?? Gradient na komponencie: 

void Form1::rgbnap()
{
        QPainter paint( pushButton1 );
        for (int i=0;i<=255;i++)
        {
            paint.setPen( QColor (0, 0, i ) );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

            paint.drawRect ( i, 4, 10, 4 );
            
            paint.setPen( QColor (0, i, 0 ) );
            paint.drawRect ( i, 8, 10, 4 );
            
            paint.setPen( QColor (i, 0, 0 ) );
            paint.drawRect ( i, 12, 10, 4 );
        }
}

Prawie   nie   różni   się   to   od   poprzedniego   kodu.   Tym   razem   jednak   trzy   paski   zostaną

namalowane na przycisku.

Szkoła się kłania

Jeśli kiedyś, któraś szkoła lub uczelnia zapragnie nauczać programowania w Qt to na pewno

będzie tam takie zadania: zrób za pomocą Qt zegarek analogowy. Nic prostszego. Użyjemy do tego
celu programu zaprezentowanego w lekcji QTimer. Zamiast jednak wyświetlania czasu w postaci
tekstowej wykorzystamy czas. W moim wykonaniu funkcja timerDo wygląda tak:

void Form1::timerDo()
{
     repaint();
     QTime time = QTime::currentTime();
     int s = time.second();
     int m = time.minute();
     int h = time.hour();
     
      QPainter paint( this );
      paint.setPen( QColor (0, 0, 0 ) );
      int x2 = (width() / 2) + lround (240 * sin(s*2*M_PI/60));
      int y2 = (height() / 2) + lround (240 * ­ cos(s*2*M_PI/60));
      paint.drawLine ( (width() / 2), (height() / 2), x2, y2 );

      paint.setPen( QColor (0, 0, 255 ) );
      x2 = (width() / 2) + lround (200 * sin(m*2*M_PI/60));
      y2 = (height() / 2) + lround (200 * ­ cos(m*2*M_PI/60));
      paint.drawLine ( (width() / 2), (height() / 2), x2, y2 );
           
      paint.setPen( QColor (60, 255, 60 ) );
      x2 = (width() / 2) + lround (160 * sin((h+m/60)*2*M_PI/12));
      y2 = (height() / 2) + lround (160 * ­ cos((h+m/60)*2*M_PI/12));
      paint.drawLine ( (width() / 2), (height() / 2), x2, y2 );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





      
      
      timer­>start( 1000, TRUE ); 
}

I co ?? Ładnie ??

Lecimy dalej. Grafikujemy pliki. 

Za pomocą tej klasy można również wyświetlać pliki. Zabawy jest z tym co nie miara,

ponieważ   mamy   dość   dużą   kontrolę   nad   tym   co   i   jak   jest   wyświetlane.   Dla   przykładu
przygotowałem dwie funkcje:

void Form1::drawPix()
{
    QPainter paint( this );        
    QImage *img = new QImage("/home/moux/natalia.jpg");
    paint.drawImage(0, 0, *img, 0, 0, ­1, ­1, OrderedAlphaDither );    
}

void Form1::drawpixbig()
{
    QPainter paint( this );        
    QImage *img = new QImage("/home/moux/natalia.jpg");
    paint.drawImage(QRect(0, 0, width(), height()), *img);    
}

Obie te funkcje wyświetlają zdjęcie z pliku dyskowego. Pierwsza pokazuje to zdjęcie w

oryginalnych rozmiarach, druga dostosowuje je do rozmiarów okna. Oczywiście nie bawiłem się
tutaj w sprawdzanie czy plik istnieje i tego typu sprawy, które normalnie w programie należało by
uczynić. 

PS. Rysować linie i kreski możemy w Qt na dwa sposoby ten tu omówiony i za pomocą klasy
QCanvas. Ta druga klasa jest bardziej złożona i zostanie omówiona w dziale Technik bardziej
zaawansowanych.

 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Mysz i klawiatura

W   tej   lekcji   pobawimy   się   myszką   i   klawiaturą.   Zaprezentuję   trzy   klasy   QKeyEvent,

QMouseEvent i QCursor. Programik, który wykonamy w tej lekcji nic specjalnego nie robi no czym
za chwilę się przekonacie.

Rozpoczynamy programowanie jak zwykle od stworzenia projektu, np. qline.pro i zapisania

go   w   osobnym   katalogu.   Następnie   dodajemy   do   programu   dialog   (QDialog)   i   dwie   labelki
tekstowe.

QCursor 

Klasę   tą   wykorzystamy   do   określenia   pozycji   kursora   względem   pulpitu.   Pomiaru

dokonamy   w  funkcji  timerDo()  timera.  Co  zostało  już   omówione  w  lekcji   poświęconej klasie
QTimer. Funkcja ta powinna wyglądać:

void Form1::timerDo()
{
  textLabel2­>setText(  QString( "%1 , %2 " ).
        arg(QCursor::pos().x()).
        arg(QCursor::pos().y()) );
  timer­>start( 100, TRUE ); 
}

Prezentowany kod może wyglądać trochę szokująco, ale postaram się żeby tak nie było.

QString(%1)::arg(x) jest to funkcja konwertująca. Działa ona na podobnych zasadach do funkcji
printf znanej z języka C++. Dane podane w parametrze funkcji arg mogą mieć różne typy liczbowe
bądź   znakowe,   zwracana   wartość   zawsze   będzie   QString.   QCursor::pos().x()   ­   tutaj   mamy   do
czynienia z pięknym kawałkiem kodu. Na czym polega jego piękno ?? Analizując ten kod krok po
kroku widzimy, że wartość zwracana przez funkcję pos() będącej funkcją klasy QCursor jest typu
klasowego QPoint. We wskazanym fragmencie programu interesuje nas tylko jedna część klasy
QPoint (klasa ta składa się z dwóch zmiennych x i y) tj. x. Dlatego też wykorzystujemy funkcję x()
do wyodrębnienia tej zmiennej. 

Drugim zastosowaniem klasy QCursor jest zmiana kursora myszy. Wykonanie tego wygląda

następująco:
      setCursor( QCursor::CrossCursor ); 
 Zmiany tej dokonamy w dalszej części programu, kiedy będzie to uzasadnione. 

QMouseEvent

Klasa  QMouseEvent  posiada dwie niesamowicie interesujące funkcje:  mouseMoveEvent

(   QMouseEvent   *e   )   i   mousePressEvent(   QMouseEvent   *e   ).   FUNKCJE   TE   SĄ

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





PREDEFINIOWANE, podobnie jak init() i destroy(). Istnienie tych funkcji w kodzie programu
sprawia,   że   instrukcje   umieszczone   w   tych   funkcjach   będą   wykonywane   zgodnie   z   ich
przeznaczeniem. Niesie to ze sobą pewne niebezpieczeństwo. Literówka w nazwie takiej funkcji,
nie   będzie   wykryta   przez   kompilator   co   może   powodować   problemy   z   odnalezieniem   błędu.
Jakkolwiek zostaliście ostrzeżeni. Przykład zastosowania tych funkcji:

 
#include <qpainter.h>

QPoint startpoint;                         
QColor color;

void Form1::init()
{
    color = black;
}

void Form1::mousePressEvent( QMouseEvent *e )
{
    setMouseTracking(TRUE);  // przechwytywanie myszy bez wcisnietych klawiszy.   
    setCursor( QCursor::CrossCursor );  // no i zmienimy cursor na inny
    
    startpoint.setX( e­>x() );
    startpoint.setY( e­>y() );
    erase(); // nowe klikniecie = nowy rysunek
}

void Form1::mouseMoveEvent( QMouseEvent *e ) 
{
    textLabel1­>setText(  QString( "%1 , %2 " ).arg(e­>x()).arg(e­>y())   );
    QPainter paint( this );
    paint.setPen(color);
    paint.drawLine ( startpoint.x(), startpoint.y(), e­>x(), e­>y() );
}

Funkcja setMouseTracking pozwala nam na rysowanie po painterze formy bez konieczności

wciśnięcia klawiszy co jest ustawione w Qt domyślnie. 

QKeyEvent

Hmmm, nie wiem czy jest sens tłumaczyć: 

void Form1::keyPressEvent( QKeyEvent *k )

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

{
    switch ( tolower(k­>ascii()) ) {
        case 'r':                               
            color = red;
            break;
        case 'g':                               
            color = green;
            break;
        case 'b':                              
            color = blue;
            break;
        default:                              
            color = black;
            break;
    }
}

Jak widać obsługa myszy i klawiatury jest w Qt banalnie prosta. Samo Qt robi wszystko za

nas. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





QCanvas, a programowanie gier

Ten tytuł nie jest do końca adekwatny do zawartości tego tekstu. W poniższym przedstawię,

lapidarnie i dość skrótowo sposób w jaki posługujemy sie klasą QCanvas. Chciałbym przy okazji
napisać   również   dlaczego   tą   klasą   się   posługujemy   ??   Klasa   ta   ma   przede   wszystkim   jedną
niezastąpioną zaletę, nie jest odświeżana za każdym razem gdy jest przerysowywane okno (jak to
miało miejsce przy QPainter). Do tego dochodzi jeszcze jeden ważny czynnik, klasa tam ma takie
funkcje   i   takie  klasy   pokrewne,  które   sprawiają,   że  po   tej   klasie   możemy:   wędrować   myszką
(obsługa zdarzeń myszy), wędrować pikselami i sprawdzać czy piksel jest czy go niema (w zasadzie
to możemy nawet sprawdzić czy w danym konkretnym miejscu na canvasie jest nasz wyrysowany
obiekt czy też nie). 

W naszym programie wykonamy najprostszą czynność z wykorzystaniem klasy QCanvas.

Posłużymy się w tym celu Qt Designerem, więc chyba nie musze już tłumaczyć co trzeba zrobić
aby projekt doszedł do skutku. Powiem tylko, że w moim przykładzie wykorzystujemy klasę okna
QMainWindow. 

Przy wykorzystaniu klasy QCanvas trzeba wiedzieć jedną rzecz. Klasa ta sama w sobie jest

niewidoczna, tak więc tworzenie na niej malunków będzie bezskuteczne dopóki nie skorzystamy z
innej klasy jaką jest QCanvasView. Wszystko będzie dobrze zobrazowane na poniższym kodzie,
jakkolwiek tą jedną rzecz należy zapamiętać żeby oszczędzić sobie impotenckich frustracji.

"Tako rzecze Zaratustra":
 
#include <qcanvas.h>

void Form1::init()
{
    QCanvas *canvas = new QCanvas( this );
    canvas­>setBackgroundColor( Qt::black );
    canvas­>resize( width() ­ 30, height() ­ 30 ); 
    // dostosowujemy do romiaru formatki
    
    QCanvasView *cView = new QCanvasView( canvas, this );
    setCentralWidget( cView ); // funkcja jest tylko w QMainWindow
    cView­>show();
    
    QCanvasRectangle *kwadrat = new QCanvasRectangle( 20, 20, 20, 20, canvas );
    kwadrat­>setPen( QPen(green, 2) );
    kwadrat­>show();    
    
    QCanvasEllipse *elipsa = new QCanvasEllipse( 100, 100, 0, 4320, canvas );
    // 4320 to kat zamkniecia kola = kat prawdziwy(270) * 16
    elipsa­>setX( 100 );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

    elipsa­>setY( 100 );    
    elipsa­>setBrush( QBrush(red, Qt::SolidPattern ) );
    elipsa­>show();    
}

Co mówi nam ten kod ?? Co z tych enigmatycznych haseł powinno się zadomowić w

naszych   mózgownicach   ??   Przede   wszystkim   metodyka   pracy   z   QCanvas,   którą   da   się
usystematyzować w poniższych punktach:
1. Tworzymy klasę QCanvas (w moim przykładzie na całej formatce)
2. Tworzymy klasę QCanvasView i w konstruktorze łączymy ją z QCanvas oraz formatką
3. Podpinamy CanvasView do formatki (może to być też Layout ­ wtedy canvasów może być kilka)
4. Tworzymy malunki
5. Robimy show()
 

Co dalej ?? Dalej jest jeszcze QCanvasItem. Klasa rozbudowująca QCanvas o funkcje takie

jak   setActive(),   setSelected().   Do   tego   mamy   jeszcze   obsługę   zdarzeń   myszy,   o   której   tu   nie
wspomniałem. Czy powinienem ??

 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Open GL w Qt ­ Light Motif

Open   GL   jest   to   programowy   interfejs   do   grafiki   sprzętowej.   Zawiera   on   około   120

odrębnych poleceń, które są używane do specyficznych obiektów i operacji wykorzystywanych w
interaktywnych trójwymiarowych aplikacjach. Open GL został zaprojektowany jako strumieniowy,
sprzętowo­niezależny   interfejs,   który   został   zaimplementowany   dla   większości   systemów
operacyjnych   i   platform   sprzętowych.   Możliwości   tego   interfejsu   są   olbrzymie,   umożliwiają
tworzenie obiektów i dodawanie im życia. Jakkolwiek aby poruszać się po poleceniach Open GL,
należy bardzo dobrze znać matematykę. Macierze, kąty, obliczenia przy poruszaniu obiektami,
wszystko to wykracza poza ramy tej strony. W tym tekście zostanie zaprezentowany szablon do
dalszego udoskonalania. Prezentowany program wyświetli za pomocą Open GL prostą figurę i
będzie nią obracał w skali x,y,z.

Do poruszania się w świecie OpenGL stworzono w Qt specjalną klasę QGLWidget. Jest ona

swoistym połączeniem możliwości OpenGL z możliwościami Qt, klasy QWidget. Jak można się
domyślić będziemy reimplementować klase QGLWidget tworząc pod nową podklasę, posiadająca
pożądane funkcje OpenGL. 

// glwidg.h
#ifndef GLWIDG_H
#define GLWIDG_H
#include <qgl.h>
#include <qtimer.h>

class GLKwad : public QGLWidget
 {
   Q_OBJECT       
public:
  GLKwad( QWidget *parent, const char *name );
  ~GLKwad();
protected:      
  void initializeGL();
  void paintGL();
  void resizeGL( int w, int h );
};
#endif

Zaprezentowane tutaj funkcje są funkcjami Qt. InitializeGL ustawia w OpenGL takie opcje

jak kontekst, listy obiektów, tło itp. Musi być wywołana przed funkcjami paintGL i resizeGL.
Funkcja resizeGL jest wykonywana za każdym razem gdy okno zmienia swój rozmiar. Wielkość
obiektów jest wtedy przeliczana, a ich rozmiar dostosowywany proporcjonalnie. PaintGL renderuje
właściwą scenę grafiki. 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

// glwidg.cpp
#include "glwidg.h"
GLfloat r;
QTimer *Timer;

GLKwad::GLKwad( QWidget *parent, const char *name )
            : QGLWidget(parent, name) 
{
    r = 0.0;
    glClearColor(0.0, 0.0, 0.0, 0.0);    
    initializeGL();
    Timer = new QTimer( this, "Timer" );
    connect( Timer, SIGNAL( timeout() ), SLOT( updateGL() ) );
    Timer­>start(10);
}

GLKwad::~GLKwad()
{
}

void GLKwad::initializeGL()
{
  glClear(GL_COLOR_BUFFER_BIT);  
}

void GLKwad::resizeGL( int w, int h )
{
  glViewport( 0, 0, (GLint)w, (GLint)h );
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();  
}       

void GLKwad::paintGL()
{
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  glLoadIdentity();
  glRotatef( r,  1.0,  1.0,  1.0 );  // kazdy 1 odpowiada za jedna skale
  glColor3f(1.0, 1.0, 1.0);
  glBegin(GL_POLYGON);
        glVertex2f(­0.5, ­0.5);
        glVertex2f(­0.5, 0.5);
        glVertex2f(0.5, 0.5);

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





        glVertex2f(0.5, ­0.5);
  glEnd();
  glFlush();
  r ­= 0.15;
}

Na powyższym przykładzie widać, że w funkcjach Qt wykonujemy standardowe polecenia

OpenGL. Ustawiamy tryb wyświetlania, czyścimy i rysujemy kwadrat. W przykładzie widać, że
stworzyliśmy Timer, którego sygnał timeout jest połączony ze slotem QGLWidget updateGL(). Slot
ten nie robi nic innego jak tylko wywołuje funkcję paintGL. Zwiększanie wartość "r" w każdym
cyklu Timera powoduje obracanie obiektu o 0.15 stopni w każdym z trzech wymiarów. 

Plik formatki do tego programu wygląda tak:

// form.ui.h
#include "glwidg.h"

void Form1::init()
{
    GLKwad *glob = new GLKwad(this, "");
    setCentralWidget( glob );
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Managery rozmieszczenia, progz bez Designera

Może się czasem zdarzyć tak, ze przy pisaniu aplikacji nie będziemy chcieli korzystać z Qt

Designera np. w sytuacji gdy większość klas będziemy reimplementować lub gdy stosowane klasy
nie są dostępne w Qt Designerze. W takiej sytuacji głównym problemem jest rozmieszenie tych
komponentów tak nie nachodziły na siebie i ich rozmieszczenie było estetyczne. Ponadto może być
problematyczne łączenie sygnałów, funkcji itp. W tym tekście zajmiemy się tego typu sprawami. 

Managery rozmieszczenia

         Aby   ułatwić   programistom   układanie   komponentów   (np.   QTextEdit,   QPushButton)   na
formatkach, dopasowywanie ich rozmiarów do zmieniających się rozmiarów formatek mamy w Qt
do dyspozycji trzy zastosowania: rozmieszczenie komponentów poprzez połączenie ich w jeden
widget,   rozmieszczenie   komponentów   poprzez   połączenie   ich   w   jeden   obiekt,   oraz   klasę
QSpacerItem  pozwalającą  na dostosowanie odstępów  pomiędzy umieszczanymi komponentami.
Wszystkie   te   metody   używane   są   z   reguły   łącznie   tworząc   tym   samym   przystępny   system
zarządzania interfejsem.

Aby dokładnie wytłumaczyć zasadę ich działania, posłużę się przykładem. Dajmy na to, że

chcemy stworzyć proste okienko, które będzie posiadało pole tekstowe typu QLineEdit, labelkę
tekstową z opisem tego pola i do tego przycisk. Stworzenie takiego okienka w Qt Designerze zajęło
by pięć sekund. Jednak jeśli byśmy koniecznie chcieli mieć dostęp do np. konstruktora formy to
wykorzystanie designera nie będzie możliwe. Dlatego tez wykonamy to w prosty sposób ręcznie.
Wykonamy to wg. poniższego schematu:

Na   powyższym   schemacie   widać   co   i   gdzie   rozmieścimy   jednak   wymaga   to   zapewne

wytłumaczenie, dlaczego np. w jednym przypadku używamy klasy QVBoxLayout, a w innym
QHBox.   Odpowidź   polega   na   tym,   że   QVBoxLayout   jest   klasą   służącą   do   rozmieszczania
komponentów, jeśli podłączymy do niej jakieś nowo utworzone obiekty to będą one automatycznie
ustawiane   wg.   specyfiki   QVBoxLayout   (QHBoxLayout,   naturalnie   też).   Klasy   typu   QHBox   i
QVBox mają o większe możliwości i są prostsze w obsłudze. Dziedziczą funkcje po QWidget,
przez  to mogą   być  rodzicami  dla  nowo tworzonych komponentów.   Skraca  to  znacznie proces
tworzenia kodu. 

Kodowanie

Zanim   utworzymy   projekt   wg.   omówionego   schematu   muszę   podać   parę   faktów.   Na

program do tej lekcji będą się składały trzy pliki. Plik main.cpp z treścią nie wiele (lub wcale), nie
różniącą się od tej tworzonej przez Qt Designera:
 
#include <qapplication.h>
#include "mform.h"

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





int main( int argc, char ** argv )
{
  QApplication a( argc, argv );
  mForm w;
  w.show();
  a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
  return a.exec();
}

     Do tego plik mform.h zawierający deklarację klasy mForm:
 
#ifndef MFORM_H
#define MFORM_H

#include <qwidget.h>
#include <qlayout.h>

class QLineEdit;
class QLabel;
class QPushButton;

class mForm: public QWidget {
    Q_OBJECT
public:
    mForm(QWidget * parent = 0, const char * name = 0, WFlags f = 0);
    ~mForm();
    QLabel *textLab1;
    QLineEdit *lineEd1;
    QPushButton *pushButt1;
protected: 
};
#endif

Oraz z pliku mform.cpp, w którym będzie zdefiniowany kod obsługi tworzenia tego okna:

 
#include "mform.h"

#include <qpushbutton.h>
#include <qlabel.h>
#include <qlineedit.h> 

#include <qvbox.h>
#include <qhbox.h>

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

mForm::mForm(QWidget * parent, const char * name, WFlags f):
QWidget(parent, name, f)
{
  setCaption( "Layout ­ przykl" );
  QVBoxLayout *vboxl_1  = new QVBoxLayout( this, 8, 8, "vboxl_1");
  
  QHBox *hbox1 = new QHBox( this, "hbox1" );
  hbox1­>setMargin( 8 );
  hbox1­>setSpacing( 8 );
        textLab1 = new QLabel("Tresc labelki", hbox1);
        lineEd1 = new QLineEdit("Jakis tekst", hbox1);
  vboxl_1­>addWidget( hbox1 );
  
  QHBoxLayout *hboxl_1 = new QHBoxLayout( vboxl_1, 8, "hboxl_1" );
        QSpacerItem *space1 = new QSpacerItem(20, 20);
        hboxl_1­>addItem(space1);  
        pushButt1 = new QPushButton("Tekst przycisku", this);
        hboxl_1­>addWidget( pushButt1 );
        QSpacerItem *space2 = new QSpacerItem(20, 20);
        hboxl_1­>addItem(space2);  
}

mForm::~mForm()
{

}

Z   przykładu   od   razu   można   dostrzec   jedną   bardzo   istotną   rzecz.   Gdy   rozmieszczamy

komponenty przy użyciu QHBox, korzystamy z tego, że jest to klasa Widget i możemy ją podać
jako rodzica dla okien potomnych (QLabel i QLineEdit to także okna). Natomiast w przypadku
QHBoxLayout wykorzystujemy funkcję addItem. Dodatkowo bardzo interesującą sprawą jest fakt,
że przy tworzeniu  tych obiektów praktycznie  nie  definiujemy żadnych rozmiarów, ani  pozycji
(jedyne   co   podajemy   to   w   konstruktorze   QVBoxLayout   wartość   odstępów   pomiędzy
komponentami, a w konstruktorze QSpacerItem rozmiar minimalny). Wszystko robi za nas Qt. 

Kompilacja

 

Utworzenie tego wszystkiego (tych trzech plików) stanowi już program. Jakkolwiek aby go

bezproblemowo skompilować przy pomocy narzędzi Qt musimy utworzyć plik projektu. Tworzymy
go za pomocą polecenia:
 qmake ­project
 qmake

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





 make
Obsługa zdarzeń

Jeśli   przebrnęliśmy   przez   ręczne   tworzeni   interfejsu   to   obsługa   sygnałów   interakcji   z

użytkownikiem nie powinna być dla nas problemem. W deklaracji klasy musimy zadeklarować
slota jakiego będziemy używać:

class mForm: public QWidget {
    Q_OBJECT
public:
    mForm(QWidget * parent = 0, const char * name = 0, WFlags f = 0);
    ~mForm();
    QLabel *textLab1;
    QLineEdit *lineEd1;
    QPushButton *pushButt1;
public slots:
    virtual void addText();
protected: 
};

 

Jak widać użyliśmy tutaj słowa kluczowego virtual, mówiąc prostymi słowami zapewniamy

sobie w ten sposób, że funkcja slota (przy reimplementacji klasy) będzie wywołana i wykonana
nawet w sytuacji gdy funkcja o takiej nazwie juz istnieje. W pliku mform.cpp w konstruktorze
funkcji (po utworzeniu przycisku) połączymy sygnał kliknięcia przycisku 
 connect( pushButt1, SIGNAL(clicked()), this, SLOT(addText()) );
 z funkcją addText(), którą umieścimy w wyżej wymienionym pliku po definicji konstruktora:
 
void mForm::addText(){
  lineEd1­>setText("Hello...");
}

 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Jak napisać fornted ??

Jedną z najciekawszych klas Qt jest klasa QProcess. Pozwala ona na urchamianie poleceń

systemowych, uruchamianie programów i odbieranie wyniku zwracanego przez te programu. Tak
więc dzieki zastosowaniu klasy QProcess możemy w bardzo prosty sposób napisać nakładkę na
jakiś konsolowy program: co w systemie Linux będzie dość przydatne, gdyz wiele jest programów
systemowych, które wymagają od nas pamiętnia nazwy i składni polecenia. Zasotosowanie takie
nazywa się w terminologii komputerowej "Front End" i jest już dość popularne w przypadku takich
programów jak np. nmap. 

W tym rozdziale zaprezentuję dwa zastosowania klasy QProcess. Pierwsze pozwoli nam na

uruchomienie i zamknięcie programu KEdit. Drugie na uruchomienie strony manuala i pobranie jej
do pola tekstowego. W obu przypadkach schemat działania jest bardzo zbliżony. Deklarujemy klasę
QProcess, tworzymy polecenie i ewentualną listę argumentów, uruchamiamy process. 

#include <qprocess.h>
#include <qtextcodec.h>
#include <qapplication.h>

QProcess *kedProc;

void Form1::init()
{
    kedProc = new QProcess( this );
    kedProc­>addArgument( "kedit" );
}

void Form1::keditrun()
{
    if ( !kedProc­>start() ) {
        qDebug( "Blad podczas uruchamiania procesu" );
    }
}

void Form1::keditkill()
{
   kedProc­>kill();    
}

W przykładzie zaprezentowano sposób na uruchomienie zewnętrznego programu jakim w

tym przypadku jest KEdit. Uruchomienie programu konsolowego nie będzie się znacznie różniło,

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





jedyne co dodamy to slot readFromStdout(), który przechwyci wynik działania polecenie. 

#include <qprocess.h>
#include <qtextcodec.h>
#include <qapplication.h>

QProcess *proc;

void Form1::doProc()
{
    textEdit1­>setText("");
    
    proc = new QProcess( this );
    proc­>addArgument( "man" );
    proc­>addArgument( "3" );
    proc­>addArgument( "atoi" );
    
    connect( proc, SIGNAL(readyReadStdout()), this, SLOT(readFromStdout()) );
    if ( !proc­>start() ) {
        qDebug( "Blad podczas uruchamiania procesu" );
    }
}

void Form1::readFromStdout()
{
    QTextCodec::setCodecForCStrings( QTextCodec::codecForName("ISO8859­2") );
    textEdit1­>append( proc­>readStdout() );
}

Możliwości   operacji   wykonywanych   przy   pomocy   QProcess   są   w   systemie   Linux

olbrzymie,   gdyż   ilość   aplikacji   wykonywanych   konsolowo   jest   w   tym   systemie   ogromna.   Z
powyższych dwóch przykładów widać również, że zastsowanie to jest bardzo proste. Tak więc w
tym   przypadku   dzięki   Qt   możemy   ułatwić   sobie   życie.   Nie   tylko   programisty,   ale   również
administratora systemu.

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Technologia XML w Qt

O możliwościach XML można by napisać niejedną encyklopedią. Główną zaletą tego języka

jest jego olbrzymia skalowalność. Możwmy opisywać struktury i przechowywać dane w dowolny
wybrany   przez   nas   sposób.   W   jakikolwiek   sposób   to   uczynimy   będzie   to   prawidłowe   pod
warunkiem   oczywiście,   że   zachowamy   zamknięcie   tagów   i   atrybutów.   Dwa  najbardziej   znane
zastosowania XML to płatnik KEDU i np. RSS. W obu przypadkach standard został stworzony na
bazie XML. 

O XMLu

Hmmm, no cóż rozpisywać się nie będę bo nie o to chodzi. Znajdziecie w necie wiele na

temat   tego   języka.   Wiele   z   tych   wiadomości   zamota   wam   w   mózgach,   ale   to   nic.   W   końcu
połapiecie się w tym i wspólnymi siłami rozpracujemy Prokom, tak aby płatnik był nie tylko na
winzawieszacza :)

Aby zacząć cokolwiek na temat XML, musimy wiedzieć dwie rzeczy. Po pierwsze XML tak

jak HTML składa się ze znaczników. Znaczniki to np.: 

        <znacznik>
                <element>wartosc</element>
        </znacznik>. 

Jakkolwiek to samo moglibyśmy osiągnąć poprzez zapis jednolinijkowy:

        <znacznik element="wartosc" />

Różnica jest i jej nie ma :). Specyfikacja XML dopuszcza obie formy od użytkownika

zależy, którą wybierze. Jako programiści musimy umieć posługiwać się i jednym i drugim zapisem. 

Do tego wszystkiego dochodzi jeszcze bardzo istotny fakt, specyfikacji i validacji XML. W

pierwszych liniach pliku xmlowego podajemy o jaką specyfikację chodzi. Na przykład w przypadku
RSS jest to:

<?xml version="1.0" encoding="iso­8859­2"?>
<!DOCTYPE rss PUBLIC "­//Netscape Communications//DTD RSS 0.91//EN"
        "http://my.netscape.com/publish/formats/rss­0.91.dtd">

Plik,   do   którego   odnosi   się   ten   adres   zawiera   opis   elementów,   ich   format,   itd.   Przy

oprogramowywaniu własnych plików XML nie musimy się tym zajmować. 

W   obu   tych   przykładach   wykorzystuję   formatkę   złożoną   z   QDialog,   4   pól   lineEdit

(opisanych   przy   pomocy   QLabel),   jednego   checkBoxa   i   trzech   pushButtonów.   Funkcje

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





wczytaj_next(), wczytaj_prev() i dodanie() są połączone ze slotami clicked() przycisków. 
Podejście pierwsze ­ Qt, XML i atrybuty

Na   sam   początek   zajmiemy   się   obsługą   pliku   XML,   którego   struktura   złożona   jest   z

atrybutów.   Podejście   takie   jest   prostsze   z   punku   widzenia   programisty,   ponieważ   mamy   do
czynienia   z   mniejszą   ilością   gałęzi   po,   których   musimy   skakać.   Do   obsługi   gałęzi   XML
wykorzystamy technologię DOM. 

     Na samym początku oczywiście będziemy potrzebować plik XML. Proponuję utworzyć coś w
stylu:

<?xml version = '1.0' encoding = 'ISO­8859­2'?>
<baza>
 <osoba imie="Tomcio" studiuje="true" nick="moux" 
        nazwisko="Pielech" miejsce="Gorzów Wlkp." />
 <osoba imie="Mariusz" studiuje="true" nick="kazio" 
        nazwisko="Kaczorek" miejsce="Świnoujście" />
 <osoba imie="Monika" studiuje="false" nick="monia" 
        nazwisko="Kowalski" miejsce="Krakow" />
 <osoba imie="Arkafiusza" studiuje="true" nick="moksik" 
        nazwisko="Nowak" miejsce="Poznań" />
</baza>

Prosta struktura, baza osób. Jak to teraz zaczytać do programu ??

W   Qt   mamy   do   dyspozycji   takie   klasy   jak:   QDomDocument,   QDomElement   i

QDomNode.QDomDocument,   będzie   w   tym   przypadku   całym   dokumentem,   QDomElement   w
pierwszym przypadku będzie odpowiadał za tag baza, a QDomNode to osoba. Zaczytanie tego do
programu będzie wyglądało tak:

#include <qdom.h>
#include <qfile.h>
#include <qtextstream.h>

QDomDocument drzewo;
QDomElement root; // cala baza
QDomNode osoba;    // osoba
QDomElement ktos; // cala osoba

void Form1::init()
{
  QFile baza( "baza.xml" );
  baza.open( IO_ReadOnly );
    

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

  drzewo.setContent( &baza );
  baza.close();
            
  root = drzewo.documentElement(); // baza
  osoba = root.firstChild(); 
  ktos = osoba.toElement(); 
  wczytanie();
}

void Form1::wczytanie()
{
 lineEdit1­>setText(ktos.attribute("imie"));
 lineEdit2­>setText(ktos.attribute("nick"));
 lineEdit3­>setText(ktos.attribute("nazwisko"));
 lineEdit4­>setText(ktos.attribute("miejsce"));
 if (ktos.attribute("studiuje") == "false" ){
     checkBox1­>setChecked( FALSE );
 } else {
     checkBox1­>setChecked( TRUE );
 }
}

Z   tego   zapisu   (funkcja   init())   widać,   że   ustawiamy   osobę   na   pierwsze   dziecko   bazy.

Następnie   ładujemy   to   do   ktosia   i   możemy   odczytywać   atrybuty   tych   tagów.   W   ten   sposób
zaczytujemy _tylko_  pierwszego luda do programu. W dalszej części programu dodamy opcję
przeglądania następnych osób (funkcje połączone z przyciskami): 

void Form1::wczytaj_prev()
{
    if ( !osoba.previousSibling().isNull() ) {
        osoba = osoba.previousSibling(); 
        ktos = osoba.toElement(); 
        wczytanie();
    } else {
        osoba = root.lastChild(); 
        ktos = osoba.toElement(); 
        wczytanie();
    }
}

void Form1::wczytaj_next()
{
    if ( !osoba.nextSibling().isNull() ) {

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





        osoba = osoba.nextSibling();
        ktos = osoba.toElement(); 
        wczytanie();
    } else {
        osoba = root.firstChild(); 
        ktos = osoba.toElement(); 
        wczytanie();
    }
}

Tutaj podobnie jak w funkcji init(), ustawiamy sobie tego childa, którego chcemy i z niego

robimy   wczytanie().   Jedyną   nowinką   w   tym   kodzie   jest   to   co   mamy   w   instrukcji   if:   !
osoba.nextSibling().isNull(). Polecenie takie mówi, że zanim przejdziemy do następnego rekordu
sprawdzamy czy nie jest on pusty i jeśli jest idziemy do pierwszego. Unikamy w ten sposób pustych
pól przy przewijaniu. 

Jak dotąd analizując kod możemy zauważyć jedną rzecz. Dreptanie po rekordach odbywa się

za pomocą funkcji nextSibling i previousSibling. W większych programach i przy operacjach na
większych   plikach   funkcje   te   mogą   być   wykonane   w   pętli,   co   zautomatyzuje   zaczytywanie
zawartości pliku. 

Zapisanie/dodanie nowej pozycji. W tym przypadku po prostu dodajemy nowy element do

drzewa, a następnie zapisujemy przy pomocy strumienia. 

void Form1::dodanie()
{
  QDomElement nowy = drzewo.createElement ("osoba" );
  nowy.setAttribute( "imie", lineEdit1­>text() );
  nowy.setAttribute( "nick", lineEdit2­>text() );
  nowy.setAttribute( "nazwisko", lineEdit3­>text() );
  nowy.setAttribute( "miejsce", lineEdit4­>text() );
  if ( checkBox1­>isChecked() ) 
  {
      nowy.setAttribute( "studiuje",  "true");
  } else {
      nowy.setAttribute( "studiuje",  "false");
  }
 root.appendChild( nowy );
  
 QFile baza( "baza2.xml" );
 baza.open( IO_WriteOnly );
 QTextStream ts( &baza );
 ts << drzewo.toString();
 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

 baza.close();
}
Podejście drugie ­ Qt, XML i wartosci

No cóż mamy i drugą opcję. Mocno zadrzewiony plik xml, taki rss dajmy na to. Wygląda on

podobnie do tego:

<?xml version = '1.0' encoding = 'ISO­8859­2'?>
<baza>
 <osoba>
  <imie>Tomasz</imie>
  <nick>moux</nick>
  <nazwisko>Pielech</nazwisko>
  <miejsce_ur>Gorzów Wlkp.</miejsce_ur>
  <student>true</student>
 </osoba>
 <osoba>
  <imie>Kazio</imie>
  <nick>kazik</nick>
  <nazwisko>Kaczorek</nazwisko>
  <miejsce_ur>Warszawa</miejsce_ur>
  <student>false</student>
 </osoba>
</baza>

Odczyt danych z takiego pliku różni się tym od atrybutów, że skaczemy po QDomNode tak

długo, aż znajdziemy ta gałąź która nas interesuje. W takim przypadku możemy ją zaczytać do
QDomText i użyć w programie. Obsługa takiego pliku to już spory kawałek kodu: 

#include <qdom.h>
#include <qfile.h>
#include <qtextstream.h>

QDomDocument drzewo;
QDomElement root; // cala baza
QDomNode osoba;    // osoba
QDomElement osobaEl; // cala osoba
QDomNode daneOs; // same dane jedna linia
QDomText daneOsText; // zawartosc 

void Form1::init()
{
  QFile baza( "baza.xml" );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





  baza.open( IO_ReadOnly );
    
  drzewo.setContent( &baza );
  baza.close();
            
  root = drzewo.documentElement(); // baza
  
  osoba = root.firstChild(); 
  osobaEl = osoba.toElement(); 
//  qDebug( osobaEl.text() );    cala linia
  wczytanie();
}

void Form1::wczytanie()
{
    daneOs = osobaEl.firstChild() ; // imie
    daneOsText = daneOs.firstChild().toText();  
    lineEdit1­>setText( daneOsText.nodeValue()  );
    qDebug( daneOsText.nodeValue() );
    
    daneOs = daneOs.nextSibling(); // nick
    daneOsText = daneOs.firstChild().toText();  
    lineEdit2­>setText( daneOsText.nodeValue()  );
    qDebug( daneOsText.nodeValue() );
    
    daneOs = daneOs.nextSibling(); // nazwisko
    daneOsText = daneOs.firstChild().toText();  
    lineEdit3­>setText( daneOsText.nodeValue()  );
    qDebug( daneOsText.nodeValue() );
    
    daneOs = daneOs.nextSibling(); // miejsce
    daneOsText = daneOs.firstChild().toText();  
    lineEdit4­>setText( daneOsText.nodeValue()  );
    qDebug( daneOsText.nodeValue() );
    
    daneOs = daneOs.nextSibling(); // student
    daneOsText = daneOs.firstChild().toText();  
    if ( daneOsText.nodeValue() == "true" ) {
        checkBox1­>setChecked( TRUE );
    } else checkBox1­>setChecked( FALSE );
    qDebug( daneOsText.nodeValue() );
}

void Form1::wczytaj_prev()

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

{
    if (  !osoba.previousSibling().isNull() ) {
        osoba = osoba.previousSibling(); 
        osobaEl = osoba.toElement(); 
        // qDebug( osobaEl.text() );    // cala osoba
        wczytanie();
    } else {
        osoba = root.lastChild(); 
        osobaEl = osoba.toElement(); 
        // qDebug( osobaEl.text() );    // cala osoba
        wczytanie();
    }
}

void Form1::wczytaj_next()
{
    if ( !osoba.nextSibling().isNull() ) {
        osoba = osoba.nextSibling();
        osobaEl = osoba.toElement(); 
        // qDebug( osobaEl.text() );    // cala osoba
        wczytanie();
    } else {
        osoba = root.firstChild(); 
        osobaEl = osoba.toElement(); 
        // qDebug( osobaEl.text() );    // cala osoba
        wczytanie();
    }
}

Długie prawda ?? No długie chociaż ten kod nic specjalnego nie robi prócz zaczytywania

drzewa.   Można   to   by   było   rozpracować   za   pomocą   pętli   ale  wtedy   nie   był   by   tak   widoczny
konsensus tego zastosowania. Analizując ten kod, szczególnie funkcję wczytanie() należy zwrócić
ogromną uwagę co jest czym. Sposób w jaki przechodzimy od Node do Text. Bierzemy pierwszy
element DomNode (daneOs) i konwertujemy pierwszy element tej gałęzi do tekstu, który ładujemy
do lineEdit. 

Ok,   moje   Panie.   Teraz   hardcore   ­   zapisywanie   tegoż,   a   właściwie   dodawanie   nowych

elementów. Dlaczego hardcore ?? Dlatego, że aby zaskutkowało musimy umieć poruszać się po
drzewie i to całkiem nieźle. Dodanie elementu do drzewa jest stosunkowo proste, jednak dodanie do
tej gałęzi a nie innej to jest już nie takie hop*siup. 

void Form1::dodanie()
{

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





  QDomElement elOsoba = drzewo.createElement( "osoba" ); //
  root.appendChild(elOsoba);
  
  QDomElement poz = drzewo.createElement( "imie" ); //
  QDomText val = drzewo.createTextNode( lineEdit1­>text() );
  elOsoba.appendChild( poz );
  poz.appendChild( val );
  
  poz = drzewo.createElement( "nick" ); //
  val = drzewo.createTextNode( lineEdit2­>text() );
  elOsoba.appendChild( poz );
  poz.appendChild( val );
  
  poz = drzewo.createElement( "nazwisko" ); //
  val = drzewo.createTextNode( lineEdit3­>text() );
  elOsoba.appendChild( poz );
  poz.appendChild( val );
  
  poz = drzewo.createElement( "miejsce_ur" ); //
  val = drzewo.createTextNode( lineEdit4­>text() );
  elOsoba.appendChild( poz );
  poz.appendChild( val );
  
  QFile baza( "baza5.xml" );
  
  baza.open( IO_WriteOnly );
  QTextStream ts( &baza );
  ts << drzewo.toString();
 
  baza.close();
}

Hehe,   czytam   sobie   to   teraz   i   ciągle   mi   się   wydaje   skomplikowane.   No   cóż   funcka

createElement jest stosunkowo prosta do obczajenia. Chodzi ona w parze z funkcją appendChild. I
to   jest   właśnie   gwóźdź   programu,   appendChild,   które   dodaje   podgałąź   (Node)   tam   gdzie   jej
będziemy kazać. Stworzenie drzewa polega w takiej sytuacji na wywoływaniu funkcji appenChild
dla   każdej   gałęzi   i   podgałęzi   naszego   QDomDocument.   Zapis   jak   w   poprzednim   przypadku
QTextStream.

Modyfikacja

Hmm, z tego tekstu wcale nie wynika jak dokonywać modyfikacji. No cóż zasada jest

prosta. Tak jak w dwóch powyższych programach dodawaliśmy nową gałąź tak teraz zmienimy
jedynie   jej   wybrane   wartości   i   to   dopiero   zapiszemy   (zmodyfikowany   kod   dotyczący   XML   i

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

atrybutów):

void Form1::dodanie()
{
  ktos.setAttribute( "imie", lineEdit1­>text() );
  ktos.setAttribute( "nick", lineEdit2­>text() );
  ktos.setAttribute( "nazwisko", lineEdit3­>text() );
  ktos.setAttribute( "miejsce", lineEdit4­>text() );
  if ( checkBox1­>isChecked() ) 
  {
      ktos.setAttribute( "studiuje",  "true");
  } else {
      ktos.setAttribute( "studiuje",  "false");
  }
  
 QFile baza( "baza2.xml" );
 baza.open( IO_WriteOnly );
 QTextStream ts( &baza );
 ts << drzewo.toString();
 
 baza.close();
}

Qt i SAX !!!

Bardziej szpanerskie podejście do XML o korzeniach podobno gdzieś w JAVIE. Główną

ideją   (*   special   flower)   takiego   podejścia   jest   zaczytywanie   wszystkiego   jak   leci   z   opcją
wykrywania końca znacznika. Jak będzie to wyglądało w naszym kodzie (2 przykład) ?? Hmm, na
początek   małe   zaciemnienie,   aby   korzystać   z   SAX   musimy   reimplementować   klasę
QXmlDefaultHandler, chodzi po prostu o to, żeby wynik zaczytania poszczególnych tagów trafiał
tam gdzie my chcemy. Na poniższym przykładzie pokażę ładowanie do QString chociaż może to
byc   oczywiście   QListView   czy   QTable.   Przykład   składa   się   z   plików   parserek.h   ("parser"   ­
analizator składni) z deklaracją klasy, parserek.cpp z definicją funkcji i form.ui.h, gdzie to wszystko
wsadzamy do komponentu textEdit1:

// parserek.h
#include <qxml.h>
#include <qstring.h> 

class sParser : public QXmlDefaultHandler
{
public:
    bool startDocument();

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





    bool startElement( const QString&, const QString&, const QString& ,
                       const QXmlAttributes& );
    bool endElement( const QString&, const QString&, const QString& );
    bool characters( const QString & ch );
    QString daneImie();
private:    
    QString dane;
};

// parserek.cpp
#include "parserek.h"

bool sParser::startDocument()
{
    return TRUE;
}

bool sParser::startElement( const QString&, const QString&,
                            const QString& ,
                            const QXmlAttributes& )
{
    return TRUE;
}

bool sParser::endElement( const QString&, const QString&, const QString& )
{
    return TRUE;
}

bool sParser::characters( const QString & ch )
{
    dane += ch ;
    return TRUE;
}

QString sParser::daneImie()
{       
    return dane;
}

// form1.ui.h

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

#include "parserek.h"

void Form1::init()
{
    sParser handler;
    QFile xmlFile( "baza.xml" );
    QXmlInputSource source( &xmlFile );
    QXmlSimpleReader reader;
    reader.setContentHandler( &handler );
    reader.parse( source );
    
    textEdit1­>setText( handler.daneImie() );
}   

OK, rzucamy teraz okiem na wynik tego co taki program nam poczynił z pliczkiem:

Tłumaczenie tego programu nie jest chyba konieczne. Zaprezentowany tutaj kod to kolejna

pozycja  w naszym katalogu "Light Motif". Zaprezentowana klasa sParser nie będzie się wiele
różniła w programach. Jedyne modyfikacje będziemy dokonywać na tych elementach wyjściowych,
zamiast QString dany cos innego. Pewnie też będzie trzeba zmienić obsługę startElement, tak aby
po znalezieniu elementu (3 QString) "xxx" komputer wykonał z nim jaką operację w stylu np.
wczytał dzieci tylko tego elementu.

       Źródła programów w pobieralni (mały tips, aby program zaczytał plik baza.xml przy pomocy
ściezki podanej w sposób reletywny, powiniśmy odpalać program z konsoli lub zmienić ścieżkę na
bezwględną). 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





Programowanie w Pythonie z użyciem Qt

Dlaczego   o   tym   piszę   ??   Jeśli   będziecie   kiedyś   chcieli   używać   komputera   do   jakichś

niestandardowych rzeczy np. w genetyce to bardzo prawdopodobne, że niezbędna wam będzie
znajomość języka Python. Na temat zalet tego języka nie będę się lepiej wypowiadał, są w tej
materii lepsi specjaliści ode mnie. Mój osobisty zachwyt wzbudziło w tym języku podobieństwo do
php, gdzie nie musimy się zbytnio martwić o typy danych i ewentualne konwersje. Ponadto python
ma chyba najwięcej bibliotek, służących do konkretnych zastosowań. Dla samego przykładu można
wskazać zastosowanie Pythona w Gimpie, Qt i zastosowaniach naukowych. Nas jednak w tym
tekście będzie interesować tylko Qt w Pythonie.

Chciałbym aby ten tekst był swoista zachętą do Pythona, dla osób które jeszcze nie miały

okazji się w to pobawić. Przy pisaniu bazowałem na tekście Alexa Fedosova: "Tutorial: Creating
GUI Application in Python with Qt", jakkolwiek nie jest to tłumaczenie słowo w słowo.

Co będzie nam potrzebne ??

Oprócz Qt i Qt Designera niezbędny będzie oczywiście Python i pakiety: PyQt oraz PyQt­

devel.   Aby   dowiedzieć   się   jakie   pakiety   pythona   posiadamy   aktualnie   w   systemie   proponuję
skorzystać z polecenia: "rpm ­qa | grep ­i "py"". 

Co dalej ??

Podobnie jak w przypadku programowania z użyciem Qt Designera musimy pamiętać o

kolejności pewnych kroków, która jest niezbędna do wykonania programu:

 1. Tworzymy GUI (ze slotami i kodem), najlepiej w Qt Designerze
 2. Kompilujemy GUI i tworzymy kod Pythona za pomocą programu pyuic
 3. Tworzymy plik główny programu, który będzie tworzył formę
 4. Odpalamy programik za pomocą python nazwa.py 
 

Pierwsze primo...

Tworzenie aplikacji rozpoczynamy od stworzenia GUI. W tym celu odpalamy Qt Designera.

Aby  metodyka  pracy  z  Qt  w Pythonie,  albo  Pythonem  w  Qt  była  zrozumiała,  program który
napiszemy będzie banalnie prosty. Po raz kolejny w tym kursie napiszemy "hello world". W Qt
Designerze tworzymy nową formatkę QDialog dodajemy do niej pushButtona, lineEdita i slota
witajcie(). Łączymy slota z sygnałem wciśnięcia przycisku. A do wygenerowanej funkcji wklejamy
kod:

void Form1::witajcie()
{
  self.lineEdit1.setText( "Witajcie programisty" )

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

}

dla porównania ten sam kod w C++ wyglądał by tak:

void Form1::witajcie()
{
  lineEdit1­>setText( "Witajcie programisty" );
}

Jak widać pewne różnice są oczywiście, w końcu mamy do czynienia z innym językiem

programowania.

Drugie primo... kompilacja

W katalogu, w którym zapisaliśmy nasze pliki wygenerowane przez Qt Designera wydajemy

polecenie: "pyuic form1.ui > form1.py". Na niektórych Linuksach polecenie to powinno wyglądać
tak: "pyuic form1.ui ­o form1.py". W ten sposób wygenerowaliśmy sobie obraz kod tworzenia
okienka  qt   przy   pomocy   Pythona.   Do  poprawnego   działania   aplikacji   jest   to   jednak   za   mało.
Musimy mieć jeszcze plik główny, który z tego kodu utworzy okienko i je uruchomi.

Trzecie primo... main.py

Na temat tego pliku powiem tylko jedno. W przypadku tworzenia aplikacji za pomocą Qt

Designera jego treść będzie praktycznie nie zmienne. Podobnie jak w przypadku C++, gdzie Qt
Designer tworzył za nas kod tego pliku. Zawsze był on prawie niezmienny. Treść jest następująca:

from qt import *
from form1 import *
import sys
if __name__ == "__main__":
 app = QApplication( sys.argv )
 f = Form1()
 f.show()
 app.setMainWidget(f)
 app.exec_loop()

zmiany   jeśli   jakieś   będziemy   wykonywać   to   co   najwyżej   w  nazwie   pliku   form1   i/lub   nazwie
formatki. Plik ten jest ostatnim ogniwem w fazie projektowania programów w PyQt. Możemy teraz
zapisać ten plik pod dowolną nazwą i odpalić całość za pomocą polecenia "python main.py"

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





           Myślę, że na dobry początek tyle wiadomości wystarczy. W następnych odcinkach napiszę
trochę więcej na temat poruszania się po klasach i slotach w PyQt, być może będzie też trochę na
temat Pythona w zastosowaniach. Any questions ?? 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Programowanie bazodanowe

Zanim zaczne opisywanie baz danych i ich obsługi w Qt, muszę zapodać wstęp na temat

tego co będzie nam potrzbne aby się tego nauczyć. Po pierwsze serwer baz danych zdalny lub
lokalny. Po drugie lib do Qt do obsługi baz danych. Będziemy się uczyć na podstawie MySQL więc
lib   do   obsługi   MySQL   w   qt   będzie   w   pakiecie   qt­MySQL.   Przykładowa   baza   danych,   którą
będziemy operować w tym tekście została utworzona za pomocą pliku PL/SQL: 

# Tworzymy szkoleniowa bazke ludzie i wybieramy ja jako uzytkowa

create database `pipole`;
use pipole;

# Tworzymy tabelke lenie w bazie pipole
#
create table `lenie` (
  `id` smallint(6) not null default '0',
  `imie` varchar(20) not null default '',
  `nazwisko` varchar(25) not null default '',
  `adres` varchar(30) not null default '',
  key `id` (`id`)
) type=MyISAM;

# Wstawimy cosik do tabelki;
#
insert into `lenie` values (0, 'Tomasz', 'Pielech', 'Gorzówek');
insert into `lenie` values (1, 'Wiaczesław', 'Mołotow', 'Moscow');
insert into `lenie` values (2, 'Endriu', 'Pornholio', 'XXX');

         Dla   tych   co   wolą   klikanie   w   phpAdmin   czy   innych   programach   wspomagających
programowanie baz danych podaje strukturę tego co utworzą te wszystkie polecenia:

mysql> select * from lenie;

+----+------------+-----------+----------+
| id | imie | nazwisko | adres |
+----+------------+-----------+----------+
| 0 | Tomasz | Pielech | Gorzówek |
| 1 | Wiaczes aw | Mo otow | Moscow |

ł

ł

| 2 | Endriu | Pornholio | XXX |
+----+------------+-----------+----------+

3 rows in set (0.00 sec)

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





     Oczywiście znajomość podstaw SQL będzie niezbędna do zrozumienia tego tekstu.

Startujemy...

Opisywany   tutaj   program   będzie   w   dużej   mierze   bazował   na   programie   opisanym   w

dokumentacji do Qt z tą tylko różnicą, że opisany tu program zostanie wykonany metodą wizualną.
Za pomocą myszy i układania komponentów na formatce. 

Zaczynamy od stworzenia projektu w Qt Designerze np. o nazwie mysql.pro, dodania pliku

formy (QMainWindow), oraz utworzenia pliku main.cpp. Następnie połozymy na formatce button i
komponent QDataTable (o nazwie table). Po tej ostatniej operacji uruchomi się nam kolejny dziwny
kreator. Proponuję go zamknąć, lub ewentualnie po eksperymentować sobie z nim. W omawianym
przykładzie   nie   będziemy   korzystać   z   kreatorów.   Aby   dodać   programikowi   funkcjonalności
położymy na formatce jeszcze dwie labelki do opisu dwóch pól textEdit (lePassword i leUser). 

W   poniższym   przykładzie   przedstawię   najprostszy   sposób   na   napisanie   aplikacji,   która

pobierze sobie cosik z serwera baz danych MySQL. Lecimy po kolei. Na początku inkludujemy: 

#include <qsqldatabase.h>
#include <qdatatable.h>
#include <qsqlcursor.h>
#include <qmessagebox.h>
#include <qsqlselectcursor.h>

Obsługa serwera sql ma to do siebie, że trzeba się z nim połączyć, wskazać co chcemy i jak

chcemy to robić. Dlatego też pierwsze linijki kodu wyglądają tak:

QSqlDatabase * db;

void Form1::init()
{
    db = QSqlDatabase::addDatabase( "QMYSQL3" );    
    db­>setDatabaseName( "pipole" );
    db­>setHostName( "localhost" );
    table­>setAutoEdit( FALSE );
}

kolejne linie oznaczają:
 addDatabase wskazuje na sterownik/typ bazy danych jakiego będziemy używać
 setDataBaseName to nazwa bazy danych, odpowiednik sqlowego use
 setHostName to adres hosta z serwerem MySQL 
 

Kolejnym   krokiem   będzie   utworzenie   slota.   Proponuję   nadać   mu   nazwę   connect()   i

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

dokopypastować do niego następujący kod: 

void Form1::conenct()
{
    db­>setUserName( leUser­>text() );
    db­>setPassword( lePassword­>text() );
    
    if ( db­>open() ) {
    
    QSqlSelectCursor* cur = new QSqlSelectCursor(QString::null, db );
    table­>setSqlCursor( cur, TRUE, TRUE );
    cur­>exec("SELECT * FROM lenie");
    table­>addColumn( "imie", "Imie" );
    table­>addColumn( "nazwisko", "Nazwisko" ); 
    table­>addColumn( "adres", "Adres" );
    table­>refresh(); 
} else {
    QMessageBox::information( this, "ERROR", "Blad podczas laczenia");
}
}

Do zapytań obsługi sql w Qt przygotowano kilka klas np. QSqlCursor. Jakkolwiek ponieważ

w naszym przykładzie głównym  celem jest wejście do bazy i załadowanie  do tabelki tego co
chcemy   więc   wykorzystałem   tutaj   o   wiele   prostszą   klasę   QSqlSelectCursor.   Myślę,   że   kod
powyższy   jest   zrozumiały   i   nie   wymaga   wytłumaczenia.   W   następnym   tekście   dodamy   do
programu opcję wsadzania do bazy rekordu.

Po wykonaniu program powinien wyglądać mniej więcej:

Dodawanie i usuwanie rekordów wykonujemy poprzez użycie klasy QSqlQuery, do której w

funkcji   exec   możemy   wsadzać   dowolne   zlecenia   SQL.   Zlecenia   usuwania   czy   dodawania   nie
zwracają   żadnego   wyniku   więc   możemy   stosować   je   w   połączeniu   z   funkcjonalnością   klasy
QSqlSelectCursor. Zastosowanie obu będzie więc polegało na tym iż najpierw załadujemy dane do
tabeli przez kursor, potem usuniemy/dodamy jakiś rekord poprzez query, a następnie odświeżymy
widok tabeli przy użyciu refresh(). Kod, który to wykona: 

void Form1::skasuj()
{
    if ( (db­>open()) && (lineEdit5­>text() != "" ) ) {
        QSqlQuery query(QString::null, db);
        query.exec( "DELETE FROM lenie WHERE imie='" 
                    + lineEdit5­>text() + "'" );
        table­>refresh(); 

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





    }
}

void Form1::dodaj()
{
    if ( db­>open() ) {
        QSqlQuery query(QString::null, db);
        query.exec("INSERT INTO lenie(imie, nazwisko, adres) VALUES ('" 
                   + lineEdit6­>text() + "','"+
                   + lineEdit7­>text() + "','"+
                   + lineEdit8­>text() + "')" );
        table­>refresh(); 
    }
}

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

Programowanie sieciowe

// podziękowania dla Artanisa za pomoc przy testowaniu

W   tym   tekście   zowstanie   omówiona   komunikacja   między   dwoma   komputerami

wykorzystująca gniazda. Zanim jednak zacznę opisywac program, chciałbym zwrócić uwagę na
bardzo istotną rzecz. Transmisja gdy już  nawiążemy  połączenie jest  wykonywana  przy użyciu
strumieni   QDataStream   i   QTextStream.   Oznacza   to   w   pierwszym   przypadku   gdy   na   taki   sie
zdecydujemy, że możemy odczytywać dowolną ilość bitów i zaczytywać je do dowolnej struktuty
danych (protokół). W drugim przypadku uławia nam konwersje między typami danych.

Jako pierwszy program sieciowy proponuję napisanie prostego komunikatora internetowego.

Wykonaymy go przy pomocy Qt Designera według prezentowanego już wcześniej schematu. W
programie wykorzsyatmi dwa pola QTextEdit jedno z formatem tekstu ustawionym na RichText
gdzie będą się pojawiać nowe wiadomości, drugi z formatem PlainText skąd będziemy wysyłać
wiadomości.  Do  tego będzie  nam potrzebne jeszcze jedno pole  LineEdit,  w którym  będziemy
wpisywać   adresy   IP.   Całość   będzie   obsługiwana   dwoma   przyciskami   QPushButton,   z   których
pierwszy połączy nas z serwerem, a drugi wyśle treść jednego z pół textEdit.

Działanie   programu   będzie   polegało   na   tym,   że   po   uruchomieniu   na   dwóch   różnych

maszynach będą się one mogły ze sobą połączyć i komunikować tj. jedna będzie mogła wysłać do
drugiej treść i na odwrot. Zasada działania mechanizmu odbioru opiera się na stworzeniu klasy
QServerSocket, z którą bądą się łączyć wszyscy klienci. Do połączeń wykorzystamy port 4242.
Połączenie   spowoduje   utworzenie   gniazda   ClientSocket   (QSocket)   odbierającego   wszystkie
informacje wpływające na port 4242. Każde połączenie powodować będzie utworzenie nowego
ClientSocket. Obie klasy zadeklarowane zostały w osobynch plikach dla łatwiejszej nawigacji w
kodzie:

// server.h
#include <qserversocket.h>
#include <qapplication.h>
#include <qtextstream.h>
#include "client.h"

class SimpleServer : public QServerSocket
{
    Q_OBJECT
public:
    SimpleServer( QObject* parent=0 ) :
            QServerSocket( 4242, 1, parent )
    {
        if ( !ok() ) {
            qWarning("Otwarcie portu nie mozliwe, aplikacja zostanie zamknieta...");

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





            exit(1);
        }
    }
    
    ~SimpleServer()
    {
    }
    
    void newConnection( int socket )
    {
        ClientSocket *s = new ClientSocket( socket, this );
        emit newConnect( s );
    }
    
signals:
    void newConnect( ClientSocket* );
};

Jak widać klasa QServerSocket została tutaj reimplementowana. Obsługujemy dzięki temu

metodę newConnection tej klasy, której to tworzymy nową instancję klienta. Klasa ta będzie miała
następującą postać:

// client.h
// klasa dla kazdego nowego klienta
#include <qsocket.h>
#include <qapplication.h>
#include <qtextstream.h>

class ClientSocket : public QSocket
{
    Q_OBJECT
public:
    ClientSocket( int sock, QObject *parent=0, const char *name=0 ) :
            QSocket( parent, name )
    {
        line = 0;
        connect( this, SIGNAL(readyRead()),
                 SLOT(readClient()) );
        connect( this, SIGNAL(connectionClosed()),
                 SLOT(deleteLater()) );
        setSocket( sock );
        qDebug("Przylaczyl sie nowy klient");
    }

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image

    ~ClientSocket()
    {
    }

signals:
    void logText( const QString& );

private slots:
    void readClient()
    {
        QTextStream ts( this );
        while ( canReadLine() ) {
            QString str = ts.readLine();
            emit logText( QString("> %1\n").arg(str) );
            line++;
        }
    }

private:
    int line;
};

Powyższy przykład prezentuje wykorzystanie funkcji readClient do odczytywania danych

wysłanych do serwera. Funkcja (slot) ta jest połączona z sygnałem klasy ClientSocket przez co jest
automatycznie wywoływana gdy dostaną się do portu dotrą jakieś dane. Po odebraniu danych są one
przesyłan do funkcji logText, którą wykorzystujemy w pliku formatki:

// form1.ui.h
SimpleServer *server; // serwer do odbioru
        
void Form1::init()
{
    textEdit1­>setTextFormat( Qt::PlainText );
    server = new SimpleServer( this );
    connect( server, SIGNAL(newConnect(ClientSocket*)),
             SLOT(newConnect(ClientSocket*)) );
}

void Form1::newConnect( ClientSocket *s )
{
    textEdit2­>append( "Polaczono\n" );
    connect( s, SIGNAL(logText(const QString&)),
             textEdit2, SLOT(append(const QString&)) );

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)

background image





}

void Form1::wyslij() // przycisk 2
{
    QTextStream os(socket);
    os << textEdit1­>text() << "\n";
    textEdit2­>append( "<b>" + textEdit1­>text() + "</b>");
    textEdit1­>setText( "" );
}

void Form1::polacz() // przycisk 1
{
    socket = new QSocket( this );
    socket­>connectToHost( lineEdit1­>text(), 4242 );
}

UWAGA !!! 
Zmienna socket została dodana do pliku form1.h poprzez dopisanie w Object Explorerze w sekcji
Class­>protected linii QSocket *socket. Zmienna ta musi być protected. Wykonana aplikacja to:

Kopiowanie kursu w całości lub w kawałku dozwolone pod warunkiem umieszczenia autorstwa

kursu (Tomasz Pielech) i podania adresu email autora (moux@post.pl)


Wyszukiwarka

Podobne podstrony:
kurs program
Asembler Kurs Programowania Dla Srednio Zaawansowanych S Kruk www !OSIOLEK!com
kurs programowania w javascript ZJP2R6YAGSKPQWSNDDGALSPMIOVH2OVSNHC2HKY
kurs programowania w języku ms basic, Programowanie mikrokontrolerów
kurs programu excel DEV4YG6ABN4ZXLHZSIK2AO2Q5NCXAHKHJBEXHNY
KURS program, KURS PILOTA WYCIECZEK
Asembler Kurs programowania dla średnio zaawansowanych
kurs program
Asembler Kurs Programowania Dla Srednio Zaawansowanych S Kruk www !OSIOLEK!com
Asembler Kurs programowania dla średnio zaawansowanych
Asembler Kurs Programowania Dla Srednio Zaawansowanych S Kruk www !OSIOLEK!com
Kurs programowania w C

więcej podobnych podstron