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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef ADRESY_H
#define ADRESY_H

#include <QWidget>
#include <QLineEdit>
#include <QTextEdit>
#include <QPushButton>
#include <QTextCodec>

namespace Ui {
class adresy;
}

class adresy : public QWidget
{
    Q_OBJECT

public:
    explicit adresy(QWidget *parent = 0);
    ~adresy();

private:
    Ui::adresy *ui;
    QPushButton *dodajBtn;
    QPushButton *zapiszBtn;
    QPushButton *anulujBtn;
    QPushButton *poprzBtn;
    QPushButton *nastBtn;
    QPushButton *edytujBtn;
    QPushButton *usunBtn;
    QPushButton *koniecBtn;
    QLineEdit *nazwaLine;
    QTextEdit *adresText;
};

#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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "adresy.h"
#include "ui_adresy.h"

adresy::adresy(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::adresy)
{
    ui->setupUi(this);

    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

    nazwaLine = new QLineEdit;
    nazwaLine = ui->nazwaLine;
    nazwaLine->setReadOnly(true);

    adresText = new QTextEdit;
    adresText = ui->adresText;
    adresText->setReadOnly(true);

    dodajBtn = new QPushButton;
    dodajBtn = ui->dodajBtn;

    zapiszBtn = new QPushButton;
    zapiszBtn = ui->zapiszBtn;
    zapiszBtn->hide();

    anulujBtn = new QPushButton;
    anulujBtn = ui->anulujBtn;
    anulujBtn->hide();

    nastBtn = new QPushButton;
    nastBtn = ui->nastBtn;
    nastBtn->setEnabled(false);

    poprzBtn = new QPushButton;
    poprzBtn = ui->poprzBtn;
    poprzBtn->setEnabled(false);

    edytujBtn = new QPushButton;
    edytujBtn = ui->edytujBtn;
    edytujBtn->setEnabled(false);

    usunBtn = new QPushButton;
    usunBtn = ui->usunBtn;
    usunBtn->setEnabled(false);

    koniecBtn = new QPushButton;
    koniecBtn = ui->koniecBtn;
    koniecBtn->setEnabled(true);

    setWindowTitle(trUtf8("Prosta książka adresowa"));
}

adresy::~adresy()
{
    delete ui;
}

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
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public:
    explicit adresy(QWidget *parent = 0);
    ~adresy();

    enum Tryb { nawigujT, dodajT, edytujT };

public slots:
    void dodajKontakt();
    void koniec();

private:
    Ui::adresy *ui;
    QPushButton *dodajBtn;
    QPushButton *zapiszBtn;
    QPushButton *anulujBtn;
    QPushButton *poprzBtn;
    QPushButton *nastBtn;
    QPushButton *edytujBtn;
    QPushButton *usunBtn;
    QPushButton *koniecBtn;
    QLineEdit *nazwaLine;
    QTextEdit *adresText;

    Tryb aktTryb;
    void aktGui(Tryb tryb);
    QString staraNazwa;
    QString staryAdres;
    QMap<QString,QString> kontakty;

};

#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
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    connect(dodajBtn, SIGNAL(clicked()), this, SLOT(dodajKontakt()));
    connect(koniecBtn,SIGNAL(clicked()),this, SLOT(koniec()));
}

adresy::~adresy()
{
    delete ui;
}

void adresy::dodajKontakt() {
    staraNazwa = nazwaLine->text();
    staryAdres = adresText->toPlainText();

    nazwaLine->clear();
    adresText->clear();

    aktGui(dodajT);
}

void adresy::aktGui(Tryb tryb) {
    aktTryb=tryb;
    switch (aktTryb) {
        case dodajT:
        case edytujT:
            nazwaLine->setReadOnly(false);
            nazwaLine->setFocus(Qt::OtherFocusReason);
            adresText->setReadOnly(false);

            dodajBtn->setEnabled(false);
            edytujBtn->setEnabled(false);
            usunBtn->setEnabled(false);

            zapiszBtn->show();
            anulujBtn->show();

            nastBtn->setEnabled(false);
            poprzBtn->setEnabled(false);
        break;
        case nawigujT:
            if (kontakty.isEmpty()) {
                nazwaLine->clear();
                adresText->clear();
            }
            nazwaLine->setReadOnly(true);
            adresText->setReadOnly(true);
            dodajBtn->setEnabled(true);

            int ile=kontakty.size();
            edytujBtn->setEnabled(ile >= 1);
            usunBtn->setEnabled(ile >=1 );
            nastBtn->setEnabled(ile > 1);
            poprzBtn->setEnabled(ile > 1);

            zapiszBtn->hide();
            anulujBtn->hide();
        break;
    }
}

void adresy::koniec() {
    adresy::close();
}

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
25
26
27
28
29
public slots:
    void dodajKontakt();
    void koniec();
    void zapiszKontakt();
    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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
void adresy::zapiszKontakt() {
    QString nazwa = nazwaLine->text();
    QString adres = adresText->toPlainText();

    if (nazwa == "" || adres == "") {
        QMessageBox::information(this, trUtf8("Puste pole"),trUtf8("Proszę wpisać nazwę i adres."));
        return;
    }

    if (aktTryb == dodajT) {
        if (!kontakty.contains(nazwa)) {
            kontakty.insert(nazwa, adres);
            QMessageBox::information(this, trUtf8("Dodano wpis"),
                                     trUtf8("Kontakt \"%1\" dodano do książki adresowej.").arg(nazwa));
        } else {
            QMessageBox::information(this, trUtf8("Nie dodano wpisu"),
                                     trUtf8("Przykro, ale kontakt \"%1\" jest już w książce adresowej.").arg(nazwa));
        }
    }

    aktGui(nawigujT);
}

void adresy::anuluj() {
    nazwaLine->setText(staraNazwa);
    adresText->setText(staryAdres);
    aktGui(nawigujT);
}

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

 void adresy::poprz() {
    QString nazwa = nazwaLine->text();
    QMap<QString, QString>::iterator i = kontakty.find(nazwa);
    if (i == kontakty.begin()) i = kontakty.end();
    i--;
    nazwaLine->setText(i.key());
    adresText->setText(i.value());
 }

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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
void adresy::edytujKontakt() {
    staraNazwa = nazwaLine->text();
    staryAdres = adresText->toPlainText();
    aktGui(edytujT);
}

void adresy::usunKontakt() {
    QString nazwa = nazwaLine->text();
    QString adres = adresText->toPlainText();

    if (kontakty.contains(nazwa)) {
        int button = QMessageBox::question(this,trUtf8("Potwierdź usunięcie"),
                                           trUtf8("Czy na pewno usunąć kontakt \"%1\"?").arg(nazwa),
                                           QMessageBox::Yes|QMessageBox::No);
        if (button == QMessageBox::Yes) {
            poprz();
            kontakty.remove(nazwa);
            QMessageBox::information(this,trUtf8("Usunięto"),
                                 trUtf8("Usunięto kontakt \"%1\".").arg(nazwa));
        }
    }
    aktGui(nawigujT);
}

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)

2.1.8.1. Pojęcia

Qt
zestaw bibliotek programistycznych ułatwiających tworzenie aplikacji z interfejsem graficznym w językach C++, QML i Java.
plik nagłówkowy
w języku C/C++ plik z rozszerzeniem .h zawierający deklaracje używanych struktur danych, np. klas, zmiennych i funkcji. Implementacja klas i funkcji umieszczana jest w plikach źródłowych. Więcej o pliku żródłowym:
plik źródłowy
w języku C/C++ plik z rozszerzeniem .c/.cpp zawierający implementację zadeklarowanych typów złożonych (np. klas) i używanych funkcji, w tym funkcji głównej (main()).
Klasa
program komputerowy.
Obiekt
zestaw komponentów i bibliotek wykorzystywany do budowy aplikacji, przykładem jest biblioteka Pythona Flask.
public
operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim są dostępne z każdeg miejsca programu.
private
operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim są dostępne tylko w jej obrębie.
Qt Creator
wieloplatformowe środowisko IDE (zintegrowane środowisko programistyczne) dla aplikacji pisanych w językach C++, JavaScript i QML. Zawiera m.in. debugger i edytor GUI (graficznego interfejsu użytkownika).
sygnały
zdarzenia generowane za pomocą graficznego interfejsu użytkownika, takie jak kliknięcie elementu, edycja, przeciągnięcie itp.
sloty
funkcje przypisane sygnałom, definiują działania podejmowane w przypadku zastnienia danego zdarzenia.

Utworzony:2017-10-03 o 07:44 w Sphinx 1.4.5