![]() |
Главная · Все классы · Основные классы · Классы по группам · Модули · Функции | ![]() |
Файлы:
Пример Order Form показывает как генерировать документ форматированного текста комбинированием простых шаблонов с данными, введенными в диалоге пользователем. Данные извлекаются из объекта DetailsDialog и отображаются в QTextEdit с QTextCursor используя различные форматы. Каждая сгенерированная форма добавляется в QTabWidget для легкого доступа.
Подкласс DetailsDialog класса QDialog реализует слот verify() что бы позволить проверять содержимое DetailsDialog позже. Это объяснено далее в реализации DetailsDialog.
class DetailsDialog : public QDialog { Q_OBJECT public: DetailsDialog(const QString &title, QWidget *parent); public slots: void verify(); public: QList<QPair<QString, int> > orderItems(); QString senderName() const; QString senderAddress() const; bool sendOffers(); private: void setupItemsTable(); QLabel *nameLabel; QLabel *addressLabel; QCheckBox *offersCheckBox; QLineEdit *nameEdit; QStringList items; QTableWidget *itemsTable; QTextEdit *addressEdit; QDialogButtonBox *buttonBox; };
Конструктор DetailsDialog принимает параметры title и parent. Класс описывает геттерные функции: orderItems(), senderName(), senderAddress() и sendOffers() что бы получать данные снаружи.
Описание функции включает виджеты ввода для требуемых полей nameEdit и addressEdit. Также определены QCheckBox и QDialogButtonBox; первый для предоставления пользователю опции получать информацию о продуктах и предложениях и второй для гарантирования того что используемые кнопки упорядочены в соответствии с родной платформой пользователя. Кроме того, QTableWidget, itemsTable используются для хранения подробностей заказа.
Скриншот ниже показывает DetailsDialog который мы хотим создать.
Конструктор DetailsDialog создает экземпляры ранее определенных полей и их соответствующих меток. Метка offersCheckBox установлена и функция setupItemsTable() вызывается для настройки и заселения itemsTable. Объект QDialogButtonBox buttonBox создается с кнопками OK и Cancel. Сигналы buttonBox accepted() и rejected() соединяются со слотами verify() и reject() в DetailsDialog.
DetailsDialog::DetailsDialog(const QString &title, QWidget *parent) : QDialog(parent) { nameLabel = new QLabel(tr("Name:")); addressLabel = new QLabel(tr("Address:")); addressLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); nameEdit = new QLineEdit; addressEdit = new QTextEdit; offersCheckBox = new QCheckBox(tr("Send information about products and " "special offers")); setupItemsTable(); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(verify())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
QGridLayout используется для размещения всех объектов на DetailsDialog.
QGridLayout *mainLayout = new QGridLayout; mainLayout->addWidget(nameLabel, 0, 0); mainLayout->addWidget(nameEdit, 0, 1); mainLayout->addWidget(addressLabel, 1, 0); mainLayout->addWidget(addressEdit, 1, 1); mainLayout->addWidget(itemsTable, 0, 2, 2, 1); mainLayout->addWidget(offersCheckBox, 2, 1, 1, 2); mainLayout->addWidget(buttonBox, 3, 0, 1, 3); setLayout(mainLayout); setWindowTitle(title); }
Функция setupItemsTable() создает объект QTableWidget itemsTable и устанавливает число строк основываясь на объекте QStringList items который упорядоченно содержит типы элементов. Число столбцов устанавливается равным двум, обеспечивая расположение "name" и "quantity". Цикл for используется для заселения itemsTable флаг элемента name установлен в Qt::ItemIsEnabled или Qt::ItemIsSelectable. С целью демонстрации элемент quantity устанавливается равным 1 и все элементы в itemsTable будут иметь это значение для количества; но оно может быть изменено редактированием содержимого ячеек во время выполнения.
void DetailsDialog::setupItemsTable() { items << tr("T-shirt") << tr("Badge") << tr("Reference book") << tr("Coffee cup"); itemsTable = new QTableWidget(items.count(), 2); for (int row = 0; row < items.count(); ++row) { QTableWidgetItem *name = new QTableWidgetItem(items[row]); name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); itemsTable->setItem(row, 0, name); QTableWidgetItem *quantity = new QTableWidgetItem("1"); itemsTable->setItem(row, 1, quantity); } }
Функция orderItems() извлекает данные из itemsTable и возвращает их в форме QList<QPair<QString,int>> где каждая QPair соответствует элементу и упорядочена по количеству.
QList<QPair<QString, int> > DetailsDialog::orderItems() { QList<QPair<QString, int> > orderList; for (int row = 0; row < items.count(); ++row) { QPair<QString, int> item; item.first = itemsTable->item(row, 0)->text(); int quantity = itemsTable->item(row, 1)->data(Qt::DisplayRole).toInt(); item.second = qMax(0, quantity); orderList.append(item); } return orderList; }
Функция senderName() используется для возвращения значения QLineEdit используемого для хранения поля имени для формы заказа.
QString DetailsDialog::senderName() const { return nameEdit->text(); }
Функция senderAddress() используется для возвращения значения QTextEdit содержащего адрес для формы заказа.
QString DetailsDialog::senderAddress() const { return addressEdit->toPlainText(); }
Функция sendOffers() используется для возвращения значения true или false которое используется для определения желает ли клиент получать больше информации о предложениях и рекламах компании.
bool DetailsDialog::sendOffers() { return offersCheckBox->isChecked(); }
Функция verify() это дополнительно реализованный слот используемый для проверки подробностей введенных пользователей в DetailsDialog. Если введенные подробности незавершены, то отображается QMessageBox предоставляя пользователю возможность отменить DetailsDialog. В противном случае подробности принимаются и вызывается функция accept().
void DetailsDialog::verify() { if (!nameEdit->text().isEmpty() && !addressEdit->toPlainText().isEmpty()) { accept(); return; } QMessageBox::StandardButton answer; answer = QMessageBox::warning(this, tr("Incomplete Form"), tr("The form does not contain all the necessary information.\n" "Do you want to discard it?"), QMessageBox::Yes | QMessageBox::No); if (answer == QMessageBox::Yes) reject(); }
Подкласс MainWindow класса QMainWindow реализует два слота - openDialog() и printFile(). Он также содержит приватный экземпляр QTabWidget, letters.
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); void createSample(); public slots: void openDialog(); void printFile(); private: void createLetter(const QString &name, const QString &address, QList<QPair<QString,int> > orderItems, bool sendOffers); QAction *printAction; QTabWidget *letters; };
Конструктор MainWindow устанавливает fileMenu и требуемые действия newAction и printAction. Сигналы triggered() этих действий соединенны дополнительно реализованному слоту openDialog() и слоту по умолчанию close(). QTabWidget letters создается и устанавливается в качестве центрального виджета окна.
MainWindow::MainWindow() { QMenu *fileMenu = new QMenu(tr("&File"), this); QAction *newAction = fileMenu->addAction(tr("&New...")); newAction->setShortcut(tr("Ctrl+N")); printAction = fileMenu->addAction(tr("&Print..."), this, SLOT(printFile())); printAction->setShortcut(tr("Ctrl+P")); printAction->setEnabled(false); QAction *quitAction = fileMenu->addAction(tr("E&xit")); quitAction->setShortcut(tr("Ctrl+Q")); menuBar()->addMenu(fileMenu); letters = new QTabWidget; connect(newAction, SIGNAL(triggered()), this, SLOT(openDialog())); connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); setCentralWidget(letters); setWindowTitle(tr("Order Form")); }
Функция createLetter() создает новый QTabWidget с QTextEdit editor в качестве его родителя. Функция принимает четыре параметра которые которые соответствуют полученным нами через DetailsDialog для того, чтобы "заполнить" editor.
void MainWindow::createLetter(const QString &name, const QString &address, QList<QPair<QString,int> > orderItems, bool sendOffers) { QTextEdit *editor = new QTextEdit; int tabIndex = letters->addTab(editor, name); letters->setCurrentIndex(tabIndex);
Затем мы получаем курсор для editor используя QTextEdit::textCursor(). Затем cursor перемещается в начало документа используя QTextCursor::Start.
QTextCursor cursor(editor->textCursor()); cursor.movePosition(QTextCursor::Start);
Напомним структуру Документа форматированного текста где последовательность фреймов и таблиц всегда разделены текстовыми блоками некоторые из которых могут не содержать информацию.
В случае примера Order Form структура документа для этой части описана в таблице ниже:
фрейм с referenceFrameFormat | |
block | A company |
block | |
block | 321 City Street |
block | |
block | Industry Park |
block | |
block | Another country |
Это выполняется следующим кодом:
QTextFrame *topFrame = cursor.currentFrame(); QTextFrameFormat topFrameFormat = topFrame->frameFormat(); topFrameFormat.setPadding(16); topFrame->setFrameFormat(topFrameFormat); QTextCharFormat textFormat; QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); QTextFrameFormat referenceFrameFormat; referenceFrameFormat.setBorder(1); referenceFrameFormat.setPadding(8); referenceFrameFormat.setPosition(QTextFrameFormat::FloatRight); referenceFrameFormat.setWidth(QTextLength(QTextLength::PercentageLength, 40)); cursor.insertFrame(referenceFrameFormat); cursor.insertText("A company", boldFormat); cursor.insertBlock(); cursor.insertText("321 City Street"); cursor.insertBlock(); cursor.insertText("Industry Park"); cursor.insertBlock(); cursor.insertText("Another country");
Заметьте, что topFrame это фрейм верхнего уровня в editor и не показан в структуре документа.
Затем мы возвращаем cursor в его последнюю позицию в topFrame и заполняем имя клиента (предоставляется конструктором) и адрес - используя цикл foreach для обхода QString address.
cursor.setPosition(topFrame->lastPosition()); cursor.insertText(name, textFormat); QString line; foreach (line, address.split("\n")) { cursor.insertBlock(); cursor.insertText(line); }
Сейчас cursor вернулся в topFrame и структура документа для части кода выше следующая:
block | Donald |
block | 47338 Park Avenue |
block | Big City |
С целью расположения с интервалами мы вызываем insertBlock() дважды. currentDate() получается и отображается. Мы используем setWidth() для увеличения ширины bodyFrameFormat и мы вставляем новый фрейм с этой шириной.
cursor.insertBlock(); cursor.insertBlock(); QDate date = QDate::currentDate(); cursor.insertText(tr("Date: %1").arg(date.toString("d MMMM yyyy")), textFormat); cursor.insertBlock(); QTextFrameFormat bodyFrameFormat; bodyFrameFormat.setWidth(QTextLength(QTextLength::PercentageLength, 100)); cursor.insertFrame(bodyFrameFormat);
Следующий код вставляет стандартный текст в форму заказа.
cursor.insertText(tr("I would like to place an order for the following " "items:"), textFormat); cursor.insertBlock(); cursor.insertBlock();
Эта часть структуры документа сейчас содержит дату, фрейм с bodyFrameFormat, так же как и стандартный текст.
block | |
block | |
block | Date: 25 May 2007 |
block | |
фрейм с bodyFrameFormat | |
block | I would like to place an order for the following items: |
block | |
block |
Объект QTextTableFormat orderTableFormat используется для хранения типа элемента и заказанное количество.
QTextTableFormat orderTableFormat; orderTableFormat.setAlignment(Qt::AlignHCenter); QTextTable *orderTable = cursor.insertTable(1, 2, orderTableFormat); QTextFrameFormat orderFrameFormat = cursor.currentFrame()->frameFormat(); orderFrameFormat.setBorder(1); cursor.currentFrame()->setFrameFormat(orderFrameFormat);
Мы используем cellAt() что бы установить заголовки для orderTable.
cursor = orderTable->cellAt(0, 0).firstCursorPosition(); cursor.insertText(tr("Product"), boldFormat); cursor = orderTable->cellAt(0, 1).firstCursorPosition(); cursor.insertText(tr("Quantity"), boldFormat);
Затем мы перебираем QList объектов QPair что бы заселить orderTable.
for (int i = 0; i < orderItems.count(); ++i) { QPair<QString,int> item = orderItems[i]; int row = orderTable->rows(); orderTable->insertRows(row, 1); cursor = orderTable->cellAt(row, 0).firstCursorPosition(); cursor.insertText(item.first, textFormat); cursor = orderTable->cellAt(row, 1).firstCursorPosition(); cursor.insertText(QString("%1").arg(item.second), textFormat); }
Результирующая структура документа для этой секции следующая::
orderTable с orderTableFormat | |
block | Product |
block | Quantity |
block | T-shirt |
block | 4 |
block | Badge |
block | 3 |
block | Reference book |
block | 2 |
block | Coffee cup |
block | 5 |
Затем cursor возвращается в topFrame в последнюю позицию lastPosition() и вставляется дополнительный стандартный текст.
cursor.setPosition(topFrame->lastPosition()); cursor.insertBlock(); cursor.insertText(tr("Please update my records to take account of the " "following privacy information:")); cursor.insertBlock();
Другая QTextTable вставляется для отображения предпочтений клиента относительно предложений.
QTextTable *offersTable = cursor.insertTable(2, 2); cursor = offersTable->cellAt(0, 1).firstCursorPosition(); cursor.insertText(tr("I want to receive more information about your " "company's products and special offers."), textFormat); cursor = offersTable->cellAt(1, 1).firstCursorPosition(); cursor.insertText(tr("I do not want to receive any promotional information " "from your company."), textFormat); if (sendOffers) cursor = offersTable->cellAt(0, 0).firstCursorPosition(); else cursor = offersTable->cellAt(1, 0).firstCursorPosition(); cursor.insertText("X", boldFormat);
Структура документа для этой части будет:
block | |
block | Please update my... |
block | |
offersTable | |
block | I want to receive... |
block | I do not want to recieve... |
block | X |
cursor перемещается для вставки "Sincerely" вместе с именем клиента. Еще несколько блоков вставляются с целью отступа. printAction разблокируется что бы показать что форма заказа может быть теперь распечатана.
cursor.setPosition(topFrame->lastPosition()); cursor.insertBlock(); cursor.insertText(tr("Sincerely,"), textFormat); cursor.insertBlock(); cursor.insertBlock(); cursor.insertBlock(); cursor.insertText(name); printAction->setEnabled(true); }
Нижняя часть структуры документа:
block | |
block | Sincerely, |
block | |
block | |
block | |
block | Donald |
Функция createSample() используется с целью иллюстрации для создания примера формы заказа.
void MainWindow::createSample() { DetailsDialog dialog("Dialog with default values", this); createLetter("Mr. Smith", "12 High Street\nSmall Town\nThis country", dialog.orderItems(), true); }
Функция openDialog() открывает объект DetailsDialog. Если подробности в dialog принимаются, функция createLetter() вызывается с использованием параметров извлеченных из dialog.
void MainWindow::openDialog() { DetailsDialog dialog(tr("Enter Customer Details"), this); if (dialog.exec() == QDialog::Accepted) createLetter(dialog.senderName(), dialog.senderAddress(), dialog.orderItems(), dialog.sendOffers()); }
Для того, чтобы распечатать форму заказа, включается функция printFile() как показано ниже:
void MainWindow::printFile() { QTextEdit *editor = static_cast<QTextEdit*>(letters->currentWidget()); QPrinter printer; [fuzzy] QPrintDialog *dlg = new QPrintDialog(&printer, this); dialog->setWindowTitle(tr("Print Document")); if (editor->textCursor().hasSelection()) dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); [fuzzy] if (dlg->exec() != QDialog::Accepted) return; editor->print(&printer); }
Эта функция так же позволяет пользователю распечатать выделенную с помощью QTextCursor::hasSelection() область вместо печати всего документа.
Функция main() создает экземпляр MainWindow и устанавливает его размер равным 640x480 пикселов прежде чем вызвать функцию show() и функцию createSample().
int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; [fuzzy] window.resize(200, 120); window.show(); window.createSample(); return app.exec(); }
Copyright © 2008 Trolltech | Торговые марки | Qt 4.3.5 |