Пример 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 структура документа для этой части описана в таблице ниже:
фрейм с 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()
{
#ifndef QT_NO_PRINTER
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);
#endif
}
Эта функция так же позволяет пользователю распечатать выделенную с помощью 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();
}
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) |
Торговые марки |
Qt 4.5.3 |
|