En esta página

Ejemplo de formulario de pedido

El ejemplo de formulario de pedido muestra cómo generar documentos de texto enriquecido combinando una plantilla sencilla con datos introducidos por el usuario en un cuadro de diálogo.

Aplicación que muestra un formulario de pedido cumplimentado para un cliente

Definición de DetailsDialog

La clase DetailsDialog es una subclase de QDialog, que implementa una ranura verify() para permitir que el contenido de DetailsDialog se verifique posteriormente. Esto se explica con más detalle en DetailsDialog Implementación.

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;
};

El constructor de DetailsDialog acepta los parámetros title y parent. La clase define cuatro funciones getter: orderItems(), senderName(), senderAddress(), y sendOffers() para permitir el acceso externo a los datos.

La definición de la clase incluye widgets de entrada para los campos obligatorios, nameEdit y addressEdit. También se definen un QCheckBox y un QDialogButtonBox; el primero para proporcionar al usuario la opción de recibir información sobre productos y ofertas, y el segundo para garantizar que los botones utilizados se disponen de acuerdo con la plataforma nativa del usuario. Además, se utiliza un QTableWidget, itemsTable, para guardar los detalles del pedido.

La siguiente captura de pantalla muestra el DetailsDialog que pretendemos crear.

Diálogo para introducir la información del pedido del cliente

Implementación de DetailsDialog

El constructor de DetailsDialog instanciará los campos definidos anteriormente y sus respectivas etiquetas. Se establece la etiqueta para offersCheckBox y se invoca la función setupItemsTable() para configurar y rellenar itemsTable. El objeto QDialogButtonBox, buttonBox, se instantiza con los botones OK y Cancel. Las señales accepted() y rejected() de buttonBox se conectan a las ranuras verify() y reject() de 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, &QDialogButtonBox::accepted, this, &DetailsDialog::verify);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &DetailsDialog::reject);

Un QGridLayout se utiliza para colocar todos los objetos en el 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);
}

La función setupItemsTable() crea una instancia del objeto QTableWidget, itemsTable, y establece el número de filas basándose en el objeto QStringList, items, que contiene el tipo de elementos ordenados. El número de columnas se establece en 2, proporcionando un diseño de "nombre" y "cantidad". Se utiliza un bucle for para rellenar itemsTable y la bandera del elemento name se establece en Qt::ItemIsEnabled o Qt::ItemIsSelectable. A efectos de demostración, el elemento quantity se establece en 1 y todos los elementos de itemsTable tienen este valor para la cantidad; pero esto puede modificarse editando el contenido de las celdas en tiempo de ejecución.

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);
    }
}

La función orderItems() extrae datos de itemsTable y los devuelve en forma de QList<QPair<QString,int>> donde cada QPair corresponde a un artículo y a la cantidad pedida.

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;
}

La función senderName() se utiliza para devolver el valor del QLineEdit utilizado para almacenar el campo de nombre para el formulario de pedido.

QString DetailsDialog::senderName() const
{
    return nameEdit->text();
}

La función senderAddress() devuelve el valor de QTextEdit que contiene la dirección de la orden de pedido.

QString DetailsDialog::senderAddress() const
{
    return addressEdit->toPlainText();
}

La función sendOffers() se utiliza para devolver un valor de true o false que se utiliza para determinar si el cliente del formulario de pedido desea recibir más información sobre las ofertas y promociones de la empresa.

bool DetailsDialog::sendOffers()
{
    return offersCheckBox->isChecked();
}

La función verify() es una ranura implementada adicionalmente que se utiliza para verificar los datos introducidos por el usuario en DetailsDialog. Si los datos introducidos están incompletos, se muestra un mensaje QMessageBox que ofrece al usuario la opción de descartar DetailsDialog. De lo contrario, los datos se aceptan y se invoca la función 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();
}

Definición de MainWindow

La clase MainWindow es una subclase de QMainWindow, que implementa dos ranuras - openDialog() y printFile(). También contiene una instancia privada de 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;
};

Implementación de MainWindow

El constructor MainWindow configura fileMenu y las acciones necesarias, newAction y printAction. Las señales triggered() de estas acciones están conectadas a la ranura openDialog() implementada adicionalmente y a la ranura por defecto close(). El QTabWidget, letters, es instanciado y establecido como widget central de la ventana.

MainWindow::MainWindow()
{
    QMenu *fileMenu = new QMenu(tr("&File"), this);
    QAction *newAction = fileMenu->addAction(tr("&New..."));
    newAction->setShortcuts(QKeySequence::New);
    printAction = fileMenu->addAction(tr("&Print..."), this, &MainWindow::printFile);
    printAction->setShortcuts(QKeySequence::Print);
    printAction->setEnabled(false);
    QAction *quitAction = fileMenu->addAction(tr("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(tr("Order Form"));
}

La función createLetter() crea un nuevo QTabWidget con un QTextEdit, editor, como padre. Esta función acepta cuatro parámetros que corresponden a obtenemos a través de DetailsDialog, para "rellenar" el 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);

A continuación, obtenemos el cursor para el editor utilizando QTextEdit::textCursor(). A continuación, el cursor se desplaza al principio del documento utilizando QTextCursor::Start.

    QTextCursor cursor(editor->textCursor());
    cursor.movePosition(QTextCursor::Start);

Recordemos la estructura de un documento de texto enriquecido, en el que las secuencias de cuadros y tablas están siempre separadas por bloques de texto, algunos de los cuales pueden no contener información.

En el caso del ejemplo de formulario de pedido, la estructura del documento para esta parte se describe en la siguiente tabla:

frame con referenceFrameFormat
bloqueA company
bloque
bloque321 City Street
bloque
bloqueIndustry Park
bloque
bloqueAnother country

Esto se consigue con el siguiente código:

    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");

Observe que topFrame es el marco de nivel superior de editor y no se muestra en la estructura del documento.

A continuación, fijamos la posición de cursor a su última posición en topFrame y rellenamos el nombre del cliente (proporcionado por el constructor) y la dirección - utilizando un bucle for basado en rangos para recorrer QString, address.

    cursor.setPosition(topFrame->lastPosition());

    cursor.insertText(name, textFormat);
    const QStringList lines = address.split('\n');
    for (const QString &line : lines) {
        cursor.insertBlock();
        cursor.insertText(line);
    }

El cursor está ahora de vuelta en topFrame y la estructura del documento para la porción de código anterior es:

bloqueDonald
bloque47338 Park Avenue
bloqueBig City

A efectos de espaciado, invocamos insertBlock() dos veces. Obtenemos y mostramos currentDate(). Utilizamos setWidth() para aumentar la anchura de bodyFrameFormat e insertamos un nuevo marco con esa anchura.

    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);

El siguiente código inserta texto estándar en el formulario de pedido.

    cursor.insertText(tr("I would like to place an order for the following "
                         "items:"), textFormat);
    cursor.insertBlock();
    cursor.insertBlock();

Esta parte de la estructura del documento contiene ahora la fecha, un marco con bodyFrameFormat, así como el texto estándar.

bloque
bloque
bloqueDate: 25 May 2007
bloque
marco con bodyFrameFormat
bloqueI would like to place an order for the following items:
bloque
bloque

Se utiliza un objeto QTextTableFormat, orderTableFormat, para contener el tipo de artículo y la cantidad pedida.

    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);

Utilizamos cellAt() para establecer las cabeceras de orderTable.

    cursor = orderTable->cellAt(0, 0).firstCursorPosition();
    cursor.insertText(tr("Product"), boldFormat);
    cursor = orderTable->cellAt(0, 1).firstCursorPosition();
    cursor.insertText(tr("Quantity"), boldFormat);

A continuación, iteramos a través de los objetos QList de QPair para rellenar 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);
    }

La estructura del documento resultante para esta sección es

orderTable con orderTableFormat
bloqueProduct
bloqueQuantity
bloqueT-shirt
bloque4
bloqueBadge
bloque3
bloqueReference book
bloque2
bloqueCoffee cup
bloque5

A continuación, cursor se desplaza de nuevo a topFrame's lastPosition() y se inserta más texto estándar.

    cursor.setPosition(topFrame->lastPosition());

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

Se inserta otro QTextTable para mostrar las preferencias del cliente en cuanto a ofertas.

    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);

La estructura del documento para esta parte es

bloque
bloquePlease update my...
bloque
offersTable
bloqueI want to receive...
bloqueI do not want to receive...
bloqueX

La dirección cursor se desplaza para insertar "Atentamente" junto al nombre del cliente. Se insertan más bloques para espaciar. Se activa el printAction para indicar que ya se puede imprimir una orden de pedido.

    cursor.setPosition(topFrame->lastPosition());
    cursor.insertBlock();
    cursor.insertText(tr("Sincerely,"), textFormat);
    cursor.insertBlock();
    cursor.insertBlock();
    cursor.insertBlock();
    cursor.insertText(name);

    printAction->setEnabled(true);
}

La parte inferior de la estructura del documento es

bloque
bloqueSincerely,
bloque
bloque
bloque
bloqueDonald

La función createSample() se utiliza con fines ilustrativos, para crear una orden de pedido de muestra.

void MainWindow::createSample()
{
    DetailsDialog dialog("Dialog with default values", this);
    createLetter("Mr. Smith", "12 High Street\nSmall Town\nThis country",
                 dialog.orderItems(), true);
}

La función openDialog() abre un objeto DetailsDialog. Si se aceptan los datos de dialog, se invoca la función createLetter() utilizando los parámetros extraídos de 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());
    }
}

Para imprimir el formulario de pedido, se incluye una función printFile(), como se muestra a continuación:

void MainWindow::printFile()
{
#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
    QTextEdit *editor = static_cast<QTextEdit*>(letters->currentWidget());
    QPrinter printer;

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

    editor->print(&printer);
#endif
}

Esta función también permite al usuario imprimir un área seleccionada con QTextCursor::hasSelection(), en lugar de imprimir todo el documento.

main() Función

La función main() instancia MainWindow y establece su tamaño en 640x480 píxeles antes de invocar la función show() y la función createSample().

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

Proyecto de ejemplo @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.