Qt 富文本处理(26):Order Form Example【来自官档的翻译】

前言

 订单表单示例显示了如何通过将简单模板与用户在对话框中输入的数据相结合来生成富文本文档。

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。 该类定义了四个getter函数:orderItems( ),senderName( ),senderAddress( )和sendOffers( ),以允许从外部访问数据。
  类定义包括必填字段的输入小部件,即nameEdit和addressEdit。 此外,还定义了QCheckBox和QDialogButtonBox。 前者为用户提供接收有关产品和报价信息的选项,后者则确保根据用户的本机平台排列使用的按钮。 另外,一个QTableWidget项目表用于保存订单明细。
  下面的屏幕快照显示了我们打算创建的DetailsDialog。

DetailsDialog 类实现

  DetailsDialog的构造函数实例化先前定义的字段及其各自的标签。 设置offersCheckBox的标签,并调用setupItemsTable( )函数来设置和填充itemsTable。 QDialogButtonBox对象buttonBox用“确定”和“取消”按钮实例化。 该buttonBox的accepted( )和rejected( )信号连接到DetailsDialog中的verify( )和reject( )插槽。

#include "DetailsDialog.h"

DetailsDialog::DetailsDialog(const QString &title, QWidget *parent)
    : QDialog(parent)
{
    nameLabel = new QLabel("Name:");
    addressLabel = new QLabel("Address");
    addressLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);

    nameEdit = new QLineEdit;
    addressEdit = new QTextEdit;

    offersCheckBox = new QCheckBox("Send information about products and special offers");

    setupItemsTable();

    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

    connect(buttonBox,&QDialogButtonBox::accepted,this,&DetailsDialog::verify);
    connect(buttonBox,&QDialogButtonBox::rejected,this,&DetailsDialog::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);
}

/******************************************************************************************************
 * 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();
}

/******************************************************************************************************
 * 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::setnderAddress() const
{
    return addressEdit->toPlainText();
}

/******************************************************************************************************
 * sendOffers()函数用于返回true或false值,该值用于确定订单中的客户是否希望接收有关公司的优惠和促销的更多信息。
*/
bool DetailsDialog::sendOffers()
{
    return offersCheckBox->isChecked();
}

/******************************************************************************************************
 * createLetter( )函数创建一个新的QTabWidget,其QTextEdit编辑器作为父级。 该函数接受四个与我们通过DetailsDialog获得的参数相对应的参数,以“填充”编辑器。
 */
void DetailsDialog::setupItemsTable()
{
    items << "T-shirt" << "Badge" << "Reference book" << "Coffee cup";

    /* 实例化QTableWidget对象itemsTable,并基于QStringList对象items设置行数,该对象保存已排序项目的类型。
     * 列数设置为2,提供“名称”和“数量”布局。*/
    itemsTable = new QTableWidget(items.count(),2);

    QStringList header;
    header << "名称" << "数量";
    itemsTable->setHorizontalHeaderLabels(header);
    itemsTable->setColumnWidth(0,100);
    itemsTable->setColumnWidth(1,80);

    //使用for循环填充itemTable,将名称项的标志设置为Qt::ItemIsEnabled | Qt::ItemIsSelectable。
    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);
    }
}

MainWindow 类定义

MainWindow类是QMainWindow的子类,实现了两个插槽-openDialog( )和printFile( )。 它还包含QTabWidget的私有实例(字母)。

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。 这些动作的trigger( )信号连接到另外实现的openDialog( )插槽和默认的close( )插槽。 实例化QTabWidget(字母)并将其设置为窗口的中央窗口小部件。

#include "MainWindow.h"
#include <QtPrintSupport>


MainWindow::MainWindow()
{
    QMenu *fileMenu = new QMenu("&File",this);
    QAction *newAction = fileMenu->addAction("&New");
    newAction->setShortcuts(QKeySequence::New);

    printAction = fileMenu->addAction("&Print",this,&MainWindow::printFile);
    printAction->setShortcuts(QKeySequence::Print);
    printAction->setEnabled(false);

    QAction *quitAction = fileMenu->addAction("E&xit");
    quitAction->setShortcuts(QKeySequence::Quit);

    menuBar()->addMenu(fileMenu);

    letters = new QTabWidget;

    connect(newAction,&QAction::triggered,this,&MainWindow::openDialog);
    connect(quitAction,&QAction::triggered,this,&MainWindow::close);

    setCentralWidget(letters);
    setWindowTitle("Order Form");
}

/******************************************************************************************************
 *   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对象。
 * 如果接受对话框中的详细信息,则使用从对话框中提取的参数来调用createLetter( )函数。
 */
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( )函数,如下所示:
 * 此功能还允许用户使用QTextCursor::hasSelection( )打印选定区域,而不是打印整个文档。
 */
void MainWindow::printFile()
{
    QTextEdit *editor = static_cast<QTextEdit*>(letters->currentWidget());
    QPrinter printer;

    QPrintDialog dialog(&printer, this);
    dialog.setWindowTitle(tr("Print Document"));
    if (editor->textCursor().hasSelection())
        dialog.addEnabledOption(QAbstractPrintDialog::PrintSelection);
    if (dialog.exec() != QDialog::Accepted) {
        return;
    }

    editor->print(&printer);
}

/******************************************************************************************************
 * createLetter()函数创建一个新的QTabWidget,其QTextEdit编辑器作为父级。
 * 该函数接受四个与我们通过DetailsDialog获得的参数相对应的参数,以“填充”编辑器。
 */
void MainWindow::createLetter(const QString &name, const QString &address, QList<QPair<QString, int> > orderItems, bool sendOffers)
{
    QTextEdit *editor = new QTextEdit;
    QFont font = editor->font();
    font.setPointSize(12);
    editor->setFont(font);
    int tabIndex = letters->addTab(editor,name);
    letters->setCurrentIndex(tabIndex);

    //然后,我们使用QTextEdit::textCursor()获得编辑器的光标。
    QTextCursor cursor(editor->textCursor());
    //然后, 使用QTextCursor::Start将光标移动到文档的开头。
    cursor.movePosition(QTextCursor::Start);

    //回想一下富文本文档的结构,其中框架和表序列总是由文本块分隔,其中一些可能不包含任何信息。
    //在订单表单示例中,此部分的文档结构如下所示:
    /* 具有referenceFrameFormat格式的框架
           block    A company
           block
           block    321 City Street
           block
           block    Industry Park
           block
           block    Another country
    */

    // 这是通过以下代码完成的:
    // 请注意,topFrame是编辑器的顶级框架,未在文档结构中显示。
    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");
    cursor.insertBlock();



    //然后,我们将光标的位置重新设置为topFrame中的最后一个位置,
    //并填写客户的名称(由构造函数提供)和地址-使用基于范围的for循环遍历QString地址。
    cursor.setPosition(topFrame->lastPosition());

    cursor.insertText(name,textFormat);
    const QStringList lines = address.split('\n');

    for(const QString &line : lines ) {
        cursor.insertBlock();
        cursor.insertText(line);
    }

    //光标现在回到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("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
        frame with bodyFrameFormat
                            block   I would like to place an order for the following items:
                            block
                            block
    */

    // QTextTableFormat对象orderTableFormat用于保存项目的类型和订购的数量。
    QTextTableFormat orderTableFormat;
    orderTableFormat.setAlignment(Qt::AlignCenter);
    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("Product",boldFormat);
    cursor = orderTable->cellAt(0,1).firstCursorPosition();
    cursor.insertText("Quantity",boldFormat);

    // 然后,我们遍历QPair对象的QList来填充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 with orderTableFormat
                    block   Product
                    block   Quantity
                    block   T-shirt
                    block   4
                    block   Badge
                    block   3
                    block   Reference book
                    block   2
                    block   Coffee cup
                    block   5
    */

    // 然后将光标移回topFrame的lastPosition( )并插入更多标准文本。
    cursor.setPosition(topFrame->lastPosition());

    cursor.insertBlock();
    cursor.insertText("Please update my records to take account of the following privacy information:");

    // 插入另一个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 receive...
                            block   X
    */

    // 移动光标以插入“ 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
     */
}

主函数main()

main( )函数实例化MainWindow并在调用show( )函数和createSample( )函数之前将其大小设置为640x480像素。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.resize(640, 480);
    window.show();
    window.createSample();
    return app.exec();
}

总结

订单案例详细地解释了文档框架、表格、文本块的使用方式,挺好的一篇的教程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值