Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Omówienie: Qt4::Rozdział_3

Podobne prezentacje


Prezentacja na temat: "Omówienie: Qt4::Rozdział_3"— Zapis prezentacji:

1 Omówienie: Qt4::Rozdział_3
Główne okno programu Sławomir Kubecki, III Informatyka dzienna 2007/08r

2 Spis treści: Wstęp Klasa MainWindow
Tworzenie menu i pasków narzędziowych Pasek statusu Implementacja menu File Używanie okien dialogowych Przechowywanie ustawień programu Obsługa wielu dokumentów jednocześnie Splash Screen A to wszystko przy pomocy czystego kodu…

3 Wstęp Program z graficznym interfejsem => obszerny kod, ale obsługa programu bardziej user-friendly W dzisiejszej prezentacji wykorzystamy kody poprzednio napisanych programów: Find - find.exe Go-to-Cell - gotocell.exe Sort – sort.exe Napiszemy w języku C++ cały kod realizujący główne okno programu, menu, menu podręczne, pasek narzędzi podręcznych, pasek tytułowy oraz ekran startowy. Zaczynamy…

4 Klasa MainWindow Program, który zamierzamy napisać, wykorzystywać będzie funkcjonalności poprzednich programów i będzie to dość prosty arkusz kalkulacyjny. Chcemy, aby główne okno naszego programu wyglądało następująco:

5 Klasa MainWindow Klasę głównego okna aplikacji tworzymy przez dziedziczenie po klasie QMainWindow. Wykorzystamy wiele technik z poprzedniego rozdziału do tworzenia głównego okna, gdyż QDialog i QMainWindow dziedziczą po klasie QWidget. Główne okno możemy również stworzyć przy pomocy programu Qt Designer, ale niestety nie jest to częścią prezentacji. Więcej informacji na ten temat znajduje się na stronie:

6 Klasa MainWindow mainwindow.h #ifndef MAINWINDOW_H
#define MAINWINDOW_H #include <QMainWindow> class QAction; class QLabel; class FindDialog; class Spreadsheet; class MainWindow : public QMainWindow // dziedziczymy po QMainWindow { Q_OBJECT // wymagane makro, aby klasa obsługiwała system sygnałów i slotów public: MainWindow(); // konstruktor protected: void closeEvent(QCloseEvent *event); /* closeEvent() to metoda wirtualna z klasy QWidget, która jest wywoływana, gdy zamykamy okno. Nadpiszemy ją, żeby przy zamykaniu okna, program zapytał użytkownika o chęć zapisania wprowadzonych zmian. */ mainwindow.h

7 Klasa MainWindow … private slots: void newFile(); void open(); bool save(); // typ zwracany bool bool saveAs(); // typ zwracany bool void find(); void goToCell(); void sort(); void about(); void openRecentFile(); void updateStatusBar(); void spreadsheetModified(); /* wiekszość slotów zwraca typ void, ale save() i saveAs() zwracają wartość logiczną. Wartość zwracana przez slot jest ignorowana, gdy jest on wywoływany przez sygnał, ale gdy wywołujemy slot jak zwykłą metodę, wtedy wartość zwracana jest dla nas dostępna, jak w przypadku zwykłej metody. */ mainwindow.h

8 Klasa MainWindow … private: void createActions(); void createMenus(); void createContextMenu(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool okToContinue(); bool loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); void updateRecentFileActions(); QString strippedName(const QString &fullFileName); /* Tutaj mamy garstkę metod prywatnych, które później umożliwią nam łatwą komunikację użytkownika z programem przy pomocy interfejsu graficznego */ mainwindow.h

9 Klasa MainWindow … // pola prywatne – ich wykorzystanie, będzie omówione w dalszej części. Spreadsheet *spreadsheet; FindDialog *findDialog; QLabel *locationLabel, *formulaLabel; QStringList recentFiles; QString curFile; enum { MaxRecentFiles = 5 }; QAction *recentFileActions[MaxRecentFiles], *separatorAction; QMenu *fileMenu, *editMenu; QToolBar *fileToolBar, *editToolBar; QAction *newAction, *openAction, *aboutQtAction; }; #endif Na tym zakończymy omawianie pliku nagłówkowego i przejdziemy do pliku implementacji… mainwindow.h

10 Klasa MainWindow mainwindow.cpp
#include <QtGui> /* plik nagłówkowy, który zawiera definicje wszystkich klas, z których korzystalismy tworząc podklasy */ // Dołączamy pliki nagłówkowe z poprzednich programów #include "finddialog.h" #include "gotocelldialog.h" #include "sortdialog.h " #include "mainwindow.h " // nasz plik nagłówkowy głównego okna #include "spreadsheet.h" // tego pliku jeszcze nie mamy, napiszemy go w rozdziale 4. mainwindow.cpp

11 Klasa MainWindow … MainWindow::MainWindow() // konstruktor głównego okna programu { spreadsheet = new Spreadsheet; /* konstruujemy widgeta klasy Spreadsheet, dziedziczy on po QTableWidget… */ setCentralWidget(spreadsheet); // .. i ustawiamy go w centrum głównego okna // wywołujemy prywatne metody, aby wypełnić pozostałą część okna głównego createActions(); // tworzymy akcje createMenus(); // tworzymy menu createContextMenu(); // tworzymy menu podręczne createToolBars(); // tworzymy pasek narzędzi createStatusBar(); // tworzymy pasek statusu readSettings(); // metoda wczytuje zapisane opcje programu findDialog = 0; /* ustawiamy wskaźnik na null; gdy pierwszy raz zostanie wywołana funkcja MainWindow::find() utworzymy nowy obiekt klasy FindDialog */ setWindowIcon(QIcon(":/images/icon.png")); /* ustawiamy ikonę głównego okna – górny lewy róg paska tytułowego */ setCurrentFile(""); } mainwindow.cpp

12 Klasa MainWindow Obiekt klasy Spreadsheet (którego dziś nie będziemy omawiać) będzie naszym głównym widgetem – umieścimy go w centrum naszego okna. Pozostałe elementy okna głównego rozmieścimy wzorując się poniższym schematem: Window Title – pasek tytułowy Menu Bar – pasek Menu Toolbar Areas – przestrzeń pasków narzędziowych Dock Window Areas – pole do „dokowania” okienek (zwykle pasków narzędziowych) Status Bar – pasek statusu/informacyjny

13 Klasa MainWindow Qt obsługuje wiele formatów plików graficznych jak BMP, JPEG, PNG, PNM, XBM, XPM. GIFy standardowo nie były dostępne z powodów patentowych w niektórych państwach. Można je jednak "odblokować" korzystając z programu configure w pliku głównym Qt: "configure -qt-gif" lub zaznaczyć podczas instalacji odpowiednią opcję. Sposoby udostępniania grafiki dla programu: 1. Przechowywanie grafik w osobnych plikach i ładowanie ich z każdym uruchomieniem programu 2. Dołączanie plików XPM do kodu źródłowego 3. Użycie mechanizmu zasobów Qt Na potrzeby prezentacji skorzystamy z mechanizmu zasobów. Całą grafikę będziemy przechowywać w podkatalogu images

14 Klasa MainWindow Aby wykorzystać mechanizm zasobów tworzymy plik zasobów (np. spreadsheet.qrc) oraz dodajemy następującą linię do pliku projektu .pro: RESOURCES = spreadsheet.qrc Pliki .qrc mają strukturę prostego pliku XML. Fragment pliku: <!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>images/icon.png</file> ... <file>images/gotocell.png</file> </qresource> </RCC> Pliki zasobów są "wkompilowywane" w plik wykonywalny. Do zasobów odwołujemy się wykorzystując prefix :/ np. :/images/icon.png Pliki zasobów mogą zawierać również inne rodzaje plików, nie tylko graficzne.

15 Menu i paski narzędziowe
Większość nowoczesnych aplikacji z graficznym interfejsem jest dostarczonych z różnymi menu, podręcznymi menu oraz paskami narzędziowymi, aby użytkownikowi ułatwić pracę z aplikacją. Technologia Qt upraszcza proces programowania menu i pasków narzędziowych poprzez system "akcji". Są to pewne własności które można dodać do dowolnej ilości menu i pasków narzędzi. Tworzenie menu i pasków narzędzi w Qt: 1. Utworzenie i ustawienie akcji 2. Utworzenie menu i połączenie ich z akcjami 3. Utworzenie pasków narzędzi i połączenie ich z akcjami W naszej aplikacji akcje są tworzone w metodze createActions():

16 Menu i paski narzędziowe
… void MainWindow::createActions() { newAction = new QAction(tr("&New"), this); // nadajemy akcji nazwę, oraz rodzica – główne okno newAction->setIcon(QIcon(":/images/new.png")); // nadajemy akcji ikonkę newAction->setShortcut(tr("Ctrl+N")); // nadajemy akcji skrót klawiszowy newAction->setStatusTip(tr("Create a new spreadsheet file")); /* nadajemy akcji adnotację, która pojawi się w pasku statusu */ connect(newAction, SIGNAL(triggered()), this, SLOT(newFile())); /* łączymy sygnał uruchomienia akcji triggered() ze slotem newFile() */ // Akcje Open, Save, Save As - analogicznie /* wypełniamy tablicę recentFileActions akcjami. Każda za nich jest ukryta i połączona ze slotem openRecentFile() */ for (int i = 0; i < MaxRecentFiles; ++i) recentFileActions[i] = new QAction(this); recentFileActions[i]->setVisible(false); // ukrywamy akcje, dopóki nie będą nam potrzebne connect(recentFileActions[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } mainwindow.cpp

17 Menu i paski narzędziowe
… // akcja Select All selectAllAction = new QAction(tr("&All"), this); selectAllAction->setShortcut(tr("Ctrl+A")); selectAllAction->setStatusTip(tr("Select all the cells in the spreadsheet")); connect(selectAllAction, SIGNAL(triggered()), spreadsheet, SLOT(selectAll())); /* slot selectAll jest udostępniony przez klasę QAbstractItemView */ /* Show Grid i Auto-Recalculate są indywidualnymi akcjami, które mogą być zaznaczone bądź odznaczone – dla skrócenia, pokażemy implementację tylko Show Grid*/ showGridAction = new QAction(tr("&Show Grid"), this); showGridAction->setCheckable(true); /* akcja jest wyświetlana z możliwością zaznaczenia lub jej odznaczenia */ showGridAction->setChecked(spreadsheet->showGrid()); showGridAction->setStatusTip(tr("Show or hide the spreadsheet's grid")); connect(showGridAction, SIGNAL(toggled(bool)), spreadsheet, SLOT(setShowGrid(bool))); /* łączymy sygnał zmiany stanu akcji ze slotem który odpowiada za wyświetlanie kratki w aplikacji */ /* Qt umożliwia również tworzenie akcji wykluczających się przy użyciu klasy QActionGroup */ mainwindow.cpp

18 Menu i paski narzędziowe
… /* Przy implementacji akcji "About Qt" wykorzystamy slot aboutQt() dostępny z poziomu zmiennej globalnej qApp klasy Qapplication */ aboutQtAction = new QAction(tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } Tak prezentuje się okno informacyjne About Qt: mainwindow.cpp

19 Menu i paski narzędziowe
Teraz skoro mamy już akcje, przejdźmy do zbudowania systemu menu, który będzie je zawierał. W Qt, menu są instancjami QMenu. Metoda addMenu() tworzy nowy widget z określonym tekstem i umieszcza go na pasku menu. Metoda QMainWindow::menuBar() zwraca wskaźnik do obiektu klasy QMenuBar. Nasze menu jest tworzone gdy pierwszy raz wywołujemy menuBar(). void MainWindow::createMenus() { // menu File fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAction); // analogicznie openAction, saveAction, saveAsAction separatorAction = fileMenu->addSeparator(); /* zachowujemy wskaźnik do tego separatora - pozwoli nam to w ukryć go w przypadku, gdy nie ma odnotowanych ostatnio otwartych plików. */ for (int i = 0; i < MaxRecentFiles; ++i) fileMenu->addAction(recentFileActions[i]); // dodajemy kolejno akcje ostatnio otwartych plików fileMenu->addSeparator(); fileMenu->addAction(exitAction); mainwindow.cpp

20 Menu i paski narzędziowe
… // menu Edit editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAction); // analogicznie copyAction, pasteAction, deleteAction selectSubMenu = editMenu->addMenu(tr("&Select")); // dodajemy podmenu w menu Edit selectSubMenu->addAction(selectRowAction); selectSubMenu->addAction(selectColumnAction); selectSubMenu->addAction(selectAllAction); editMenu->addSeparator(); // dodajemy akcje związane z uprzednio napisanymi programami editMenu->addAction(findAction); editMenu->addAction(goToCellAction); mainwindow.cpp

21 Menu i paski narzędziowe
… toolsMenu = menuBar()->addMenu(tr("&Tools")); toolsMenu->addAction(recalculateAction); toolsMenu->addAction(sortAction); // akcja związana z napisanym wcześniej programem optionsMenu = menuBar()->addMenu(tr("&Options")); optionsMenu->addAction(showGridAction); optionsMenu->addAction(autoRecalcAction); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAction); helpMenu->addAction(aboutQtAction); } Nasze menu (w zależności od platformy systemowej) wygląda mniej więcej tak: mainwindow.cpp

22 Menu i paski narzędziowe
Dowolny widget w Qt może mieć przypisany zestaw akcji. By zbudować menu podręczne dla aplikacji, dodajemy akcje do wybranego widgeta i ustawiamy, by menu podręczne wyświetlało możliwe do wykonania akcje. Menu podręczne są wywoływane poprzez prawy klik myszą (bądź jego odpowiednik na innych platformach systemowych) na widgecie. Istnieje też inny bardziej skomplikowany sposób dostarczania menu podręcznych, polegający na tym by nadpisać metodę QWidget::contextMenuEvent(), stworzyć widgeta QMenu i wypełnić go pożądanymi akcjami i wywołać metodę exec() na nim. void MainWindow::createContextMenu() { // dodajemy menu podręczne do głównego widgeta - spreadsheet spreadsheet->addAction(cutAction); spreadsheet->addAction(copyAction); spreadsheet->addAction(pasteAction); spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu); } Menu podręczne: mainwindow.cpp

23 Menu i paski narzędziowe
void MainWindow::createToolBars() { fileToolBar = addToolBar(tr("&File")); fileToolBar->addAction(newAction); // analogicznie dla openAction, saveAction //… editToolBar = addToolBar(tr("&Edit")); editToolBar->addAction(cutAction); // analogicznie dla copyAction, pasteAction editToolBar->addSeparator(); editToolBar->addAction(findAction); editToolBar->addAction(goToCellAction); } Pasek narzędziowy: mainwindow.cpp

24 Różne stany paska statusu:
Pasek statusu Przejdziemy teraz do zaimplementowania paska statusu. Zwykle będzie on wyświetlał aktualną pozycję oraz formułę zaznaczonej komórki. Dodatkowo, czasami będzie wyświetlał tymczasowe informacje. Wszystkie informacje będą reprezentowane przez obiekty klasy QLabel, których rodzicem będzie pasek statusu. Jak widzimy na obrazku, dwa główne pola informacyjne wymagają różnej ilości miejsca na pasku. Decydujemy zatem, że w razie gdy będziemy zmieniać rozmiar okna podczas użytkowania, dodatkowe miejsce przeznaczymy dla pola z formułą po prawej stronie. Osiągamy to poprzez ustawienie współczynnika rozciągalności na 1. Różne stany paska statusu:

25 Pasek statusu … void MainWindow::createStatusBar() { locationLabel = new QLabel(" W999 "); /* zostawiamy trochę miejsca z obu stron dla lepszej widoczności */ locationLabel->setAlignment(Qt::AlignHCenter); locationLabel->setMinimumSize(locationLabel->sizeHint()); /* ustawiamy minimalną wielkość miejsca jaką powinna zająć informacja na pasku statusu */ formulaLabel = new QLabel; formulaLabel->setIndent(3); // dodajemy akapit przed tekstem statusBar()->addWidget(locationLabel); statusBar()->addWidget(formulaLabel, 1); /* ustawienie współczynnika rozciągalności na 1 (domyślnie ustawiany jest na 0) */ // łączymy dwa sygnały ze obiektu spreadsheet do slotów mainWindow connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateStatusBar())); connect(spreadsheet, SIGNAL(modified()), this, SLOT(spreadsheetModified())); updateStatusBar(); } mainwindow.cpp

26 Pasek statusu … /* slot updateStatusBar() uaktualnia wyświetlanie informacji na pasku statusu dotyczących aktualnie zaznaczonej komórki. Dodatkowo jest wywoływany jako zwykła metoda przy createStatusBar() by zainicjować wyświetlanie informacji, ponieważ obiekt spreadsheet nie wysyła sygnału currentCellChanged() przy uruchomieniu */ void MainWindow::updateStatusBar() { locationLabel->setText(spreadsheet->currentLocation()); formulaLabel->setText(spreadsheet->currentFormula()); } void MainWindow::spreadsheetModified() setWindowModified(true); // uaktualnienie paska tytułowego updateStatusBar(); /* uaktualenienie indykatorów informacyjnych paska statusu, by odpowiadały bieżącej sytuacji */ Na tym zakończymy implementację paska statusu i przejdziemy do implementacji menu File. mainwindow.cpp

27 Implementacja menu File
… /* metoda pytająca czy użytkownik chce zapisać ewentualne zmiany w akualnie edytowanym pliku w przypadku niezapisanych zmian */ bool MainWindow::okToContinue() { if (isWindowModified()) { // sprawdzamy czy były wprowadzone zmiany od ostatniego zapisu int r = QMessageBox::warning(this, tr("Spreadsheet"), tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox::Yes | QMessageBox::Default, /* domyślna odpowiedź - naciśnięcie "Enter" jest równoznaczne z wyborem Yes */ QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); /* naciśnięcie "ESC" na klawiaturze jest równoznaczne z wyborem Cancel */ if (r == QMessageBox::Yes) { return save(); // decydujemy się na zapis pliku } else if (r == QMessageBox::Cancel) { return false; } /* gdy rezygnujemy z decyzji która wywołala tę metodę, jak np. decyzja o utworzeniu nowego pliku, bądź zamknięcie programu */ } return true; // gdy nie chcemy zapisać zmian w pliku … mainwindow.cpp

28 Implementacja menu File
Składnia wywołania QMessageBox::warning() może wyglądać trochę zagmatwanie: QMessageBox::warning(parent, title, message, button0, button1, ...); QMessageBox udostępnia również information(), question() oraz critical() - każde ze swoją własną ikonką. void MainWindow::newFile() { if (okToContinue()) spreadsheet->clear(); // czyścimy wszystko co jest w głównym widgecie setCurrentFile(""); // nadajemy nazwę nowemu dokumentu - pusty string } } … mainwindow.cpp

29 Implementacja menu File
… void MainWindow::open() { if (okToContinue()) QString fileName = QFileDialog::getOpenFileName(this, /* rodzic - warunkuje początkowe umiejscowienie okna na ekranie */ tr("Open Spreadsheet"), // tytuł okna ".", // rozpoczęcie wybierania pliku z katalog początkowego tr("Spreadsheet files (*.sp)\n" "Comma-separated values files (*.csv)\n" "Lotus files (*.wk1 *.wks)")) // filtry plików do wczytania (wymienione w kolejnych liniach) ); // statyczna metoda zwracająca ścieżkę do pliku, lub pusty string w przypadku naciśnięcia Cancel if (!fileName.isEmpty()) loadFile(fileName); } mainwindow.cpp

30 Implementacja menu File
… bool MainWindow::loadFile(const QString &fileName) { if (!spreadsheet->readFile(fileName)) /* wykorzystujemy Spreadsheet::readfile() by wczytać plik z dysku */ statusBar()->showMessage(tr("Loading canceled"), 2000); /* w przypadku problemów z otwarciem pliku pojawi się odpowiednia informacja na pasku statusu przez 2000ms, czyli 2s…*/ return false; // ...a metoda zwróci false } setCurrentFile(fileName); // uaktualniamy pasek tytułowy o nazwę aktualnego pliku statusBar()->showMessage(tr("File loaded"), 2000); return true; mainwindow.cpp

31 Implementacja menu File
... bool MainWindow::save() { if (curFile.isEmpty()) { // gdy plik nie byl jeszcze zapisany pod żadną nazwą, wówczas... return saveAs(); } else { // .. w przeciwnym razie zapisujemy go pod bieżącą nazwą return saveFile(curFile); } bool MainWindow::saveFile(const QString &fileName) { if (!spreadsheet->writeFile(fileName)) { statusBar()->showMessage(tr("Saving canceled"), 2000); return false; setCurrentFile(fileName); // uaktualniamy pasek tytułowy w przypadku nowo zapisanego pliku statusBar()->showMessage(tr("File saved"), 2000); return true; … mainwindow.cpp

32 Implementacja menu File
... bool MainWindow::save() { if (curFile.isEmpty()) { // gdy plik nie byl jeszcze zapisany pod żadną nazwą, wówczas... return saveAs(); } else { // .. w przeciwnym razie zapisujemy go pod bieżącą nazwą return saveFile(curFile); } bool MainWindow::saveFile(const QString &fileName) { if (!spreadsheet->writeFile(fileName)) { statusBar()->showMessage(tr("Saving canceled"), 2000); return false; setCurrentFile(fileName); // uaktualniamy pasek tytułowy w przypadku nowo zapisanego pliku statusBar()->showMessage(tr("File saved"), 2000); return true; … mainwindow.cpp

33 Implementacja menu File
… bool MainWindow::saveAs() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save Spreadsheet"), ".", tr("Spreadsheet files (*.sp)")); /* pobieramy nazwę pliku od użytkownika; w przypadku Cancel, metoda zwraca pusty string. dodając dodatkowy argument QFileDialog::DontConfirmOverwrite, metoda nie zapyta użytkownika w przypadku istnienia już pliku o podanej nazwie tylko od razu go nadpisze. */ if (fileName.isEmpty()) return false; return saveFile(fileName); } mainwindow.cpp

34 Implementacja menu File
… void MainWindow::closeEvent(QCloseEvent *event) { if (okToContinue()) /* w przypadku gdy nie ma niczego nowego do zapisania; chęci zapisu, bądź odmowy zapisu zmian w dokumencie przy wyjściu z programu... */ writeSettings(); // zapis aktualnych ustawień programu event->accept(); // zgoda na zamknięcie programu } else // .. w przypadku wybrania Cancel przy pytaniu o zapis -> powrót do programu event->ignore(); // zignorowanie wywołania wydarzenia /* program jest wyłączany z zamknięciem ostatniego okna programu. możemy to zmienić modyfikując właściwość QApplication::quitOnLastWindowClosed na false. Wówczas program zakończy się dopiero przy wywołaniu QApplication::quit() */ mainwindow.cpp

35 Implementacja menu File
… QString MainWindow::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); /* zwracamy tylko nazwę pliku */ } void MainWindow::setCurrentFile(const QString &fileName) { curFile = fileName; setWindowModified(false); /* dopóki dokument nie zostanie zmodyfikowany, ustawiamy zmiany na false */ QString shownName = "Untitled"; if (!curFile.isEmpty()) shownName = strippedName(curFile); /* wywołanie skracania nazwy pliku do niezbędnego minimum, aby wyświetlanie było jak najbardziej przejrzyste */ recentFiles.removeAll(curFile); /* usuwamy wystąpienia pliku w tablicy ostatnio otwartych plików, by uniknąć duplikacji... */ recentFiles.prepend(curFile); /* ... a następnie dodajemy nazwę pliku na początek tablicy ostatnio otwartych plików */ updateRecentFileActions(); // uaktualniamy wpisy w menu File } … mainwindow.cpp

36 Implementacja menu File
… setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("Spreadsheet"))); /* wyświetlanie * przy nazwie pliku w pasku tytułowym w przypadku, gdy nastąpiła modyfikacja pliku od ostatniego zapisu, bądź otwarcia */ /* metoda QString::arg() podmienia kolejno parametry %n w stringu swoimi argumentami. pierwsze wywołanie arg() zastąpi %1 nazwą pliku, a drugie - %2, tekstem "Spreadsheet". oczywiście można to napisać w prostszy sposób: setWindowTitle(shownName + tr("[*] - Spreadsheet")); ale korzystając z arg() ułatwiamy ew. tłumaczenie programu na inne języki. */ } mainwindow.cpp

37 Implementacja menu File
… void MainWindow::updateRecentFileActions() { // wykorzystamy iterator do usuwania nieistniejących plików z recentFiles (klasy QStringList) QMutableStringListIterator i(recentFiles); while (i.hasNext()) if (!QFile::exists(i.next())) i.remove(); } mainwindow.cpp

38 Implementacja menu File
… for (int j = 0; j < MaxRecentFiles; ++j) { if (j < recentFiles.count()) { /* przypisujemy kolejnym plikom stringi składające się ze znaku &,liczby porządkowej(j+1), spacji oraz nazwy pliku. */ QString text = tr("&%1 %2").arg(j + 1).arg(strippedName(recentFiles[j])); recentFileActions[j]->setText(text); /* nadajemy odpowiednim akcjom nazwy, które wygenerowaliśmy ... */ recentFileActions[j]->setData(recentFiles[j]); /* ... oraz przypisujemy im wartości -> odpowiednie korespondujące wartości z recentFiles */ recentFileActions[j]->setVisible(true); } else { recentFileActions[j]->setVisible(false); /* chowamy pozostałe pola w recentFileActions, w przypadku, gdy nie ma wystarczającej ilości ostatnio otwartych plików, by w całości zapełnić tablicę. } separatorAction->setVisible(!recentFiles.isEmpty()); /* dodajemy widoczny separator w przypadku, gdy są ostatnio otwarte pliki */ } … mainwindow.cpp

39 Implementacja menu File
… void MainWindow::openRecentFile() { if (okToContinue()) { QAction *action = qobject_cast<QAction *>(sender()); /* metoda ta wykonuje dynamiczne rzutowanie na meta-informację wygenerowaną przez moc - meta-obiektowy kompilator Qt. zwraca wskaźnik na wskazaną podklasę QObject, lub 0 jeśli nie można rzutować obiektu w ten sopsób. */ if (action) // jeśli rzutowanie się powiodło... loadFile(action->data().toString()); /*... wywołujemy metodę loadFile() z pełną ścieżką do pliku, którą wyłuskalismy z miejsca na które wskazuje action. */ } } … Tak oto prezentuje się nasze menu File: mainwindow.cpp

40 Używanie okien dialogowych
Zaczniemy od okna dialogowego Find z poprzedniego rozdziału. Chcemy, żeby użytkownik mógł bez problemu przełączać się z okna głównego aplikacji do okna Find i z powrotem. Nasze okno Find musi być modeless(niemodalne?) tzn. takie, które działa niezależnie od pozostałych okien danej aplikacji. Jak łatwo można się domyśleć, okno Find pozwala użytkownikowi wyszukać wpisany tekst w arkuszu kalkulacyjnym. Slot find() jest wywoływany, gdy użytkownik wybierze Edit|Find i wówczas pojawia się okno dialogowe Find. Możliwe są 3 sytuacje: 1. Użytkownik uruchomił po raz pierwszy Find 2. Użytkownik uruchomił już wcześniej Find, ale później zamknął. 3. Użytkownik uruchomił Find, które było już uruchomione. Jeśli Find nie było uruchomione, tworzymy je i łączymy jego sygnaly findNext() i findPrevious() z odpowiednimi slotami w Spreadsheet. Na koniec wywołujemy show() i activateWindow() by się upewnić że okno jest widoczne i aktywne. mainwindow.cpp

41 Używanie okien dialogowych
… void MainWindow::find() { if (!findDialog) findDialog = new FindDialog(this); connect(findDialog, SIGNAL (findNext(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT (findNext(const QString &, Qt::CaseSensitivity))); connect(findDialog, SIGNAL(findPrevious(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT(findPrevious(const QString &,Qt::CaseSensitivity))); } findDialog->show(); findDialog->activateWindow(); mainwindow.cpp

42 Używanie okien dialogowych
… void MainWindow::goToCell() { GoToCellDialog dialog(this); /* tworzymy zmienną na stosie, która po wykorzystaniu zostanie automatycznie usunięta */ if (dialog.exec()) /* uruchamiamy okno Go To Cell; metoda exec() zwraca true w przypadku kliknięcia OK i false w przypadku Cancel */ QString str = dialog.lineEdit->text().toUpper(); /* pobieramy zawartość okna dialogowego Go To Cell... */ spreadsheet->setCurrentCell(str.mid(1).toInt() - 1, // numer wiersza str[0].unicode() - 'A' ); // nr kolumny // gdzie np. A1 = (0,0), B27 = (26,1) //... i przenosimy się w głównym oknie do odpowiadającej komórki arkusza // mamy pewność że wartość w oknie jest poprawna, bo wykorzystaliśmy w niej specjalny filtr wprowadzanych danych. } mainwindow.cpp

43 Używanie okien dialogowych
… void MainWindow::about() { QMessageBox::about(this, tr("About Spreadsheet"), tr("<h2>Spreadsheet 1.1</h2>" "<p>Copyright © 2006 Software Inc." "<p>Spreadsheet is a small application that " "demonstrates QAction, QMainWindow, QMenuBar, " "QStatusBar, QTableWidget, QToolBar, and many other " "Qt classes.")); /* metoda about() jest bardzo podobna do warning() za wyjątkiem tego, że wykorzystuje ikonkę z okna nadrzędnego, zamiast standardowej ikonki "ostrzeżenia". */ } .. mainwindow.cpp

44 Przechowywanie ustawień programu
Przy uruchamianiu programu, w konstruktorze MainWindow korzystaliśmy z metody readSettings(), aby wczytać zapisane ustawienia programu. Podobnie zamykając program, w closeEvent(), wywoływaliśmy writeSettings(). Teraz napiszemy te metody: void MainWindow::writeSettings() { QSettings settings("Software Inc.", "Spreadsheet"); /* domyślnie QSettings są przechowywane w różnych miejscach zależnych od platformy systemowej. W Windows jest to rejestr, w Unixie w plikach tekstowych, a w Mac OS X wykorzystywane jest Core Foundation Preferences API. Później na podstawie danych w konstruktorze będziemy mogli odnaleźć ustawienia w systemie i je wczytać. Opcje przechowywane są w postaci pary (nazwa,wartość) */ settings.setValue("geometry", geometry()); // zapisujemy pozycję i rozmiar okna głównego settings.setValue("recentFiles", recentFiles); // zapisujemy listę ostatnio otwieranych plików settings.setValue("showGrid", showGridAction->isChecked()); /* zapisujemy ustawienie showGrid, jako true lub false */ settings.setValue("autoRecalc", autoRecalcAction->isChecked()); /* jw tylko odnośnie opcji autoRecalc */ } mainwindow.cpp

45 Przechowywanie ustawień programu
… void MainWindow::readSettings() { QSettings settings("Software Inc.", "Spreadsheet"); QRect rect = settings.value("geometry", // wczytujemy dane o ostatnim położeniu i rozmiarze okna… QRect(200, 200, 400, 400)).toRect(); /*... lub gdy nie ma zapisanych ustawień wykorzystujemy domyślnie te ustawienia podane jako drugi parametr move(rect.topLeft()); // ustawiamy pozycję okna na podstawie posiadanych danych resize(rect.size()); // ustawiamy rozmiar okna na podstawie posiadanych danych recentFiles = settings.value("recentFiles").toStringList(); updateRecentFileActions(); bool showGrid = settings.value("showGrid", true).toBool(); showGridAction->setChecked(showGrid); bool autoRecalc = settings.value("autoRecalc", true).toBool(); autoRecalcAction->setChecked(autoRecalc); } Ukończyliśmy implementację okna głównego!! Możemy przejść teraz implementacji programu głównego  mainwindow.cpp

46 Obsługa wielu dokumentów jednocześnie
main.cpp #include <QApplication> #include "mainwindow.h" /* tak skonstruowana główna fukcja pozwala aplikacji uruchomić tylko jedno okno główne i obsłużyć jeden dokument na raz. */ int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWin; // tworzymy okno, jako zmienną na stosie mainWin.show(); return app.exec(); } main.cpp

47 Obsługa wielu dokumentów jednocześnie
Możemy uruchomić po prostu wiele razy naszą aplikację, aby obsłużyła wiele plików "naraz", ale nie jest to zbyt wygodne. Wolelibyśmy, aby w jednej instancji naszego programu można było uruchomić wiele okien, w których moglibyśmy edytować dokumenty - podobnie jak w przeglądarkach internetowych. Jednak żeby to osiągnąć musimy zmodyfikować nasz program... main.cpp int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow *mainWin = new MainWindow; /* wykorzystujemy new do stworzenia okna. później wykorzystując delete będziemy mogli usunąć okna które zamykamy */ mainWin->show(); return app.exec(); } main.cpp

48 Obsługa wielu dokumentów jednocześnie
Zmiany w Menu File: 1. File|New będzie tworzyć nowe okno główne z pustym dokumentem, zamiast wykorzystywać istniejące okno 2. File|Close będzie zamykać aktualne okno główne 3. File|Exit będzie zamykać wszystkie okna mainwindow.cpp void MainWindow::newFile() { MainWindow *mainWin = new MainWindow; /* nie musimy zapisywać nigdzie wskaźnika do nowego okna, ponieważ Qt obserwuje dla nas wszystkie okna. */ mainWin->show(); } Nowe menu File: mainwindow.cpp

49 Obsługa wielu dokumentów jednocześnie
// dodajemy akcje dla Close i modyfikujemy Exit void MainWindow::createActions() { ... closeAction = new QAction(tr("&Close"), this); closeAction->setShortcut(tr("Ctrl+W")); closeAction->setStatusTip(tr("Close this window")); connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); /* dodajemy akcję zamknięcia pojedynczego okna */ exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); /* zamykamy wszystkie okna przy wywołaniu Exit */ } mainwindow.cpp

50 Obsługa wielu dokumentów jednocześnie
To jednak nie wszystko. Problemem może stać się fakt, iż użytkownik zechce otworzyć ogromną ilość okien, a my nigdzie nie zwalniamy pamięci po zamkniętych oknach, gdyż domyślnym zachowaniem programu jest ukrycie tych okien. Wykorzystamy atrybut Qt::WA_DeleteOnClose w konstruktorze okna głównego... mainwindow.cpp MainWindow::MainWindow() { ... setAttribute(Qt::WA_DeleteOnClose); // przy zamknięciu okna, niszczymy obiekt i zwalniamy pamięć } Jednak wycieki pamięci to nie jedyny problem z jakim musimy się zmierzyć. Każde okno ma własne ustawienia i własne ostatnio otwarte pliki. Oczywiste jest, że lista ostatnio otwartych plików powinna być globalna w całej aplikacji. Możemy to łatwo osiągnąć poprzez dodanie właściwości static przy deklaracji zmiennej recentFiles. Pozostaje jeszcze upewnić się, że kiedykolwiek nie wywołamy updateRecentFileActions() by uaktualnić menu File, metoda ta wywoła się dla wszystkich okien... mainwindow.cpp

51 Obsługa wielu dokumentów jednocześnie
/* podobną składnię możemy wykorzystać przy synchronizacji okien względem opcji Show Grid i Auto-Recalculate */ foreach (QWidget *win, QApplication::topLevelWidgets()) { if (MainWindow *mainWin = qobject_cast<MainWindow *>(win)) mainWin->updateRecentFileActions(); } Aplikacje, które pozwalają na obsługę tylko jednego dokumentu naraz określane są jako aplikacje SDI (single document interface). Alternatywą dla nich są aplikacje MDI (multiple document interface), gdzie aplikacja ma swoje główne okno, w którym zarządza wieloma otwartymi oknami dokumentów. Qt pozwala na łatwe tworzenie zarówno SDI, jak i MDI, na wszystkie obsługujące je platformy systemowe. SDI MDI

52 Splash Screen Splash screen to określenie na pewną grafikę, która pojawia się na ekranie podczas uruchamiania programu. Wiele aplikacji posiada splash screeny. Często programiści używają ich, by zamaskować powolne uruchamianie programu, a inni, by po prostu ubarwić program. Dodawanie splash screenów w Qt jest niezwykle proste - wykorzystamy klasę QSplashScreen. Klasa QSplashScreen wyświetla obrazek zanim pojawi się okno główne programu. Może również wyświetlać napisy na obrazku, aby np. informować użytkownika o postępie w inicjalizacji programu. Zazwyczaj kod splash screena umieszczony jest w funkcji main(), przed wywołaniem QApplication::exec()

53 Splash Screen Main.cpp int main(int argc, char *argv[]) {
QApplication app(argc, argv); QSplashScreen *splash = new QSplashScreen; splash->setPixmap(QPixmap(":/images/splash.png")); // wczytujemy grafikę do splash screena … splash->show(); // ... i wyświetlamy Qt::Alignment topRight = Qt::AlignRight | Qt::AlignTop; splash->showMessage(QObject::tr("Setting up the main window..."), topRight, Qt::white); // wyświetlamy napis o aktualnym stanie inicjalizacji programu MainWindow mainWin; splash->showMessage(QObject::tr("Loading modules..."), topRight, Qt::white); loadModules(); splash->showMessage(QObject::tr("Establishing connections..."), establishConnections(); main.cpp

54 I to by było na tyle odnośnie 3. rozdziału…
Splash Screen mainWin.show(); splash->finish(&mainWin); delete splash; return app.exec(); } I to by było na tyle odnośnie 3. rozdziału… main.cpp

55 The End; // That’s all folks!
main.cpp


Pobierz ppt "Omówienie: Qt4::Rozdział_3"

Podobne prezentacje


Reklamy Google