2.1. Adresy (Qt5)

Niniejszy scenariusz pokazuje, jak zacząć programowanie z wykorzystaniem biblioteki Qt w wersji 5 przy użyciu dedykowanego środowiska IDE Qt Creator. Celem jest stworzenie prostej 1-okienkowej książki adresowej, w której można dodawać dane adresowe powiązane z określoną nazwą, np. imieniem i nazwiskiem.

2.1.1. Nowy projekt

Po uruchomieniu aplikacji Qt Creator wybieramy przycisk „New Project”, który uruchamia kreatora aplikacji.

W pierwszym oknie „Applications” i „Qt Widget Applications”, co oznacza, że chcemy utworzyć program z interfejsem graficznym oparty na klasie QWidget. W następnym oknie podajemy nazwę projektu, np, „adresy”, oraz wskazujemy ścieżkę do katalogu, w którym będą zapisywane pliki wchodzące w skład projektu. W następnym oknie wybieramy tzw. „kit”, czyli zestaw definiujący docelowe środowisko, kompilator itp. ustawienia. Dostępne zestawy muszą być wcześniej określone w ustawieniach Qt Creatora

Kolejne okno pozwala definiować nazwę klasy głównej i klasę podstawową, podajemy „adresy” i wybieramy „QWidget”. W następnym ostatnim oknie niczego nie zmieniamy, kończymy kliknięciem przycisku „Finish”.

Efektem działania kreatora będzie utworzenie następujących plików:

1) adresy.h - plik nagłówkowy, tutaj będziemy deklarować wszystkie używane w programie obiekty (elementy interfejsu), a także publiczne sloty, czyli funkcje powiązanie z określonymi sygnałami (zdarzeniami).

2) adresy.cpp - plik źródłowy, tu znajdzie się kod tworzący obiekty interfejsu, łączący sygnały ze slotami, a wreszcie implementacja slotów.

3) main.cpp - plik źródłowy, w którym tworzona i uruchamiana jest instancja naszej aplikacji.

4) adresy.ui - jak wskazuje rozszerzenie („ui” - ang. user interface), plik zawierać będzie opis graficznego interfejsu aplikacji zapisany za pomocą znaczników XML.

2.1.2. Tworzenie interfejsu

Zaczniemy od utworzenia głównego okna naszej aplikacji. W tym celu dwa razy klikamy plik adresy.ui i przechodzimy do tworzenia formularza.

Na początku klikamy obiekt „Grid Layout” z kategorii „Layouts” i rysujemy prostokąt na formularzu tak, aby nie wypełniał go w całości. Dodana kontrolka umożliwia porządkowanie innych elementów tworzących interfejs w prostokątnej siatce. Dalej dodamy dwie etykiety, czyli obiekty „Label” z kategorii „Display Widgets”. Staramy się je umieścić jedna nad drugą w dodanej przed chwilą siatce.

Wskazówka

Po wybraniu obiektu i najechaniu na Grid Layout należy obserwować niebieskie podświetlenia, które pojawiają się w pionie i poziomie, wskazują one, gdzie umieszczony zostanie dodawany obiekt.

Po dwukrotnym kliknięciu na dodane etykiety możemy zmienić treść przez nie wyświetlaną. Modyfikujemy w ten sposób właściwość text danego obiektu. Etykieta górna powinna zawierać tekst „Nazwa”, dolna - „Adresy”.

Informacja

Lista wszystkich obiektów wyświetlana jest po prawej stronie na górze w oknie Hierarchia obiektów. W kolumnie Obiekt widzimy tam nazwy dodanych obiektów, a w kolumnie Klasa nazwy klas, które reprezentują. Po wskazaniu myszą dowolnego obiektu możemy edytować wszystkie jego właściwości poniżej. Np. nazwę obiektu zmienimy w polu objectName.

Nazwę etykiety górnej ustalamy na „nazwaLbl”, dolnej - na „adresyLbl”.

Wskazówka

Konwencji nazywania obiektów jest wiele, ważne żeby konsekwentnie trzymać się wybranej. Tutaj proponujemy uwzględnianie w nazwie typu obiektu przy użyciu skrótu pisanego z dużej litery, np. „nazwaLbl”.

Po prawej stronie etykiety „Nazwa” dodajemy kontrolkę Line Edit z grupy Input Widgets o nazwie „nazwaLine”. Poniżej, czyli w drugiej kolumnie, tworzymy obiekt Text Edit z tej samej grupy, co poprzedni o nazwie „adresText”. Powinniśmy uzyskać poniższy układ:

../../_images/qt5_08.png

Czas na dodanie przycisków pozwalających inicjować działanie aplikacji. Dodajemy więc 5 przycisków PushButton z kategorii Buttons po prawej stronie i poza(!) obiektem GridLayouts jeden pod drugim. Na samym dole umieszczamy kontrolkę Vertical Spacer z kategorii Spacers. Następnie zaznaczamy wszystkie dodane obiekty, obrysowując je myszką, i klikamy ikonę Rzmieść w pionie (CTRL+L) na pasku narzędziowym. Teraz stworzoną grupę przeciągamy na siatkę jako 3. kolumnę.

Musimy zmienić nazwy i tekst dodanych przycisków. Od góry ustawiamy kolejne właściwości (nazwa/tekst): „dodajBtn/Dodaj”, „zapiszBtn/Zapisz”, „anulujBtn/Anuluj”, „edytujBtn/Edytuj”, „usunBtn/Usuń”. W efekcie powinniśmy uzyskać następującą formatkę:

../../_images/qt5_09.png

Musimy dodać jeszcze 3 przyciski pozwalające na nawigację między adresami i wyjście z programu. Poniżej obiektu siatki umieszczamy więc 2 przyciski (PushButton), zaznaczamy je i klikamy ikonę Rozmieść poziomo w splitterze, następnie przeciągamy grupę na dół 2. kolumny siatki. Na koniec dodajemy jeszcze jeden przycisk na dole 3. kolumny. Dodanym obiektom zmieniamy właściwości (nazwa/tekst): „poprzBtn/Porzedni”, „nastBtn/Następny”, „koniecBtn/Koniec”.

Na koniec zaznaczamy formularz główny, na którym znajdują się wszystkie elementy interfejsu i klikamy przycisk Rozmieść w siatce (CTRL+G). Dzięki temu kontrolki będą skalowane wraz ze zmianą rozmiaru okna.

W sumie uzyskujemy poniższy projekt:

../../_images/qt5_10.png

Możemy uruchomić naszą aplikację, wybierając Budowanie/Uruchom (CTRL+R) lub klikając trzecią od dołu ikonę zielonego trójkąta w lewej kolumnie Qt Creatora. Powinniśmy zobaczyć podobne do poniższego okno:

../../_images/qt5_11.png

2.1.3. Deklaracje i implementacje

Po dodaniu elementów interfejsu musimy zadeklarować zmienne, za pomocą których będziemy mogli nimi manipulować. Przechodzimy do pliku adresy.h i wprowadzamy poniższe zmiany:

adresy.h nr
 1#ifndef ADRESY_H
 2#define ADRESY_H
 3
 4#include <QWidget>
 5#include <QLineEdit>
 6#include <QTextEdit>
 7#include <QPushButton>
 8#include <QTextCodec>
 9
10namespace Ui {
11class adresy;
12}
13
14class adresy : public QWidget
15{
16    Q_OBJECT
17
18public:
19    explicit adresy(QWidget *parent = 0);
20    ~adresy();
21
22private:
23    Ui::adresy *ui;
24    QPushButton *dodajBtn;
25    QPushButton *zapiszBtn;
26    QPushButton *anulujBtn;
27    QPushButton *poprzBtn;
28    QPushButton *nastBtn;
29    QPushButton *edytujBtn;
30    QPushButton *usunBtn;
31    QPushButton *koniecBtn;
32    QLineEdit *nazwaLine;
33    QTextEdit *adresText;
34};
35
36#endif // ADRESY_H

Na początku musimy zaimportować klasy, z których skorzystaliśmy przy budowie interfejsu. Najważniejszą jest klasa podstawowa wszystkich elementów interfejsu, czyli QWidget. Kolejne trzy odpowiadają wykorzystanym przez nas kontrolkom edycyjnym i przyciskom. Dodatkowa klasa QTextCodec pozwoli poprawnie wyświetlać polskie znaki. W wewnątrz naszej klasy głównej, której deklaracja rozpoczyna się w linii 14., deklarujemy prywatne (private) właściwości, których nazwy odpowiadają nazwom wcześniej dodanych elementów interfejsu graficznego. Formalnie każda zmienna jest wskaźnikiem do obiektu odpowiedniego typu.

W pliku adresy.cpp korzystamy ze zadekarowanych zmiennych, aby ustawić początkowe właściwości obiektów składających się na interfejs użytkownika.

adresy.cpp nr
 1#include "adresy.h"
 2#include "ui_adresy.h"
 3
 4adresy::adresy(QWidget *parent) :
 5    QWidget(parent),
 6    ui(new Ui::adresy)
 7{
 8    ui->setupUi(this);
 9
10    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
11
12    nazwaLine = new QLineEdit;
13    nazwaLine = ui->nazwaLine;
14    nazwaLine->setReadOnly(true);
15
16    adresText = new QTextEdit;
17    adresText = ui->adresText;
18    adresText->setReadOnly(true);
19
20    dodajBtn = new QPushButton;
21    dodajBtn = ui->dodajBtn;
22
23    zapiszBtn = new QPushButton;
24    zapiszBtn = ui->zapiszBtn;
25    zapiszBtn->hide();
26
27    anulujBtn = new QPushButton;
28    anulujBtn = ui->anulujBtn;
29    anulujBtn->hide();
30
31    nastBtn = new QPushButton;
32    nastBtn = ui->nastBtn;
33    nastBtn->setEnabled(false);
34
35    poprzBtn = new QPushButton;
36    poprzBtn = ui->poprzBtn;
37    poprzBtn->setEnabled(false);
38
39    edytujBtn = new QPushButton;
40    edytujBtn = ui->edytujBtn;
41    edytujBtn->setEnabled(false);
42
43    usunBtn = new QPushButton;
44    usunBtn = ui->usunBtn;
45    usunBtn->setEnabled(false);
46
47    koniecBtn = new QPushButton;
48    koniecBtn = ui->koniecBtn;
49    koniecBtn->setEnabled(true);
50
51    setWindowTitle(trUtf8("Prosta książka adresowa"));
52}
53
54adresy::~adresy()
55{
56    delete ui;
57}

W obrębie konstruktora głównej klasy naszej aplikacji o nazwie adresy, którego definicja rozpoczyna się w linii 4., tworzymy instancje klas użytych w interfejsie graficznym. Do zmiennych zadeklarownych w pliku adresy.h przypisujemy obiekty utworzone za pomocą operatora new, a następnie definiujemy ich początkowe właściwości.

Konstruktorowi odpowiada zawzwyczaj destruktur, a więc działanie, które usuwa stworzony obiekt, w tym wypadku interfejs użytkownika: adresy::~adresy().

Aby określić stan elementów interfejsu wykorzystujemy odpowiednie właściwości i metody reprezentujących je obiektów. Np. właściwość setReadOnly(true) blokuje edycję danego elementu, a właściwość setEnabled(false) uniemożliwia kliknięcie danego przycisku. Metoda hide() ukrywa obiekt.

Instrukcja QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")) określa kodowanie komunikatów w standardzie „UTF-8” używanych w aplikacji, które wprowadzane są dalej za pomocą funkcji trUtf8(). Tak dzieje się np. podczas określania tytułu okna w wywołaniu setWindowTitle().

Wskazówka

W środowisku MS Windows kodowanie powinno zostać ustawione na Windows-1250.

Dzięki powyższym uzupełnieniom po uruchomieniu aplikacji pola nazwy i adresu będą nieaktywne, będziemy mogli natomiast użyć przycisków Dodaj, aby utworzyć nowy wpis, lub Koniec, aby zakończyć aplikację.

../../_images/qt5_12.png

2.1.4. Sygnały i sloty

Działanie aplikacji z interfejsem graficznym polega w uproszczeniu na reagowaniu na działania użytkownika, takie jak np. kliknięcie, naciśnięcie klawisza, przeciągnięcie itp. Wszystkie zdarzenia generowane z poziomu interfejsu użytkownika w terminologii biblioteki Qt emitują tzw. sygnały. Programista decyduje o tym, które z nich i jak są obsługiwane, definiując tzw. sloty, czyli funkcje powiązane z określonymi zdarzeniami. Mechanizm sygnałów i slotów umożliwia komunikację między obiektami aplikacji.

Każda z funkcji obsługujących zdarzenia musi zostać najpierw zadeklarowana w pliku adresy.h w sekcji public slots:, ich implementację musimy dopisać później do pliku adresy.cpp.

adresy.h nr
18public:
19    explicit adresy(QWidget *parent = 0);
20    ~adresy();
21
22    enum Tryb { nawigujT, dodajT, edytujT };
23
24public slots:
25    void dodajKontakt();
26    void koniec();
27
28private:
29    Ui::adresy *ui;
30    QPushButton *dodajBtn;
31    QPushButton *zapiszBtn;
32    QPushButton *anulujBtn;
33    QPushButton *poprzBtn;
34    QPushButton *nastBtn;
35    QPushButton *edytujBtn;
36    QPushButton *usunBtn;
37    QPushButton *koniecBtn;
38    QLineEdit *nazwaLine;
39    QTextEdit *adresText;
40
41    Tryb aktTryb;
42    void aktGui(Tryb tryb);
43    QString staraNazwa;
44    QString staryAdres;
45    QMap<QString,QString> kontakty;
46
47};
48
49#endif // ADRESY_H

Oprócz deklaracji slotów w liniach 24-26 dopisujemy deklaracje kilku potrzebnych zmiennych. Definiujemy więc typ wyliczeniowy Tryb, z którego korzystamy deklarując zmienną aktTryb oraz prywatną funkcję pomocniczą aktGui. Posłużą one do określania 1 z 3 stanów działania aplikacji, takich jak: przeglądanie wpisów, dodawanie i ich edycja.

Dalej dopisujemy deklaracje zmiennych pomocniczych staraNazwa i staryAdres. Korzystamy tu z typu QString oznaczającego dane tekstowe. Na końcu deklarujemy specjalną zmienną kontakty, która posłuży do przechowywania nazw i skojarzonych z nimi adresów w postaci słownika typu QMap. Poszczególne elementy takiej listy mają postać skojarzonych ze sobą par (klucz, wartość).

adresy.cpp nr
 53    connect(dodajBtn, SIGNAL(clicked()), this, SLOT(dodajKontakt()));
 54    connect(koniecBtn,SIGNAL(clicked()),this, SLOT(koniec()));
 55}
 56
 57adresy::~adresy()
 58{
 59    delete ui;
 60}
 61
 62void adresy::dodajKontakt() {
 63    staraNazwa = nazwaLine->text();
 64    staryAdres = adresText->toPlainText();
 65
 66    nazwaLine->clear();
 67    adresText->clear();
 68
 69    aktGui(dodajT);
 70}
 71
 72void adresy::aktGui(Tryb tryb) {
 73    aktTryb=tryb;
 74    switch (aktTryb) {
 75        case dodajT:
 76        case edytujT:
 77            nazwaLine->setReadOnly(false);
 78            nazwaLine->setFocus(Qt::OtherFocusReason);
 79            adresText->setReadOnly(false);
 80
 81            dodajBtn->setEnabled(false);
 82            edytujBtn->setEnabled(false);
 83            usunBtn->setEnabled(false);
 84
 85            zapiszBtn->show();
 86            anulujBtn->show();
 87
 88            nastBtn->setEnabled(false);
 89            poprzBtn->setEnabled(false);
 90        break;
 91        case nawigujT:
 92            if (kontakty.isEmpty()) {
 93                nazwaLine->clear();
 94                adresText->clear();
 95            }
 96            nazwaLine->setReadOnly(true);
 97            adresText->setReadOnly(true);
 98            dodajBtn->setEnabled(true);
 99
100            int ile=kontakty.size();
101            edytujBtn->setEnabled(ile >= 1);
102            usunBtn->setEnabled(ile >=1 );
103            nastBtn->setEnabled(ile > 1);
104            poprzBtn->setEnabled(ile > 1);
105
106            zapiszBtn->hide();
107            anulujBtn->hide();
108        break;
109    }
110}
111
112void adresy::koniec() {
113    adresy::close();
114}

Powiązania między sygnałami i slotami ustalamy w pliku adresy.cpp za pomocą poleceń typu: connect(dodajBtn, SIGNAL(clicked()), this, SLOT(dodajKontakt()));. Funkcja conect() jako pierwszy argument wymaga zmiennej wskazującej obiekt, który emituje sygnał określony w 2. argumencie (np. SIGNAL(clicked()), czyli kliknięcie), 3. argument określa obiekt, który zostaje powiadomiony o zdarzeniu, w ostatnim argumencie podajemy funkcję, która ma zostać wykonana (SLOT(dodajKontakt())).

Jak widać powyżej, na końcu konstruktora naszej klasy adresy wiążemy kliknięcia przycisków dodajBtn i koniecBtn z funkcjami dodajKontakt() i koniec().

Funkcja dodajKontakt() przygotowuje aplikację do przełączenia w stan dodawania nowych danych. W tym celu najpierw zapamiętujemy dotychczasową nazwę i adres, a następnie wywołujemy funkcję pomocniczą z argumentem typu Tryb oznaczającym wymagany stan aplikacji: aktGui(dodajT).

Działanie funkcji aktGui(), obsługującej stany aplikacji, polega na uaktywnianiu lub wyłączaniu określonych elementów interfejsu w zależności od przeprowadzanej przez użytkownika czynności. Np. w trybie dodawania i edycji odblokowujemy możliwość wprowadzania tekstu w polach nazwy (nazwaLine->setReadOnly(false);) i adresu (adresText->setReadOnly(false);), pokazujemy przyciski pozwlające na zapis lub anulowanie wywołując metodę show(). Wyłączamy również nawigację, blokując odpowiednie przyciski (metoda setEnabled(false)). Po wejściu w tryb nawigacji czyścimy (clear()) zawartość pól nazwy i adresu, o ile lista kontaktów jest pusta (if (kontakty.isEmpty())). Następnie uaktywniamy przyciski edycji, usuwania i przeglądania, jeżeli mamy jakieś kontakty. Ilość kontaktów zapisujemy wcześniej w osobnej zmiennej (int ile=kontakty.size();). Na koniec przyciski zapisu i anulowania zostają zablokowane.

Slot koniec() wywoływany jest po kliknięciu przycisku Koniec i powoduje zamknięcie aplikacji przy użyciu metody close(). Wywołuje ona m.in. destruktor klasy, co powoduje – w naszym przypadku – usunięcie instancji obiektu interfejsu graficznego (delete ui;).

2.1.5. Dodawanie adresów

Pora zaimplementować obsługę trybu dodawania danych adresowych. Najpierw do pliku nagłówkowego dopisujemy deklaracje odpowiednich slotów:

adresy.h nr
25public slots:
26    void dodajKontakt();
27    void koniec();
28    void zapiszKontakt();
29    void anuluj();

Musimy też na początku pliku dodać import klasy QMessageBox pozwalającej wyświetlać informacje użytkownikowi.

Następnie przechodzimy do pliku adresy.cpp, w którym trzeba powiązać sloty zapiszKontakt() i anuluj() ze zdarzeniem kliknięcia przycisków zapiszBtn i anulujBtn. Zadanie to proponujemy wykonać samodzielnie :-).

Na końcu pliku musimy dopisać definicje powiązanych funkcji:

adresy.cpp nr
118void adresy::zapiszKontakt() {
119    QString nazwa = nazwaLine->text();
120    QString adres = adresText->toPlainText();
121
122    if (nazwa == "" || adres == "") {
123        QMessageBox::information(this, trUtf8("Puste pole"),trUtf8("Proszę wpisać nazwę i adres."));
124        return;
125    }
126
127    if (aktTryb == dodajT) {
128        if (!kontakty.contains(nazwa)) {
129            kontakty.insert(nazwa, adres);
130            QMessageBox::information(this, trUtf8("Dodano wpis"),
131                                     trUtf8("Kontakt \"%1\" dodano do książki adresowej.").arg(nazwa));
132        } else {
133            QMessageBox::information(this, trUtf8("Nie dodano wpisu"),
134                                     trUtf8("Przykro, ale kontakt \"%1\" jest już w książce adresowej.").arg(nazwa));
135        }
136    }
137
138    aktGui(nawigujT);
139}
140
141void adresy::anuluj() {
142    nazwaLine->setText(staraNazwa);
143    adresText->setText(staryAdres);
144    aktGui(nawigujT);
145}

Funkcja zapiszKontakt() pobiera tekst wpisany w pola edycyjne za pomocą metod text() oraz toPlainText() i zapisuje je w zmiennych tekstowych. Następnie sprawdza, czy użytkownik wprowadził obydwie informacje. Jeżeli nie, wyświetla odpowiedni komunikat przy użyciu metody QMessageBox::information(). Pierwszy tekst, który przekazujemy do tej funkcji to tytuł okna dialogowego, drugi – właściwy komunikat. Następnie, jeżeli aplikacja jest w trybie dodawania, sprawdza, czy podana nazwa nie została zapisana wcześniej na liście kontakty. Jeśli nie (if (!kontakty.contains(nazwa))), dodaje nowe dane (kontakty.insert(nazwa, adres);) i wyświetla potwierdzenie. W przeciwnym razie informuje użytkownika o duplikacie. Na końcu aktywuje tryb nawigacji (aktGui(nawigujT);).

Jeżeli użytkownik rozmyśli się i kliknie odpowiedni przycisk, wywoływana jest funkcja anuluj(). Jak widać, przywraca ona w polach edycyjnych poprzednio wprowadzane dane i również aktywuje tryb nawigacji.

2.1.6. Tryb nawigacji

Obsługa nawigacji wymaga napisania funkcji obsługujących naciśnięcie przycisków Następny i Poprzedni, które stają się aktywne, jeżeli mamy więcej niż 1 dodany adres. Jak zwykle, zaczynamy od zadeklarowania publicznych slotów nast() i poprz() w pliku nagłówkowym. Dopisanie tych 2 linijek pozostawiamy do samodzielnego wykonania. Podobnie powiązanie zadeklarowanych slotów z sygnałami (kliknięciami) obiektów nastBtn i poprzBtn w konstruktorze klasy adresy.

Następnie dopisujemy implementację zadeklarowanych funkcji na końcu pliku adresy.cpp:

Na końcu pliku musimy dopisać definicje powiązanych funkcji:

adresy.cpp nr
149void adresy::nast() {
150    QString nazwa = nazwaLine->text();
151    QMap<QString, QString>::iterator i = kontakty.find(nazwa);
152    if (i != kontakty.end()) i++;
153    if (i == kontakty.end()) i = kontakty.begin();
154    nazwaLine->setText(i.key());
155    adresText->setText(i.value());
156}
157
158 void adresy::poprz() {
159    QString nazwa = nazwaLine->text();
160    QMap<QString, QString>::iterator i = kontakty.find(nazwa);
161    if (i == kontakty.begin()) i = kontakty.end();
162    i--;
163    nazwaLine->setText(i.key());
164    adresText->setText(i.value());
165 }

Wyświetlając kolejną parę powiązanych danych, tzn. nazwę i przypisany jej adres(y), musimy sprawdzić w fukcji nast(), czy mamy kolejny wpis, czy też aktualny jest ostatni. Wtedy należałoby wyświetlić wpis pierwszy. W tym celu pobieramy nazwę aktualnie wyświetlonego wpisu i tworzymy obiekt tzw. iteratora inicjowanego przez metodę find() i przypisanego do zmiennej i: QMap<QString, QString>::iterator i = kontakty.find(nazwa);. Iterator umożliwia łatwe poruszanie się po liście słowników zapisanych w zmiennej kontakty. Metoda i.key() zwraca nam klucz, a i.value() przypisaną mu wartość.

Jeżeli bieżący wpis nie jest ostatnim inkrementujemy wartość iteratora (if (i != kontakty.end()) i++;). W przeciwnym wypadku ustawiamy go na pierwszy wpis (i = kontakty.begin();); Na koniec pozostaje wczytanie nazwy (i.key()) i przypisanych jej danych (i.value()) do odpowiednich pól interfejsu.

Funkcja poprz() zaczyna się tak samo jak poprzednia, czyli od utworzenia iteratora wskazującego na bieżący wpis. Jeżeli jesteśmy na początku listy, ustawiamy iterator na element końcowy. Następnie przechodzimy do elementu końcowego (i--) i wyświetlamy odpowiednie dane.

Informacja

Metoda .end() klasy QMap zwraca iterator wskazujący na wirtualny (!) element po ostatnim elemencie listy. Dlatego, aby uzyskać dostęp do niego, musimy iterator dekrementować (i--).

2.1.7. Edycja i usuwanie

Do oprogramowania zostay jeszcze dwa przyciski: btnEdytuj, którego kliknięcie powinno wywołać funkcję edytujKontakt(), oraz btnUsun, który wywołuje funkcję usunKontakt(). Samodzielnie dopisujemy deklaracje funkcji do pliku nagłówkowego, a ich powiązania z sygnałami umieszczamy w pliku źródłowym.

Następnie implementujemy funkcje:

adresy.cpp nr
185void adresy::edytujKontakt() {
186    staraNazwa = nazwaLine->text();
187    staryAdres = adresText->toPlainText();
188    aktGui(edytujT);
189}
190
191void adresy::usunKontakt() {
192    QString nazwa = nazwaLine->text();
193    QString adres = adresText->toPlainText();
194
195    if (kontakty.contains(nazwa)) {
196        int button = QMessageBox::question(this,trUtf8("Potwierdź usunięcie"),
197                                           trUtf8("Czy na pewno usunąć kontakt \"%1\"?").arg(nazwa),
198                                           QMessageBox::Yes|QMessageBox::No);
199        if (button == QMessageBox::Yes) {
200            poprz();
201            kontakty.remove(nazwa);
202            QMessageBox::information(this,trUtf8("Usunięto"),
203                                 trUtf8("Usunięto kontakt \"%1\".").arg(nazwa));
204        }
205    }
206    aktGui(nawigujT);
207}

Przejście do trybu edycji, czyli działanie funkcji edytujKontak(), polega na zapisaniu aktualnie wyświetlanych danych (przydatne, jeżeli użytkownik anuluje zmiany) i uaktywnieniu trybu (aktGui(edytujT);), tzn. odblokowaniu pól tekstowych i odpowiednich przycisków.

Usuwanie kontaktów również jest proste. Na początku pobieramy nazwę i związany z nim adres(y). Metoda .contains(nazwa) pozwala sprawdzić, czy lista kontaktów zawiera słownik o podanym kluczu. Natępnie prosimy użytkownika o potwierdzenie operacji. Po jego uzyskaniu najpierw wyświetlamy w aplikacji dane poprzedniego wpisu dzięki wywołaniu zdefiniowanej wcześniej funkcji poprz(), później dopiero usuwamy wpis za pomocą metody .remove(nazwa) i wyświetlamy potwierdzenie. Na koniec aktywujemy tryb nawigacji.

2.1.7.1. Poćwicz sam

Spróbuj rozszerzyć napisaną aplikację o możliwość przechowywania danych w pliku lub w bazie na dysku.

2.1.8. Materiały

  1. Projekt Qt

  2. Biblioteka Qt 5

  3. Qt Creator

  4. Dokumentacja Qt 5

  5. Qt Developer Wiki (pl)


Utworzony:

2024-04-23 o 08:28 w Sphinx 7.3.7