programowanie
Qt ISO Maker
moja pierwsza
aplikacja w Qt
Tomasz Pielech
brew obiegowym opiniom, ce m.in. do generowania plików Makefile)
programowanie aplikacji mogły zostać znalezione. Tę część powin-
okienkowych w Linuksie ny wykonać za nas skrypty dołączone do
Wjest proste. Ten artykuł pakietów z biblioteką. W niektórych przy-
udowodni tę tezę; wykonamy pierwszą padkach (np. gdy wydanie polecenia qmake
aplikację, która będzie służyła do two- zwróci błąd command not found ) może
rzenia obrazów płyt CD. Program będzie okazać się konieczne utworzenie zmiennej
umożliwiał przeglądanie plików i katalo- systemowej QTDIR, która będzie zawie-
gów znajdujących się na dysku i dodawa- rała ścieżkę do katalogu z biblioteką Qt.
nie ich do obrazu. Sam proces tworzenia Przykładowo, jeśli Qt jest zainstalowane
będzie opierał się na programie Mkisofs, w katalogu /usr/lib/qt3, to w pliku ~/.bashrc
więc w rzeczywistości stworzymy graficz- powinniśmy dopisać:
ną nakładkę na ten program.
export QTDIR=/usr/lib/qt3
Narzędzia, wymagania
systemowe Rozpoczęcie pracy
Omawiana tutaj aplikacja zostanie wyko- projekt aplikacji
nana przy pomocy biblioteki Qt i narzę- Pierwszym elementem przy tworzeniu
dzi dostarczonych razem z nią. W trak- naszej aplikacji będzie stworzenie okien-
cie przygotowania artykułu korzystałem ka, w którym będą rozmieszczone wszyst-
z biblioteki Qt w wersji 3.3.3 oraz kompi- kie kontrolki używane przez program.
latora GCC 3.3.5. Aby uniknąć podstawo- Do jego stworzenia wykorzystamy pro-
wych błędów związanych z kompilacją gram Qt Designer, który ułatwi nam stwo-
zródeł, wymagane jest posiadanie biblio- rzenie całego interfejsu. Za jego pomocą
teki Qt w wersji 3.3 lub wyższej. Wersja stworzymy również projekt aplikacji (plik
kompilatora nie ma większego znaczenia. o rozszerzeniu pro), a także plik main.cpp.
Programy tworzone z użyciem Qt można Ważne jest więc, aby prześledzić uważnie
kompilować zarówno w GCC3, GCC4, jak wszystkie opisywane poniżej kroki, gdyż
również korzystając z innych kompilato- pozwoli nam to uniknąć wielu niepotrzeb-
rów, np. Intela. nych błędów.
W systemach, w których oprogramo- W pierwszym kroku stworzymy plik
wanie jest dystrybuowane w postaci projektu naszej aplikacji. W tym celu uru-
RPM-ów, powinniśmy posiadać następuj- chamiamy program Qt Designer i w okien-
ące pakiety (Aurox): qt-designer, qt-3.3.3 ku Qt Designer New/Open (lub po wybraniu
oraz qt-devel. W systemach, w których File->New) wskazujemy opcję C++ Project.
oprogramowanie jest dystrybuowane w po- Plik ten zapisujemy w katalogu specjalnie
staci DEB-ów, powinniśmy posiadać na- przygotowanym do tego celu. W omawia-
stępujące pakiety (Ubuntu): qt3-apps-dev, nym przykładzie będzie to ~/Projekty/qtiso/,
qt3-assistant, qt3-designer, qt3-dev-tools oraz a nazwa pliku to qtiso.pro.
qt3-doc. Rysunek 1 przedstawia ekran Qt De-
Komplenta konfiguracja tych narzędzi signera. Oznaczenia to w kolejności: (1)
Na CD/DVD
powinna zapewniać, aby pliki bibliotek Przestrzeń robocza, w której będą poja-
Na CD/DVD znajduje się kod
zródłowy tworzonej aplikacji. oraz pliki i programy używane w procesie wiać się okienka; (2) Elementy graficznego
kompilacji (np. Qmake narzędzie służą- interfejsu aplikacji; (3) Właściwości aktual-
24 luty 2006
programowanie
programowanie w Qt
nie zaznaczonego elementu; (4) Elementy
Listing 1. Kod wczytujący listę katalogów systemu do kontroli ListView
klasy, które są tworzone na podstawie
okna; (5) Przyciski umożliwiające roz-
mieszczanie elementów interfejsu. #include
Po zapisaniu pliku projektu przy- void qtISOMForm::init (){
stępujemy do etapu właściwego, czyli listDir->setRootIsDecorated (true);
tworzenia okienka naszego programu. mainDirRead ();
W menu File->New wybieramy Main Win- }
dow. Kreator, który pojawi się, możemy void qtISOMForm::mainDirRead (){
od razu zamknąć przyciskiem Cancel. QDir dir("/");
Teraz nazwiemy nasze okienko. W prawej dir.setFilter (QDir::All);
części programu Qt Designer powin- QStringList content = dir.entryList ();
na być zakładka Properties (punkt 3 na QListViewItem *newItem;
S
Rysunku 1) ze wszystkimi właściwościa- for (QStringList::Iterator it = content.begin ();
mi. Odszukujemy na tej liście wartość name it != content.end (); ++it){
S
i wpisujemy tam qtISOMForm. Po wpisa- if ((QString::compare (*it, "..") != 0)
niu tej nazwy zapisujemy plik okna, na && (QString::compare (*it, ".") != 0)){
którym za chwilę będziemy umieszczać QFileInfo fileinfo ("/" + *it);
kolejne elementy interfejsu. newItem = new QListViewItem (listDir, *it);
Ostatnim krokiem w przygotowy- if (fileinfo.isReadable () && fileinfo.isDir ())
waniu projektu naszej pierwszej aplika- newItem->setPixmap (0, QPixmap ("images/dir.png"));
cji będzie stworzenie pliku main.cpp. W if (!fileinfo.isReadable () && fileinfo.isDir ())
tym celu ponownie wybieramy z menu newItem->setPixmap (0, QPixmap ("images/dirro.png"));
File->New, a następnie C++ Main-File. if (fileinfo.isFile ())
Po wybraniu tego pliku będziemy mogli newItem->setPixmap (0, QPixmap ("images/file.png"));
wybrać główne okno programu. Jeśli }
wszystkie kroki przeprowadziliśmy pra- }
widłowo, to na liście będziemy mieli tylko }
jedną pozycję, więc wystarczy po prostu
zatwierdzić przyciskiem OK. Po wygene- proces wymagający, po pierwsze, umiesz- wego. Przykładowo, jeśli przed literą F
rowaniu pliku main.cpp możemy go zapi- czenia wszystkich kontrolek wykorzy- podamy znak &, to w programie po skom-
sać i od razu zamknąć, gdyż nie będzie- stywanych przez program, a po drugie, pilowaniu użycie klawiszy [Alt]+[F] spo-
my modyfikować jego treści. Jedynym jego poprawnego ich rozmieszczenia. woduje rozwinięcie menu File. Dodanie
zadaniem jest uruchomienie głównej pętli Na początku dodamy menu do nasze- separatora (pionowa linia oddzielająca
naszej aplikacji (w tym przypadku chodzi go okienka. Wykonujemy to poprzez pozycje w menu) do menu jest możliwe
o pętlę w rozumieniu sygnałów i slotów, kliknięcie prawym klawiszem myszy w po przeciągnięciu go metodą przeciągnij
gdyż jej działanie polega na tym, że aplika- dowolnym miejscu głównego formularza, i upuść w odpowiednie miejsce.
cja działa tak długo, aż nie zostanie wyge- a następnie wybraniu opcji Add Menu item. Gdy dodamy wszystkie trzy wymie-
nerowany sygnał lastWindowClosed(); Po wykonaniu tej operacji będzie od razu nione wcześniej elementy do menu, będą
dopiero wówczas następuje zamknięcie widoczne menu, które możemy dosto- one widoczne w oknie Action Editor.
aplikacji i usunięcie jej z pamięci), która sować do swoich potrzeb. Utworzymy Dodamy teraz elementy interfejsu do
wyświetli okno qtISOMForm. teraz dwie pozycje: File i Help. Do menu obsługi systemu plików. Będzie to ListView
File dodamy dwa elementy: Save ISO oraz dostępne z menu Tools->Views->ListView.
Tworzenie GUI Exit, a w menu Help dodamy tylko pozy- Dokonamy teraz edycji tej kontrolki. Aby
Przygotowanie GUI programu z wyko- cję About. było to możliwe, klikamy na nią dwu-
rzystaniem Qt Designera jest stosunkowo Wykonanie tych czynności jest stosun- krotnie. Powinno ukazać się nam okien-
proste. Należy jednak pamiętać, że jest to kowo intuicyjne. Podam kilka użytecz- ko Edit ListView z aktywną zakładką Items.
nych porad, które mogę przydać się pod- Usuniemy domyślnie dodaną pozycję New
czas tworzenia menu. Po pierwsze, gdy Item oraz w zakładce Columns zmienimy
klikamy myszą na jakimś menu, możemy nazwę kolumny na Directories. W oknie
wykorzystać klawisz [F2], aby zmienić Properties zmienimy nazwę obiektu (kon-
jego nazwę. Po drugie, użycie klawisza trolki listView1) na listDir. Dodamy
[F2], gdy jest zaznaczona pozycja new item, teraz jeszcze kontrolkę Display->TextLabel
wpisanie nowej nazwy menu i zatwier- oraz Buttons->PushButton. Pierwszą umie-
dzenie klawiszem [Enter] powoduje doda- ścimy ponad listDir, a drugą poniżej. Treść
nie nowej pozycji. Po trzecie, użycie znaku wyświetlaną przez textLabel możemy zmie-
ampersand & przed literą, w nazwie nić klikając dwukrotnie na tym elemencie.
którejś z menu, umożliwia uruchomie- Wpiszemy tutaj: List of files and directories.
Rysunek 1. Ekran Qt Designera
nie tej opcji za pomocą skrótu klawiaturo- Zmianę tekstu na przycisku wykonuje-
www.lpmagazine.org 25
programowanie
czenie tych kontrolek tak, aby w każdej roz- Kod aplikacji
dzielczości ekranu oraz niezależnie od usta- Środowisko Qt Designer (w Qt z serii 3.x)
wień personalnych użytkownika bądz sys- umożliwia nam, oprócz tworzenia GUI
temu (takich jak np. wykorzystywane style) aplikacji, również tworzenie kodu progra-
były poprawnie wyświetlane. Zaznaczamy mu. Zanim przejdziemy do samego two-
wszystkie kontrolki, a mówiąc dokładniej, rzenia kodu, omówię jeszcze udogodnie-
dwie grupy kontrolek i wybieramy opcję nia, które oferuje nam Qt Designer. Warto
Layout->Lay Out Horizontally (in Splitter), jednak zaznaczyć, że cały kod, który
a następnie klikamy w dowolnym miejscu będziemy tworzyć, nie jest kompletnym
na formularzu okna (tak, aby nic nie było kodem, z jakiego będzie zbudowany pro-
zaznaczone) i wybieramy opcję Layout->Lay gram. Na podstawie tego kodu, jak rów-
Rysunek 2. Wykonanie GUI Out in a Grid. nież pewnych zmiennych, które zostaną
Ostatnim krokiem, który nam jesz- teraz omówione, będzie generowany wła-
my w analogiczny sposób. Tu wpiszemy cze pozostał w przygotowaniu GUI ściwy kod programu (pliki *.h i *.cpp).
A&dd. Zmienimy jeszcze nazwę przycisku naszej aplikacji, jest zmiana tytułu, który
(Properties->name) na addBtn. jest wyświetlany przez główne okno. Na Sloty
Gdy wszystkie te operacje będziemy zakładce properties odszukujemy pozycję Gdy wybierzemy z menu Edit->Slots...,
mieli za sobą, wykonamy bardzo istotną caption i wpisujemy: Qt ISO Maker. Jeśli ukaże się nam okienko, w którym będzie-
czynność, jaką jest grupowanie kontrolek. wszystkie kroki wykonaliśmy zgodnie z
W tym celu musimy zaznaczyć wszyst- tym, co zostało opisane, to powinniśmy
Tworzenie aplikacji
kie trzy kontrolki (zaznaczanie wykonu- ujrzeć surowy projekt aplikacji podobny
w polskiej wersji językowej
je się analogicznie do zaznaczania ikon do tego zaprezentowanego na Rysunku 2.
Tworząc oprogramowanie z wykorzysta-
np. na pulpicie). Gdy będą zaznaczone, O tworzeniu GUI Czytelnik powinien
niem Qt Designera można z powodze-
wybieramy opcję Lay Out Verticaly (przy- pamiętać, że samo umieszczenie kontrolek
niem stosować polskie nazwy z polski-
cisk z trzema poziomymi liniami na pasku na formularzu nie jest jeszcze wystarcza-
mi znakami diakrytycznymi. W przypadku
narzędziowym Qt Designera) lub używa- jące, jak również, że poprawne rozmiesz-
napisów na przyciskach i w menu nie ma
my kombinacji klawiszy [Ctrl]+[L]. Od tej czenia jest niezwykle istotne, gdy aplikacja
z tym żadnego problemu. W przypadku
pory wszystkie trzy kontrolki mogą być ma być dostępna dla osób korzystających z
napisów (stringów) wpisanych w kodzie
traktowane jako jedna podczas dalszego innych środowisk graficznych lub dystry-
programu, tj. tekst komunikatu, musimy
rozmieszczenia. bucji. Postępowanie z menedżerami roz-
stosować się do pewnych reguł, aby pol-
Gdy już wiemy, jak dodawać i rozmiesz- mieszczania (na Rysunku 2 widoczne są
skie znaki diakrytyczne były wyświetlane
czać kontrolki na formatkach Qt Designera, w postaci czerwonych linii) jest analogicz-
prawidłowo. Po pierwsze, w konstruktorze
dodamy jeszcze trzy. Będą to textLabel, list- ne do projektowania stron internetowych
klasy (lub w funkcji init()), w której chcemy
Box, pushButton. Tekst w kontrolce textLabel i stosowania znacznika table. Layouty
używać polskich znaków, musimy wska-
zmienimy na Add this to ISO. Z listBox są swego rodzaju tabelkami, do których
zać kodowanie, które ma być używane w
usuniemy domyślnie dodaną pozycję (New będą justowane kontrolki. Zarówno przy
programie. Służy do tego funkcja:
item) i zmienimy jej nazwę na listIso. Na projektowaniu stron internetowych, jak
przycisku napiszemy Re&move, a następnie i w przypadku programowania z użyciem #include
zgrupujemy wszystkie trzy tak samo jak w Qt, prawidłowe wykonanie pozycjonowa- [...]
poprzednim akapicie. nia poszczególnych elementów jest szcze- QTextCodec::setCodecForTr( QText
W chwili obecnej mamy już wszystkie gólnie ważne, gdy strona bądz program Codec::codecForName("ISO8859-2") );
kontrolki niezbędne do wykonania pod- będzie wykorzystywany na innych kom-
Następnie w kodzie programu, zawsze
stawowej funkcjonalności naszej aplikacji. puterach niż ten, na którym został stwo-
gdy będziemy używać napisów w języku
Niezbędne jest jednak poprawne rozmiesz- rzony i wygląda ładnie.
polskim, będziemy opatrywać je funkcją
tr(). W przypadku komunikatu informa-
cyjnego może to wyglądać następująco:
QMessageBox::information
(this, tr("Ostrzeżenie"),
tr("Nie można uruchomić
procesu..."),
QmessageBox::Ok);
Zastosowanie funkcji tr() ma tę dodat-
kową zaletę, że na jej podstawie można
wygenerować pliki językowe, które stosu-
je się przy tworzeniu wersji językowych
programu.
Rysunek 3. Aączenie sygnałów ze slotami
26 luty 2006
programowanie
programowanie w Qt
my mogli zadeklarować dowolną funkcję
Listing 2. Kod wczytujący listę podkatalogów według pozycji startowej
języka C++. Funkcje, które w ten sposób
utworzymy, zostaną w procesie kompila-
cji dodane do definicji naszej klasy qtISOM- void qtISOMForm::readDir (QString initDir, QListViewItem * parent){
Form i będą widoczne w pliku nagłówko- QDir dir (initDir);
wym .ui/qtisomform.h. dir.setFilter (QDir::All);
Okienko (Edit Functions), które teraz QStringList content = dir.entryList ();
mamy na ekranie, pozwala zdefiniować QListViewItem *newItem;
S
wszystkie części funkcji, takie jak nazwa, for (QStringList::Iterator it = content.begin ();
typ oraz zwracana wartość. Najbardziej it != content.end (); ++it){
intrygujące będzie pewnie pole Type:, w if ((QString::compare (*it, "..") != 0)
którym mamy do wyboru slot lub funk- && (QString::compare (*it, ".") != 0)){
cję. Różnica polega na tym, że slot jest spe- QFileInfo fileinfo (initDir + "/" + *it);
cyficzną funkcją, która może być połączo- newItem = new QListViewItem (parent, *it);
na z sygnałem wygenerowanym przez if (fileinfo.isReadable () && fileinfo.isDir ())
użytkownika. Przykładem sygnału jest newItem->setPixmap (0, QPixmap ("images/dir.png"));
np. kliknięcie na przycisku lub wybranie if (!fileinfo.isReadable () && fileinfo.isDir ())
jakieś pozycji z menu. newItem->setPixmap (0, QPixmap ("images/dirro.png"));
if (fileinfo.isFile ())
Connections newItem->setPixmap (0, QPixmap ("images/file.png"));
Gdy z menu Qt Designera wybierzemy }
Edit->Connections, ukaże się nam okien- }
ko, które służy do łączenia slotów (specjal- }
nych funkcji) z sygnałami występującymi
w programie. Od razu zdefiniujemy jedno
połączenie, które doda do naszego progra- tworzyć wszystkie definicje metod, które Listing 3. Slot powiązany z sygnałem
mu pierwszą funkcjonalność. Po wciśnię- będą wykorzystywane w naszym progra- podwójnego kliknięcia na elemencie listy
ciu przycisku New ustawimy wszystkie mie. Mówiąc kolokwialnie, w tym miejscu
kolumny wg Rysunku 3. stworzymy całą mechanikę naszej aplikacji. void qtISOMForm::lv1DbClicked
W ten sposób od razu po skompilowa- (QListViewItem * item){
S
niu, program będzie zamykał się po wybra- init()/destroy() if ((item->firstChild () == 0)
niu opcji Exit z menu. Nie jest to nic zna- Oprócz opisanych powyżej zalet, two- && (!item->isOpen ())){
czącego, ale jak do tej pory nie wpisali- rzenia aplikacji w programie Qt Designer QString tmpStr;
śmy żadnej linii kodu. Podobnych ułatwień ma również kilka wad. Jedną z nich jest if (item != 0){
w przypadku programowania z użyciem to, że nie mamy dostępu do konstruktora if (item->parent () != 0){
Qt jest sporo. i destruktora klasy okna. Tak więc wykona- tmpStr = item->text (0);
S
nie jakichś operacji zaraz po uruchomieniu QListViewItem *tmpItem =
Members programu jest realizowane w inny sposób. item;
W prawym górnym rogu w programie Mamy do tego dwie dodatkowe funkcje: while (tmpItem->parent ()){
Qt Designer znajduje sie zakładka opi- init() oraz destroy(). Aby można było tmpItem = tmpItem->parent ();
S
sana jako Members, w której definiujemy z nich skorzystać, należy je dodać w oknie tmpStr.prepend (tmpItem
wszystkie części składowe klasy edyto- Edit Functions, które zostało już omówione. ->text (0) + "/");
wanego okna. Ta część została podzielo- W naszym programie będziemy potrzebo- }
na na kilka elementów. Odpowiadają one wać jedynie funkcji init(), więc tylko taką }
wszystkim częściom, które mogą wcho- funkcję musimy dodać. Klikamy przycisk else{
dzić w skład definicji klasy w języku C++. New function, a następnie w polu Function tmpStr = item->text (0);
Na podstawie zawartości tego okna, jak i wpisujemy init() i zatwierdzamy przy- }
naszej formatki, zostanie wygenerowany ciskiem OK. Po dodaniu tej funkcji będzie }
plik nagłówkowy odpowiedzialny za cały ona widoczna w edytorze kodu. readDir ("/" + tmpStr, item);
wygląd i funkcjonalność naszej aplikacji. item->setOpen (true);
Drzewo katalogów }
Edytor kodu Aby nasz program był intuicyjny i pozwał if (!item->isOpen ()){
Gdy klikniemy dwukrotnie w dowolnym łatwo dodawać pliki do obrazu ISO, musi item->setOpen (false);
miejscu okna naszej aplikacji (nie doty- zawierać listę katalogów i plików systemu. }
czy przycisków i innych kontrolek), zosta- Lista ta powinna być załadowana zaraz po else{
nie utworzony plik qtisomform.ui.h. Jego uruchomieniu programu. Zajmiemy się teraz item->setOpen (true);
wygenerowanie jest również możliwe po stworzeniem takiej właśnie funkcjonalności. }
wybraniu kombinacji klawiszy [Ctrl]+[E]. W W pierwszym etapie dodamy plik }
oknie, które wówczas się pojawi, będziemy nagłówkowy qdir.h na samej górze w edy-
www.lpmagazine.org 27
programowanie
Pętla for z Listingu 1 pozwala wycią- Można wykorzystać dowolne inne ikony,
Listing 4. Metoda łączona z sygnałem
gnąć pojedyncze nazwy z tak przygotowanej ale jeśli będą miały inne nazwy i/lub będą
select()
listy nazw katalogów. Wykonujemy to przy znajdować się w innych katalogach, należy
void pomocy iteratorów (zaprezentowany przy- to uwzględnić w kodzie programu.
qtISOMForm::lv1selChanged kład użycia iteratora jest dość schematyczny
S
(QListViewItem * item){ dla całego Qt i na tym etapie Czytelnik nie Poruszanie po katalogach
QString tmpStr; musi wiedzieć więcej o tym zastosowaniu). Samo załadowanie głównego drzewa kata-
selectedItem = ""; Pierwszą czynnością, którą wykonujemy logów systemu to trochę za mało do spraw-
S
if ((item != 0) && w pętli, jest sprawdzenie, czy nazwa kata- nej obsługi programu. Dodamy teraz funk-
(item->isSelected ())){ logu jest różna od . oraz .. . Zostało ono cję umożliwiającą poruszanie się po tych
if (item->parent () != 0){ dodane, gdyż metoda entryList() zwraca katalogach. Mechanizm będzie polegał na
tmpStr = item->text (0); listę również z tymi wartościami. W naszym tym, że podwójne kliknięcie na wybranej
QListViewItem *tmpItem = item; przykładzie są niepotrzebne. pozycji z drzewa powoduje rozwinięcie
while (tmpItem->parent ()){ Gdy wiemy, że kolejna wartość listy wszystkich podkatalogów, które znajdu-
S
tmpItem = tmpItem-> nie jest . lub .. , możemy dodać tę pozy- ją się w wybranej pozycji, o ile takowe się
parent (); cję do naszego ListView o nazwie listDir. znajdują. W pierwszej kolejności dodamy
S
tmpStr.prepend Dodanie polega na stworzeniu nowego kod wczytujący listę podkatalogów według
(tmpItem->text (0) + "/"); obiektu QListViewItem, którego rodzicem pozycji startowej podanej w parametrze
} (parent) jest listDir. Korzystamy tutaj wywołania tej metody Listing 2.
} z konstruktora klasy QlistViewItem: Slot, który połączymy z sygnałem
else{ doubleClicked(QListViewItem * item),
S
tmpStr = item->text (0); QListViewItem ( QListView * parent, będzie wyglądał jak na Listingu 3 (po-
S
} QString label1, QString label2 = winniśmy go dodać przy pomocy okienka
S
selectedItem = "/" + tmpStr; QString::null, QString label3 = Slots, które zostało już omówione).
S
} QString::null, QString label4 = Kluczowym elementem tej funkcji
S
} QString::null, QString label5 = jest wczytanie nazw wszystkich rodzi-
S
QString::null, QString label6 = ców wybranej pozycji. W ten sposób uzy-
S
torze kodu. W tym pliku zostały zdefinio- QString::null, QString label7 = skujemy całą ścieżkę drzewa, którą wyko-
S
wane wszystkie funkcje umożliwiające QString::null, QString label8 = rzystujemy w funkcji wczytującej podka-
poruszanie się po katalogach systemu, jak QString::null ) talogi do kontrolki ListView. Od linii z if
i wczytywanie listy katalogów. Kod, który (!item->isOpen ()) zaczyna się kod odpo-
wczyta listę katalogów systemu do kontrol- Z zaprezentowanego powyżej konstrukto- wiedzialny za zwijanie lub rozwijanie list
ki ListView, będzie wyglądał tak jak na ra wynika, że musimy podać dwie pierw- w zależności od stanu tej pozycji (może
Listingu 1. sze wartości. Wszystkie inne nie są koniecz- być rozwinięta lub zwinięta).
Omawiany kod w pierwszej kolejno- ne, gdyż mają przypisane wartości domyśl- Teraz pozostaje jeszcze jedna czyn-
ści zmienia parametry kontrolki ListView. ne. W dalszej części tej pętli dodaliśmy do ność, czyli połączenie tego slota z sygna-
Opcja rootIsDecorated jest również każdej pozycji ikonkę, w zależności od łem podwójnego kliknięcia na kontrolce
dostępna w Qt Designerze. Ustawienie jej typu danej pozycji. Sprawdzanie odbywa listDir. Powinno ono wyglądać następu-
na wartość true sprawia, że katalogi są się przy pomocy klasy QFileInfo, która jąco: listDir generuje sygnał doubleClic-
wyświetlane w postaci drzewa, a nie listy w konstruktorze dostaje wartość QString ked(QListViewItem * item), który odbiera
(w przypadku ustawienia tej wartości na z nazwą i ścieżką do obiektu, którego wła- qtISOMForm i wykonuje lv1DbClicked(QLi-
false). Zaraz po ustawieniu parametrów ściwości chcemy sprawdzić. Typ danego stViewItem * item).
wywołujemy funkcję mainDirRead, która obiektu jest określany w metodach isDir(), Na tym etapie może skompilować pro-
wczytuje wszystkie katalogi, które znaj- isFile() oraz isReadable(). Ikonki użyte gram i przetestować jego działanie. Kom-
dują się w głównym drzewie /. Funkcja ta w programie są dołączone do kodów zró- pilacja programów tworzonych z wyko-
w pierwszej kolejności tworzy zmienną dłowych opisywanych w tym artykule. rzystaniem biblioteki Qt opiera się na uru-
dir, która jest klasą, której konstruktor
wygląda następująco:
Listing 5. Slot podłączany do sygnału clicked()
S
QDir ( const QString & path, const void qtISOMForm::addSelected (){
S
QString & nameFilter = QString:: if (selectedItem != ""){
S
null, int sortSpec = Name | listIso->insertItem (selectedItem, -1);
IgnoreCase, int filterSpec = All ) }
else{
S
W ten sposób tworzymy zmienną, dzięki QMessageBox::information (this, "Warning",
której możemy wczytać listę wszystkich pod- "You must select something!", QMessageBox::Ok);
katalogów do zmiennej QStringList (lista }
ciągów znaków), a następnie dowolnie ją }
przeszukać i wyciągnąć to, co nas interesuje.
28 luty 2006
programowanie
programowanie w Qt
addBtn. Sygnał clicked() tego przycisku
Listing 6. Metoda podłączana do
połączymy do slotu z Listingu 5.
sygnału clicked() przycisku remBtn
Metoda information, jak i cała klasa
void qtISOMForm::remSelected (){ QmessageBox, zostały zdefiniowane w pliku
S
int selNr, posCount = qmessagebox.h, więc musimy dodać ten plik
listIso->count (); na samej górze w naszym edytorze kodu.
bool hasSel = false; Możemy od razu dodać metodę remSelec-
for (selNr = 0; selNr ted(), którą połączymy z sygnałem clic-
S
< posCount; ++selNr){ ked() przycisku remBtn, a której zadaniem
S
if (listIso-> będzie usuwanie pozycji z listy ListBox
isSelected (selNr)){ Listing 6.
hasSel = true; Zasada działania funkcji z Listingu 6
Rysunek 4. Gotowy program podczas pracy
break; polega na odnalezieniu zaznaczonej pozy-
} cji w listIso. Każdy zaznaczony element " wyświetlaniu komunikatu z podsu-
} zostaje usuwany, a ponieważ tylko jeden mowaniem lub wyświetlane informa-
if (hasSel){ na raz może być zaznaczony, to funkcja ta cji o błędach.
listIso->removeItem (selNr); sprawnie wykonuje swoje zadanie.
} W chwili obecnej nasz program jest już QFileDialog
else{ w pełni przygotowany do tego, aby roz- Standardowe okienka do wyboru pliku do
S
QMessageBox::information począć prace nad nakładką na program otwarcia lub zapisania zostały zdefiniowa-
S
(this, "Warning", "You must Mkisofs. Schemat działania będzie polegał ne w klasie QFileDialog. Ona z kolei jest
select something!", QMessageBox::Ok); na: zdefiniowana w pliku qfiledialog.h, który to
} musimy dodać do naszego kodu. Do pobra-
} " pobraniu nazwy, pod którą mamy nia nazwy pliku wystarczy jedna metoda
zapisać ISO; z tej klasy. Metodę tę wywołamy w slocie
chomieniu dwóch programów: qmake " uruchomieniu programu Mkisofs połączonym do sygnału activated() menu
i make, które należy wywołać w katalogu z odpowiednimi parametrami; Save ISO. Składnia metody do pobierania
z kodem zródłowym programu: " pobieraniu komunikatów progra- nazwy pliku wygląda następująco:
mu Mkisofs o procentach i prezen-
S
qmake towanie tych danych za pomocą QString sIso = QFileDialog::
S
make QprogressDialog; getSaveFileName(
./qtiso
Listing 7. Metoda do wywołania programu Mkisofs
Kopiowanie nazwy
void qtISOMForm::doProc (QString outFile){
wybranego katalogu/pliku
QProcess *proc = new QProcess (this);
Zanim przejdziemy do uruchamiania
proc->addArgument ("mkisofs");
mkisofs i tworzenia samego obrazu, musi-
proc->addArgument ("-r");
my zebrać odpowiednie argumenty i gdzieś
proc->addArgument ("-o");
je zapisać. W naszym programie spis pli-
proc->addArgument (outFile + ".iso");
ków/katalogów, które będą dodane do
int itemCount = listIso->count ();
ISO, znajduje się w kontrolce listIso (List-
for (int i = 0; i < itemCount; ++i){
Box). Dodawanie wybranej nazwy do tego
proc->addArgument (listIso->text (i));
spisu będzie realizowane przez przycisk
}
addBtn. Sama nazwa będzie jednak zapa-
if (!proc->start ()){
miętywana już podczas wskazania jej np.
S
QMessageBox::information (this, "Warning",
myszą. Jest to w naszym przypadku reali-
"Can't execute process", QMessageBox::Ok);
zowane sygnałem select(), który połączy-
}
my z metodą z Listingu 4.
}
W omawianym kodzie występuje
zmienna selectedItem. Jest to zmienna
typu Qstring, która została zadeklaro-
Listing 8. Funkcja zapisu obrazu ISO
wana w Class variables->protected na za-
kładce Members w programie Qt Desig-
void qtISOMForm::saveIso (){
ner. S
QString sIso = QFileDialog::getSaveFileName ("/home", "ISO Images
Zadaniem slotu lv1selChanged jest za-
(*.iso)", this, "save file dialog", "Choose a filename to save under");
pisanie tekstu z aktualnie wskazanej pozy-
if (!sIso.isNull ())
cji listDir (item) do zmiennej selectedI-
doProc (sIso);
tem. To właśnie zawartość tej zmiennej trafi
}
do listy listIso po wciśnięciu przycisku
www.lpmagazine.org 29
programowanie
przypadku, musimy dodać plik nagłówko-
Listing 9. Metody obsługi programu Mkisofs
wy qprocess.h.
Do wywołania programu Mkisofs
void qtISOMForm::cancel (){
stworzymy sobie osobną metodę, która
proc->kill ();
będzie wyglądała tak jak na Listingu 7.
}
W zaprezentowanym przykładzie two-
void qtISOMForm::readFromStdout (){
rzymy klasę QProcess. Za pomocą metody
qDebug (proc->readStdout ());
addArgument dodajemy wszystkie części
}
składowe polecenia Mkisofs, łącznie ze
void qtISOMForm::readFromStderr (){
spisem plików, które mają być dodane do
QString err1 = proc->readStderr ();
obrazu, które wcześniej załadowaliśmy do
qDebug (err1);
ListView listIso. Wywołujemy tam także
if (err1.find ("estimate finish", 0) != -1){
metodę start, która probuje uruchomić
QStringList lst (QStringList::split (" ", err1));
proces. Gdy to się nie udaje, wyświetlane
QStringList lst2 (QStringList::split (".", lst[0]));
jest okienko informacyjne z komunikatem.
progressInt = lst2[0].toInt ();
Metodę tę wywołamy w slocie podpię-
progress->setProgress (progressInt);
tym do menu Save ISO, więc cała funkcja
}
zapisu obrazu będzie wyglądać tak jak na
if (err1.find ("Invalid", 0) != -1){
Listingu 8.
progress->close ();
S Jak widać, wywołujemy metodę doProc()
QMessageBox::critical (this, "Warning",
z parametrem, który odpowiada nazwie
"ISO wasn't created! \n" + err1);
pliku pobranej z okienka dialogowego
}
SaveFileDialog.
if (err1.find ("extents written", 0) != -1){
progress->close ();
S Rozbudowa programu
QMessageBox::information (this, "Information",
QProgressDialog
"ISO was created! \n" + err1, QMessageBox::Ok);
Zaprezentowane wcześniej rozwiązanie, tzn.
}
uruchamianie zewnętrznych procesów,
}
w większości przypadków (np. uruchomie-
nie strony internetowej programu) wystar-
Listing 10. Funkcja doProc() po połączeniu sygnałów z funkcjami cza. Dodamy teraz funkcjonalność, która
została przewidziana w 3 i 4 punkcie sche-
void qtISOMForm::doProc (QString outFile){
matu działania naszego programu. Będzie
S
progress = new QProgressDialog ("Creating iso...", "Abort..", 100,
to wymagało zmodyfikowania funkcji
this, "progress", TRUE);
doProc(), ale pierwszym krokiem będzie do-
connect (progress, SIGNAL (canceled ()), this, SLOT (cancel ()));
danie pliku nagłówkowego qprogessdialog.h.
proc = new QProcess (this);
Ponieważ z obiektu proc, a także obiek-
proc->addArgument ("mkisofs");
tu progress (QProgressDialog) będziemy
proc->addArgument ("-r");
korzystać w kilku metodach, zadeklaruje-
proc->addArgument ("-o");
my te obiekty zaraz pod spisem plików
proc->addArgument (outFile + ".iso");
nagłówkowych w edytorze kodu:
int itemCount = listIso->count ();
for (int i = 0; i < itemCount; ++i){
QProcess *proc;
proc->addArgument (listIso->text (i));
QProgressDialog *progress;
}
connect (proc, SIGNAL (readyReadStdout ()), this, SLOT (readFromStdout ()));
Dodamy teraz trzy metody, które w kolej-
connect (proc, SIGNAL (readyReadStderr ()), this, SLOT (readFromStderr ()));
ności: pozwolą przerwać tworzenie obrazu,
progress->setProgress (0);
wyświetlą na ekranie (w obecnej wersji pro-
if (!proc->start ()){
gramu na konsoli, o ile program będzie
QMessageBox::information (this, "Warning", "Can't execute process.
odpalony z konsoli) informacje wysyła-
Check if you have mkisofs inst alled correctly.", QMessageBox::Ok);
ne przez program Mkisofs na stdout oraz
}
wyświetlą i przetworzą wszystkie infor-
}
macje wysyłane przez program mkisofs na
stderr. Znajdują się one na Listingu 9.
S
"/home", QProcess Pierwsza funkcja wywołuje jedynie
S
"ISO Images (*.iso)", Wszystkie funkcje, a także cała obsługa metodę kill() klasy QProcess, która koń-
S
this, zewnętrznych poleceń, którą teraz będzie- czy działanie procesu uruchomionego przez
S
"save file dialog", my wykorzystywać, znajduje się w klasie metodę start(). W drugiej funkcji wypi-
"Choose a filename to save under" ); QProcess. Analogicznie do poprzedniego sujemy na ekran konsoli wszystkie infor-
30 luty 2006
programowanie
programowanie w Qt
macje, które zostaną wysłane na stdout. Total directory bytes: 1908736 z odpowiadającymi im slotami. Opis tych
Warto w tym miejscu nadmienić, że Mki- Path table size(bytes): 10226 funkcji i sygnałów znajduje się w doku-
sofs w wersji użytej podczas pisania tego Max brk space used 4c0000 mentacji do Qt, którą Czytelnik ma na
artykułu (mkisofs 2.01a34-unofficial-iconv) 149175 extents written (291 MB) dysku, a która jest dostępna po urucho-
nie wysyła informacji na stdout, a zamiast mieniu programu Qt Assistant.
tego korzysta z stderr. Z tego powodu W funkcji readFromStderr() odnajduje-
właśnie ta funkcja została bardziej rozbu- my pewne ciągi wyrazów i na ich podsta- Podsumowanie
dowana. wie wykonujemy takie operacje, jak pobra- W ten oto sposób przebrnęliśmy przez cały
W trzeciej funkcji widać, że właśnie tu nie informacji o postępie, czy też infor- proces tworzenia aplikacji z wykorzysta-
analizujemy wynik tego, co Mkisofs zwraca macji o wynikach. Zaprezentowane w tej niem biblioteki Qt. Omówione tutaj zasto-
podczas pracy. Gdy uruchomimy ten pro- funkcji mechanizmy są niczym innym jak sowanie jest z pewnością najprostszym spo-
gram normalnie (np. poleceniem mkisofs tylko operacjami na stringach. Zmienna sobem na wykonanie takiej aplikacji. Nie
-r -o test.iso /home/user/docs), na progressInt została dodana podobnie jak wymaga specjalistycznej wiedzy, jak i zna-
ekranie powinno pokazać się mniej więcej seletedItem na zakładce Members w pro- jomości niuansów programowania w syste-
coś takiego: gramie Qt Designer. mie Linux.
Wszystkie trzy funkcje musza zostać
3.35% done, estimate finish Sun Nov 27 połączone z odpowiednimi sygnałami, aby
W Internecie:
21:25:04 2005 funkcjonowały prawidłowo. Ponieważ
10.06% done, estimate finish Sun Nov obiekty Qprocess, jak i QprogressDialog,
" Strona producenta biblioteki Qt:
27 21:25:14 2005 nie są elementami GUI, łączenie ich sygna-
http://www.trolltech.com/
[...] łów z funkcjami musimy dokonać w
" Alternatywny tutorial do Qt:
93.86% done, estimate finish Sun Nov kodzie aplikacji, a dokładniej w funkcji
http://www.digitalfanatics.org/projects/
27 21:26:05 2005 doProc(). Po modyfikacji funkcja ta będzie
qt_tutorial/
97.21% done, estimate finish Sun Nov wyglądała jak na Listingu 10.
" Kurs programowania w Qt po polsku:
27 21:26:05 2005 Jak widać, połączyliśmy sygnał can-
http://free.of.pl/q/qtmoux/qt3/index.php
Total translation table size: 0 celed() klasy QProgressDialog ze slotem
" Forum poświęcone Qt:
Total rockridge attributes bytes: cancel() omówionym wcześniej, a także
http://www.qtforum.org/
608404 readyReadStdout() i readyReadStderr()
R E K L A M A
www.lpmagazine.org 31
Wyszukiwarka
Podobne podstrony:
2006 02 Tworzenie aplikacji dla Sony PlayStation Portable [Programowanie]
2007 02 Programowanie równoległe z Qt [Programowanie]
2002 02 Qt Creating Interfaces
flex pierwsza aplikacja we flex
2006 02 Zespol bolesnego barku hemiplegika
SIMR MAT1 EGZ 2006 02 08a rozw
3 Pierwsza aplikacja
SIMR MAT1 EGZ 2006 02 01b rozw
2006 02 Wyposażamy od A do Z
2006 02 Czy udar mózgu można mierzyć
Pierwsza aplikacja we Flex
2001 12 Geometry Classes Under Qt Programming
2006 02 06 Egzamin
2006 02 Wygodnie mieszkać aktywnie żyć
Facebook tworzenie aplikacji pierwsza aplikacja
2006 02 Private Roads Test An Easy Vpn with Ssl Explorer
2006 02 Podstawy farmakologicznego leczenia spastyczności
więcej podobnych podstron