Главная · Все классы · Основные классы · Классы по группам · Модули · Функции

Пример 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
blockA company
block
block321 City Street
block
blockIndustry Park
block
blockAnother 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 и структура документа для части кода выше следующая:

blockDonald
block47338 Park Avenue
blockBig 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
blockDate: 25 May 2007
block
фрейм с bodyFrameFormat
blockI 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
blockProduct
blockQuantity
blockT-shirt
block4
blockBadge
block3
blockReference book
block2
blockCoffee cup
block5

Затем 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
blockPlease update my...
block
offersTable
blockI want to receive...
blockI do not want to recieve...
blockX

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
blockSincerely,
block
block
block
blockDonald

Функция 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;
     [fuzzy] window.resize(200, 120);
     window.show();
     window.createSample();
     return app.exec();
 }


Copyright © 2008 Trolltech Торговые марки
Qt 4.3.5