Miał być w piątek, więc wrzucam dzisiaj.
Z tej części kursu poznamy klasę głównego okna w Qt, jak tworzyć własne widgety oraz na czym polega mechanizm sygnałów i slotów. Miłej lektury.
Główne okno w Qt reprezentuje klasa QMainWindow. Jest to chyba najczęściej dziedziczona klasa. Pozwala nam na umieszczanie menu oraz statusu. Najprostszy program z QMainWindow wygląda tak:
mainwindow.hpp:
1 2 3 4 5 6 7 8 9 10 11 12 | #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include <QMainWindow> class MainWindow : public QMainWindow { public: MainWindow(); }; #endif // MAINWINDOW_HPP |
W 4. linijce załączamy nagłówek klasy QMainWindow. W 6. definiujemy własną klasę MainWindow, która dziedziczy po QMainWindow. Natomiast w 9. definiujemy konstruktor naszej klasy, którzy przyda nam się później do tworzenia własnych menu i paska statusu. W ten oto sposób zrobiliśmy nasz pierwszy widget w Qt. :]
mainwindow.cpp:
1 2 3 4 5 | #include "mainwindow.hpp" MainWindow::MainWindow() { } |
main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <QApplication> #include <QMainWindow> #include "mainwindow.hpp" int main (int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); } |
W 9. linijce tworzymy obiekt klasy naszego okna, a w 11. wyświetlamy to okno. Po kompilacji i uruchomieniu pokazuje się nam okno. Gdy już się pozachwycamy jacy jesteśmy piękni, mądrzy i wspaniali możemy przejść do dalszej zabawy z naszym okienkiem.
Dołożymy przycisk, dzięki któremu (za pomocą sygnałów i slotów) będziemy mogli zamknąć okno (to dla tych, którzy mają problem w trafienie w ‘x’ na belce okna
). A wygląda to tak:
mainwindow.hpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include <QAainwindow> #include <QTextCodec> #include <QPushButton> class MainWindow : public QMainWindow { private: QPushButton *button; public: MainWindow(); }; #endif // MAINWINDOW_HPP |
W linii 5. i 6. dołączamy nagłówki, pierwszy odpowiada za kodowanie, drugi zawiera klasę przycisku. Natomiast w 11. tworzymy wskaźnik na obiekt klasy przycisku.
mainwindow.cpp:
1 2 3 4 5 6 7 8 9 | #include "mainwindow.hpp" MainWindow::MainWindow() { QTextCodec::setCodecForTr (QTextCodec::codecForName ("UTF-8")); button = new QPushButton (tr("&Wciśnij mnie ;)"), this); button->setGeometry(25, 15, 150, 75); } |
W konstruktorze (linia 5.) ustawiamy kodowania, dla tłumaczeń, na UTF-8, dzięki czemu będziemy widzieć polskie (i nie tylko) krzaczki. Jeżeli mimo ustawienia UTF-8 nadal masz dziwne znaki, to najwyższy czas, żebyś zaczął kodować swoje źródła w UTF-8.
Oprócz kodowania dla tłumaczeń (o których będzie innym razem), może również ustawić kodowania dla lokali (setCodecForLocale) oraz standardowych stringów (setCodecForCStrings). W linii 7. przypisujemy do naszego wskaźnika przycisku obiekt dynamiczny klasy QPushButton. Pierwszy argument jaki podajemy jest tekst który znajduje się na przycisku. & postawiony przed ‘W’ pozwala nam na wciskanie tego przycisku poprzez skrót Alt-w. Nasz napis przechodzi przez funkcję tr(). Służy ona do tłumaczenia interfejsu aplikacji na inne języki (jak tłumaczyć i wykorzystać do tego Qt Linguist napiszę kiedy indziej). Drugi argument, to wskaźnik na rodzica, na którym ma być wyświetlany nasz widget. Oprócz tego pozwala to, na pomijanie operatora delete, gdyż Qt sam usuwa z pamięci obiekty widgetów, które są “potomkami” (w sensie bycia umieszczonym na formatce rodzica, nie w sensie dziedziczenia) innych widgetów. W następnej linii ustawiamy wymiary i położenie naszego przycisku (kolejno: x, y, szerowość oraz wysokość). Po kompilacji i uruchomieniu pokazuje nam się okno z przyciskiem który możemy sobie poklikać.
Żeby dodać trochę więcej interakcji z programem, podepniemy sygnał kliknięcia przycisku do slotu zamknięcia aplikacji:
mainwindow.hpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include <QApplication> #include <QMainWindow> #include <QTextCodec> #include <QPushButton> class MainWindow : public QMainWindow { private: QPushButton *button; public: MainWindow(); }; #endif // MAINWINDOW_HPP |
W mainwindow.hpp dodajemy tylko nagłówek klasy QApplication.
mainwindow.cpp:
1 2 3 4 5 6 7 8 9 10 11 | #include "mainwindow.hpp" MainWindow::MainWindow() { QTextCodec::setCodecForTr (QTextCodec::codecForName ("UTF-8")); button = new QPushButton (tr("&Wciśnij mnie ;)"), this); button->setGeometry(25, 15, 150, 75); connect(button, SIGNAL(clicked()), qApp, SLOT(quit())); } |
W konstruktorze klasy MainWindow dodajemy połączenie między przyciskiem a naszą aplikacją. Przycisk wysyła sygnał kliknięcia do qApp (jest to makro, które reprezentuje obiekt naszej aplikacji), który to wywołuje metodę wyjścia z programu. Taką aplikację możemy już zamknąć przy pomocy naszego przycisku.
Teraz pokażę jak tworzyć własne sloty. Stworzymy dodatkowy przycisk i etykietę, którą będziemy zmieniali tym przyciskiem:
mainwindow.hpp:
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 | #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include <QApplication> #include <QMainWindow> #include <QTextCodec> #include <QPushButton> #include <QLabel> class MainWindow : public QMainWindow { Q_OBJECT private: QPushButton *button; QPushButton *butlab; QLabel *label; public: MainWindow(); private slots: void foo(); }; #endif // MAINWINDOW_HPP |
Nową rzeczą jest marko Q_OBJECT, które musi być załączone, w prywatnej sekcji, każdej klasy, która ma własne sygnały lub sloty (lub inne możliwości dostarczane przez meta-object system Qt). Taka klasa musi również dziedziczyć (bezpośrednio lub pośrednio) po klasie QObject. Deklarujemy również prywatny slot. W rzeczywistości jest to metoda, którą możemy normalnie wywoływać, bez łączenia z sygnałami. Jeden sygnał może być podpięty do wielu slotów, tak samo jak jeden slot może przyjmować wiele różnych sygnałów. Jedyne ograniczenie jest takie, że slot nie może przyjmować więcej argumentów niż ma ich sygnał (tworzeniem sygnałów zajmiemy się innym razem).
mainwindow.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "mainwindow.hpp" MainWindow::MainWindow() { QTextCodec::setCodecForTr(QTextCodec::codecForName ("UTF-8")); label = new QLabel(tr("Smutna etykieta :(", this); label->setGeometry(5, 5, 150, 30); button = new QPushButton(tr("&Wciśnij mnie ;)"), this); button->setGeometry(5, 35, 100, 30); butlab = new QPushButton(tr("&Zmień napis"), this); butlab->setGeometry(5, 65, 100, 30); connect(button, SIGNAL(clicked()), qApp, SLOT(quit())); connect(butlab, SIGNAL(clicked()), this, SLOT(foo())); } void MainWindow::foo() { label->setText(tr("Wesoła etykieta :)")); } |
Mam nadzieję że kod konstruktora jest w miarę jasny. Na końcu łączymy sygnał przycisku butlab ze slotem foo() obiektu klasy MainWindow. Metoda foo() zmienia tekst naszej etykiety. Jak widać, żadna magia.
A na sam koniec nasza aplikacja z prostym menu i paskiem statusu:
mainwindow.hpp:
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 | #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include <QApplication> #include <QMainwindow> #include <QTextCodec> #include <QPushButton> #include <QLabel> #include <QMenuBar> #include <QStatusBar> class MainWindow : public QMainWindow { Q_OBJECT private: QPushButton *button; QPushButton *butlab; QLabel *label; QMenu *menu; QAction *quitAction; void createMenus(); void createStatusBar(); public: MainWindow(); private slots: void foo(); }; #endif // MAINWINDOW_HPP |
W nagłówku klasy naszego okna dodajemy wskaźniki na obiekty menu oraz akcji.
mainwindow.cpp:
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 | #include "mainwindow.hpp" MainWindow::MainWindow() { QTextCodec::setCodecForTr (QTextCodec::codecForName ("UTF-8")); createMenus(); createStatusBar(); label = new QLabel(tr("Smutna etykieta :("), this); label->setGeometry(5, 15, 150, 30); button = new QPushButton(tr("&Wciśnij mnie ;)"), this); button->setGeometry(5, 45, 100, 30); butlab = new QPushButton(tr("&Zmień napis"), this); butlab->setGeometry(5, 75, 100, 30); connect(button, SIGNAL(clicked()), qApp, SLOT(quit())); connect(butlab, SIGNAL(clicked()), this, SLOT(foo())); setMinimumSize(200, 200); resize(480, 320); } void MainWindow::foo() { label->setText(tr("Wesoła etykieta :)")); } void MainWindow::createMenus() { menu = menuBar()->addMenu(tr("&Plik")); quitAction = new QAction(tr("&Wyjście"), this); quitAction->setStatusTip(tr("Wyjdź z programu.")); connect (quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); menu->addAction(quitAction); } void MainWindow::createStatusBar() { statusBar()->showMessage(tr("Gotowy")); } |
Konstruktor klasy naszego okna głównego doczekał się ustawień minimalnego rozmiaru okna (linia 22.) oraz zmiany wielkości okna (linia 23.). Znacznie ciekawsze jest to co się dzieje w metodach createMenus() oraz createStatusBar(). Pierwsza metoda tworzy poszczególne menu. Dodajemy menu “Plik” (linia 33.), tworzymy akcję “Wyjście” (linia 35.), której podpowiedź będzie wyświetlana w belce statusu (linia 36.), podłączamy tą akcję do slotu wyjścia z aplikacji oraz dodajemy ją do menu (linie 37. i 38). W metodzie createStatusBar() ustawiamy początkową wartość opisu na belce statusu.
Nie musimy tworzyć głównego menu ani belki statusu, gdyż zawierają się one w klasie QMainWindow. Wskaźniki do nich zwracają metody menuBar() oraz statusBar() (odpowiednio menu programu oraz belka statusu). Jest jeszcze ToolBar, który pozwala trzymać przyciski (chociaż można tam wcisnąć również zwykłe widgety, jak suwaki) ale o tej klasie powiem przy okazji zasobów programu. QActions (jak sama nazwa wskazuje
) pozwalają nam przypisać do nich różne akcje (funkcje) poprzez system sygnałów i slotów. Taką akcję możemy później dowolnie umieścić czy to w menu czy na ToolBarze. Na początku mogą się wydać mało intuicyjne, ale po kilku przykładach wszystko staje się jasne i zrozumiałe.
To na tyle, następnym razem wprowadzenie do Qt Creator. W razie pytań, wątpliwości lub jeżeli znaleźliście błąd piszcie w komentarzach, na e-mail lub Jabbera.




polski
bardzo fajny kurs. Brakuje tylko opisu, jak skompilować opisany kod. A tu można natrafić na dwie trudności. Po pierwsze trzeba poinformować kompilator o ścieżce do nagłówków ( -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui). Drugą, jest konieczność użycia moc-qt4 do każdego pliku z makrem Q_OBJECT, oraz dołączenie tak powstałego pliku w momencie linkowania
P.S.
tak, widziałem datę wpisu
@Piotr: jak kompilować znajduje się we wstępie do kursu.
po napisaniu komentarza zauważyłem.
Jednak wolę napisać własnego makefile zamiast korzystać z qmake
przynajmniej wiem dokładniej co się dzieje podczas kompilacji
@Piotr: no cóż, jak chce Ci się marnować czas na coś co automat i tak zrobi to samo, ale znacznie szybciej.
Trzeba się przyzwyczajać, że coraz więcej zadań będą za naś robić automaty – nawet w programowaniu
Fajny ten Twój kurs, ale nie wiem czego u mnie, gdy są dużą litery w includach i brakuje końcówki “.h” to się nie chce skompilować.
PS W mainwindow.hpp masz: #include