Пример Order Form
Файлы:
Пример Order Form показывает как генерировать документ форматированного текста комбинированием простых шаблонов с данными, введенными в диалоге пользователем. Данные извлекаются из объекта DetailsDialog и отображаются в QTextEdit с QTextCursor используя различные форматы. Каждая сгенерированная форма добавляется в QTabWidget для легкого доступа. Определение DetailsDialogПодкласс 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Конструктор 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Подкласс 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Конструктор 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 структура документа для этой части описана в таблице ниже:
Это выполняется следующим кодом: 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 и структура документа для части кода выше следующая:
С целью расположения с интервалами мы вызываем 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, так же как и стандартный текст.
Объект 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); } Результирующая структура документа для этой секции следующая::
Затем 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); Структура документа для этой части будет:
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); } Нижняя часть структуры документа:
Функция 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()Функция main() создает экземпляр MainWindow и устанавливает его размер равным 640x480 пикселов прежде чем вызвать функцию show() и функцию createSample(). int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.resize(640, 480); window.show(); window.createSample(); return app.exec(); }
|
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |