1 / 38

QT 4:

QT 4:. Implementacja funkcjonalności aplikacji Paweł Piątkowski A.D 2008. O czym w tym rozdziale?. Widget centralny Podklasa QTableWidget Zapisywanie i odczytywanie arkusza Implementacja Menu Edit Implementacja pozostałych Menu Podklasa QTableWidgetItem. Widget centralny.

bryony
Download Presentation

QT 4:

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. QT 4: Implementacja funkcjonalności aplikacji Paweł Piątkowski A.D 2008

  2. O czym w tym rozdziale? • Widget centralny • Podklasa QTableWidget • Zapisywanie i odczytywanie arkusza • Implementacja Menu Edit • Implementacja pozostałych Menu • Podklasa QTableWidgetItem

  3. Widget centralny • W centralnej częśći QMainWindowmoże znajdować się dowolny widget Przegląd dostępnych możliwości: • Standardowy widget (np. QTableWidget lub QTextEdit); Funkcjonalność aplikacji musi być wtedy zaimplementowana w innym miejscu (np. QMainWindow) • Niestandardowy widget • Standardowy widget z layout managerem; Wiele połączonych widgetów mających za rodzica Qwidget • Splitter (przy użyciu QSplitter); Możliwość rozmieszczenia widgetów • Obszar roboczy MDI; W części centralnej QWorkspace + okienka będące jego potomkami

  4. Spreadsheet.h - podklasa QTableWidget #ifndef SPREADSHEET_H #define SPREADSHEET_H #include <QTableWidget> class Cell; class SpreadsheetCompare; class Spreadsheet : public QTableWidget { Q_OBJECT public: Spreadsheet(QWidget *parent = 0); bool autoRecalculate() const { return autoRecalc; } // definicja inline QString currentLocation() const; QString currentFormula() const; QTableWidgetSelectionRange selectedRange() const; void clear(); bool readFile(const QString &fileName); bool writeFile(const QString &fileName); void sort(const SpreadsheetCompare &compare);

  5. Spreadsheet.h - podklasa QTableWidget public slots: void cut(); void copy(); void paste(); void del(); void selectCurrentRow(); void selectCurrentColumn(); void recalculate(); void setAutoRecalculate(bool recalc); void findNext(const QString &str, Qt::CaseSensitivitycs); void findPrevious(const QString &str, Qt::CaseSensitivitycs); signals: void modified(); // wysłanie sygnału, gdy nastąpi zmiana w arkuszu private slots: void somethingChanged();

  6. Spreadsheet.h - podklasa QTableWidget private: enum { MagicNumber = 0x7F51C883, RowCount = 999, ColumnCount = 26 }; // MagicNimber – do formatu pliku arkusza Cell *cell(int row, int column) const;// wskaźnik na pojedyńczą komórkę QString text(int row, int column) const;// zwraca tekst komórki QString formula(int row, int column) const;// zwraca formułę z komórki void setFormula(int row, int column, const QString &formula);// ustawia formułę bool autoRecalc; }; class SpreadsheetCompare // funktor – obiekt funkcyjny; Można używać klasy jak funkcji dzięki zdefiniowanemu operatorowi; klasa do sortowania komórek { public: bool operator()(const QStringList &row1, const QStringList &row2) const; enum { KeyCount = 3 }; Int keys[KeyCount]; bool ascending[KeyCount]; }; #endif

  7. Spreadsheet.cpp - podklasa QTableWidget #include <QtGui> #include "cell.h" #include "spreadsheet.h" Spreadsheet::Spreadsheet(QWidget *parent) : QTableWidget(parent) { autoRecalc = true; setItemPrototype(new Cell); /* zastąpienie standardowego obiektu QTableWidgetItem przez Cell; Cell klonowany za każdym razem */ setSelectionMode(ContiguousSelection); /* możliwość zaznaczenia jedynie pojedyńczego prostokątnego obszaru ciągłego */ connect(this, SIGNAL(itemChanged(QTableWidgetItem *)), // przy edycji komórki this, SLOT(somethingChanged())); clear(); /* zmiana rozmiaru tabeli i utworzenie nagłówków kolumn – przygotowanie arkusza do pracy */ }

  8. Spreadsheet.cpp - podklasa QTableWidget void Spreadsheet::clear() { setRowCount(0); setColumnCount(0); // arkusz o rozmiarze 0 x 0 – czyszczenie nagłówków setRowCount(RowCount); setColumnCount(ColumnCount); // ustawienie rozmaru początkowego for (int i = 0; i < ColumnCount; ++i) { QTableWidgetItem *item = new QTableWidgetItem; item->setText(QString(QChar('A' + i))); // nagłówki kolumn A,B,C...Z setHorizontalHeaderItem(i, item); } setCurrentCell(0, 0); } /* Istnieje klasa QTableWidget::clear() jednak czyści ona jedynie zawartość komórek i zaznaczenia pozostawiając nagłówki */

  9. Mała dygresja - Budowa QTableWidget • QHeaderView na górze i po lewej stronie • viewport na środku – na nim rysowana jest właściwa część tabeli • QScrollArea na dole i po prawej stronie

  10. Spreadsheet.cpp - podklasa QTableWidget Cell *Spreadsheet::cell(int row, int column) const { return static_cast<Cell *>(item(row, column)); } /* Nie można skorzystać z QTableWidget::item() , bo zwraca QTableWidgetItem, z którego zrezygnowaliśmy na korzyść Cell */ QString Spreadsheet::text(int row, int column) const { Cell *c = cell(row, column); if (c) { return c->text(); // zwraca tekst komórki } else { return ""; // zwraca pusty string, gdy komórka jest pusta } }

  11. Spreadsheet.cpp - podklasa QTableWidget QString Spreadsheet::formula(int row, int column) const { Cell *c = cell(row, column); if (c) { return c->formula(); // zwraca obliczoną formułę (liczy ją Cell) } else { return ""; } } void Spreadsheet::setFormula(int row, int column, const QString &formula) { Cell *c = cell(row, column); if (!c) { c = new Cell; setItem(row, column, c); } c->setFormula(formula); }

  12. Spreadsheet.cpp - podklasa QTableWidget QString Spreadsheet::currentLocation() const { return QChar('A' + currentColumn()) + QString::number(currentRow() + 1); } /* zwraca aktualny adres komórki np. „C16”; Metoda używana przez MainWindow::updateStatusBar */ QString Spreadsheet::currentFormula() const { return formula(currentRow(), currentColumn()); } /* zwraca formułę aktualnej komórki; Metoda używana przez MainWindow::updateStatusBar */ void Spreadsheet::somethingChanged() { if (autoRecalc) recalculate(); emit modified(); }

  13. Zapisywanie i odczytywanie arkusza bool Spreadsheet::writeFile(const QString &fileName)// użyta w MainWindow::saveFile() { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { // otworzenie pliku do zapisu QMessageBox::warning(this, tr("Spreadsheet"), tr("Cannot write file %1:\n%2.") .arg(file.fileName()) .arg(file.errorString())); return false; } QDataStream out(&file); // podobnie jak w <iostream> w czystym C++ out.setVersion(QDataStream::Qt_4_1); // ustawienie wersji strumienia  wersja 7 out << quint32(MagicNumber); // zmienna 32 bitowa; Gwarancja rozmiaru danej QApplication::setOverrideCursor(Qt::WaitCursor); // zmiana wyglądu kursora for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { QString str = formula(row, column); if (!str.isEmpty()) // zapis tylko komórek z zawartością out << quint16(row) << quint16(column) << str; } } QApplication::restoreOverrideCursor(); // przywrócenie poprzedniego kursora return true; }

  14. Zapisywanie i odczytywanie arkusza bool Spreadsheet::readFile(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("Spreadsheet"), tr("Cannot read file %1:\n%2.") .arg(file.fileName()) .arg(file.errorString())); return false; } QDataStream in(&file); in.setVersion(QDataStream::Qt_4_1); quint32 magic; in >> magic;

  15. Zapisywanie i odczytywanie arkusza if (magic != MagicNumber) { // sprawdzenie poprawności formatu pliku QMessageBox::warning(this, tr("Spreadsheet"), tr("The file is not a Spreadsheet file.")); return false; } clear (); // przed wczytywaniem zawsze trzeba wyczyścić arkusz quint16 row; quint16 column; QString str; QApplication::setOverrideCursor(Qt::WaitCursor); while (!in.atEnd()) { in >> row >> column >> str; setFormula(row, column, str); } QApplication::restoreOverrideCursor(); return true; }

  16. Implementacja Menu Edit void Spreadsheet::cut() { copy(); del(); }

  17. Implementacja Menu Edit void Spreadsheet::copy() { QTableWidgetSelectionRange range = selectedRange(); // odczyt zaznaczenia QString str; // zamiana zawartości wszystkich komórek na jednego stringa for (int i = 0; i < range.rowCount(); ++i) { if (i > 0) str += "\n"; // \n - separator wiersza for (int j = 0; j < range.columnCount(); ++j) { if (j > 0) str += "\t"; // \t - separator kolumny str += formula(range.topRow() + i, range.leftColumn() + j); } } QApplication::clipboard()->setText(str); } /* takim sposobem kopiowania zawartości komórek posługuje się m.in. Microsoft Excel */

  18. Implementacja Menu Edit QTableWidgetSelectionRange Spreadsheet::selectedRange() const { QList<QTableWidgetSelectionRange> ranges = selectedRanges(); if (ranges.isEmpty()) // teoretycznie niepotrzebne zabezpieczenie return QTableWidgetSelectionRange(); return ranges.first(); // zwraca pierwsze (i jedyne) zaznaczenie } void Spreadsheet::paste() { QTableWidgetSelectionRange range = selectedRange(); QStringstr = QApplication::clipboard()->text(); QStringList rows = str.split('\n'); // rozbicie stringa na wiersze int numRows = rows.count(); intnumColumns = rows.first().count('\t') + 1;// zliczenie liczby kolumn

  19. Implementacja Menu Edit if (range.rowCount() * range.columnCount() != 1 && (range.rowCount() != numRows || range.columnCount() != numColumns)) { QMessageBox::information(this, tr("Spreadsheet"), tr("The information cannot be pasted because the copy " "and paste areas aren't the same size.")); return; } for (int i = 0; i < numRows; ++i) { QStringList columns = rows[i].split('\t'); for (int j = 0; j < numColumns; ++j) { int row = range.topRow() + i; int column = range.leftColumn() + j; if (row < RowCount && column < ColumnCount) setFormula(row, column, columns[j]); } } somethingChanged(); }

  20. Implementacja Menu Edit void Spreadsheet::del() { foreach (QTableWidgetItem *item, selectedItems()) delete item; // automatyczny repaint po delete }

  21. Implementacja Menu Edit void Spreadsheet::selectCurrentRow() { selectRow(currentRow()); /* implementacja oparta na na metodzie dostarczonej przez QTableWidget */ } void Spreadsheet::selectCurrentColumn() { selectColumn(currentColumn()); /* implementacja oparta na na metodzie dostarczonej przez QTableWidget */ }

  22. Implementacja Menu Edit void Spreadsheet::findNext(const QString &str, Qt::CaseSensitivitycs) { int row = currentRow(); int column = currentColumn() + 1; /* przeszukiwanie od aktywnej komórki do końca arkusza */ while (row < RowCount) { while (column < ColumnCount) { if (text(row, column).contains(str, cs)) { clearSelection(); setCurrentCell(row, column); // przejście do komórki activateWindow(); // uaktywnienie okna ze SpreadSheet return; } ++column; } column = 0; ++row; } QApplication::beep(); // użycie dźwięku systemowego

  23. Implementacja pozostałych Menu void Spreadsheet::recalculate() { for (int row = 0; row < RowCount; ++row) { for (int column = 0; column < ColumnCount; ++column) { if (cell(row, column)) cell(row, column)->setDirty(); /* oznaczenie potrzeby ponownego przeliczenia. Przy wywołaniu metody text() na komórce jej zawartość zostanie zaktualizowana */ } } viewport()->update(); // wywołanie repaint na całym arkuszu }

  24. Implementacja pozostałych Menu void Spreadsheet::setAutoRecalculate(bool recalc) { autoRecalc = recalc; if (autoRecalc) recalculate(); } void Spreadsheet::sort(const SpreadsheetCompare &compare) { QList<QStringList> rows; QTableWidgetSelectionRange range = selectedRange(); int i;

  25. Implementacja Menu Edit for (i = 0; i < range.rowCount(); ++i) { QStringList row; for (int j = 0; j < range.columnCount(); ++j) row.append(formula(range.topRow() + i, range.leftColumn() + j)); rows.append(row); } qStableSort(rows.begin(), rows.end(), compare); // sortowanie stabilne; Sortowanie względem formuł, a nie wartości (dla uproszczenia); compare jest funkcją porównującą dwie wartości i zwracajacą wartość logiczną przy porównaniu „<‘’ for (i = 0; i < range.rowCount(); ++i) { for (int j = 0; j < range.columnCount(); ++j) setFormula(range.topRow() + i, range.leftColumn() + j, rows[i][j]); } clearSelection(); somethingChanged(); }

  26. Implementacja pozostałych Menu Rys. Przebieg sortowania

  27. Implementacja pozostałych Menu boolSpreadsheetCompare::operator()(const QStringList &row1, const QStringList &row2) const { for (int i = 0; i < KeyCount; ++i) { int column = keys[i]; if (column != -1) { if (row1[column] != row2[column]) { if (ascending[i]) { return row1[column] < row2[column]; } else { return row1[column] > row2[column]; } } } } return false; }

  28. Cell.h - Podklasa QTableWidgetItem #ifndef CELL_H #define CELL_H #include <QTableWidgetItem> class Cell : public QTableWidgetItem { public: Cell(); QTableWidgetItem *clone() const; void setData(int role, const QVariant &value); QVariant data(int role) const; // odpowiednik unii z C++; Niektóre komórki zawierają tyb double, a niektóre Qstring, dlatego trzeba mieć swobodę w interpretowaniu obszaru pamięci void setFormula(const QString &formula); QString formula() const; void setDirty();

  29. Cell.h - Podklasa QTableWidgetItem private: QVariant value() const; QVariant evalExpression(const QString &str, int &pos) const; QVariant evalTerm(const QString &str, int &pos) const; QVariant evalFactor(const QString &str, int &pos) const; mutable QVariant cachedValue; // mutable – można modyfikować pole w funkcjach „const” mutable bool cacheIsDirty; /* True – jeżeli komórka jest zmodyfikowana, a nie została zapisana */ }; #endif

  30. Cell.cpp - Podklasa QTableWidgetItem #include <QtGui> #include "cell.h" Cell::Cell() { setDirty(); } QTableWidgetItem *Cell::clone() const { return new Cell(*this); } // metoda używana przez QTableWidget, gdy jest tworzona nowa komórka void Cell::setFormula(const QString &formula) { setData(Qt::EditRole, formula); }

  31. Cell.cpp - Podklasa QTableWidgetItem QString Cell::formula() const { return data(Qt::EditRole).toString(); } void Cell::setData(int role, const QVariant &value) { QTableWidgetItem::setData(role, value); if (role == Qt::EditRole) setDirty(); } void Cell::setDirty() { cacheIsDirty = true; } // po wywołaniu funkcji cachedValue nie jest uznawane za aktualne

  32. Cell.cpp - Podklasa QTableWidgetItem QVariant Cell::data(int role) const// reimplementacja z QTableWidgetItem { if (role == Qt::DisplayRole) { // tryb wyświetlania if (value().isValid()) { return value().toString(); } else { return "####"; } } else if (role == Qt::TextAlignmentRole) {// tryb rozmieszczenia if (value().type() == QVariant::String) { return int(Qt::AlignLeft | Qt::AlignVCenter); } else { return int(Qt::AlignRight | Qt::AlignVCenter); } } else { return QTableWidgetItem::data(role); } }

  33. Cell.cpp - Podklasa QTableWidgetItem const QVariant Invalid; QVariant Cell::value() const { if (cacheIsDirty) { cacheIsDirty = false; QString formulaStr = formula(); if (formulaStr.startsWith('\'')) { cachedValue = formulaStr.mid(1); // kopiowanie stringa bez „\” } else if (formulaStr.startsWith('=')) { cachedValue = Invalid; QString expr = formulaStr.mid(1); expr.replace(" ", ""); expr.append(QChar::Null); int pos = 0; cachedValue = evalExpression(expr, pos); // obliczenie wyr. if (expr[pos] != QChar::Null) cachedValue = Invalid;

  34. Cell.cpp - Podklasa QTableWidgetItem } else { bool ok; double d = formulaStr.toDouble(&ok); if (ok) { cachedValue = d; } else { cachedValue = formulaStr; } } } return cachedValue; }

  35. Cell.cpp - Podklasa QTableWidgetItem QVariantCell::evalExpression(constQString &str, int &pos) const { QVariant result = evalTerm(str, pos); while (str[pos] != QChar::Null) { QChar op = str[pos]; if (op != '+' && op != '-') return result; ++pos; QVariant term = evalTerm(str, pos); if (result.type() == QVariant::Double && term.type() == QVariant::Double) { if (op == '+') { result = result.toDouble() + term.toDouble(); } else { result = result.toDouble() - term.toDouble(); } } else { result = Invalid; } } return result; }

  36. Cell.cpp - Podklasa QTableWidgetItem QVariant Cell::evalTerm(const QString &str, int &pos) const { QVariant result = evalFactor(str, pos); while (str[pos] != QChar::Null) { QChar op = str[pos]; if (op != '*' && op != '/') return result; ++pos; QVariant factor = evalFactor(str, pos); if (result.type() == QVariant::Double && factor.type() == QVariant::Double) { if (op == '*') { result = result.toDouble() * factor.toDouble(); } else { if (factor.toDouble() == 0.0) { result = Invalid; } else { result = result.toDouble() / factor.toDouble(); } } } else { result = Invalid; } } return result; }

  37. Cell.cpp - Podklasa QTableWidgetItem QVariant Cell::evalFactor(const QString &str, int &pos) const { QVariant result; bool negative = false; if (str[pos] == '-') { negative = true; ++pos; } if (str[pos] == '(') { ++pos; result = evalExpression(str, pos); If (str[pos] != ')') result = Invalid; ++pos; } else { QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); QString token; while (str[pos].isLetterOrNumber() || str[pos] == '.') { token += str[pos]; ++pos; }

  38. Cell.cpp - Podklasa QTableWidgetItem if (regExp.exactMatch(token)) { int column = token[0].toUpper().unicode() - 'A'; int row = token.mid(1).toInt() - 1; Cell *c = static_cast<Cell *>( tableWidget()->item(row, column)); if (c) { result = c->value(); } else { result = 0.0; } } else { bool ok; result = token.toDouble(&ok); if (!ok) result = Invalid; } } if (negative) { if (result.type() == QVariant::Double) { result = -result.toDouble(); } else { result = Invalid; } } return result; }

More Related