주문 양식 예제

주문 양식 예제는 간단한 템플릿과 사용자가 대화 상자에 입력한 데이터를 결합하여 서식 있는 텍스트 문서를 생성하는 방법을 보여줍니다.

디테일 대화 상자 정의

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 의 생성자는 titleparent 파라미터를 받습니다. 이 클래스는 네 개의 게터 함수를 정의합니다: orderItems(), senderName(), senderAddress(), sendOffers() 를 정의하여 외부에서 데이터에 액세스할 수 있도록 합니다.

클래스 정의에는 필수 필드인 nameEditaddressEdit 에 대한 입력 위젯이 포함되어 있습니다. 또한 QCheckBoxQDialogButtonBox 이 정의되어 있는데, 전자는 사용자에게 제품 및 오퍼에 대한 정보를 수신할 수 있는 옵션을 제공하고 후자는 사용자의 기본 플랫폼에 따라 사용되는 버튼이 정렬되도록 합니다. 또한 QTableWidget, itemsTable 은 주문 세부 정보를 보관하는 데 사용됩니다.

아래 스크린샷은 우리가 만들려는 DetailsDialog 을 보여줍니다.

세부 정보 다이얼로그 구현

DetailsDialog 의 생성자는 앞서 정의한 필드와 해당 레이블을 인스턴스화합니다. offersCheckBox 에 대한 레이블이 설정되고 setupItemsTable() 함수가 호출되어 itemsTable 를 설정하고 채웁니다. QDialogButtonBox 객체 buttonBoxOKCancel 버튼으로 인스턴스화됩니다. 이 buttonBoxaccepted()rejected() 신호는 DetailsDialogverify()reject() 슬롯에 연결됩니다.

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

QGridLayoutDetailsDialog 에 모든 객체를 배치하는 데 사용됩니다.

    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 를 기준으로 행 수를 설정합니다. 열 수는 2로 설정되어 "이름" 및 "수량" 레이아웃을 제공합니다. 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 클래스는 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 과 필수 액션인 newActionprintAction 을 설정합니다. 이러한 액션의 triggered() 신호는 추가적으로 구현된 openDialog() 슬롯과 기본 close() 슬롯에 연결됩니다. QTabWidget , letters 은 인스턴스화되어 창의 중앙 위젯으로 설정됩니다.

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

createLetter() 함수는 QTextEdit, editor 을 부모로 하여 QTabWidget 을 새로 만듭니다. 이 함수는 editor 을 "채우기" 위해 DetailsDialog 을 통해 얻은 네 개의 매개 변수를 받아들입니다.

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

그런 다음 QTextEdit::textCursor()을 사용하여 editor 에 대한 커서를 얻습니다. 그런 다음 QTextCursor::Start 을 사용하여 cursor 을 문서의 시작 부분으로 이동합니다.

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

프레임과 표의 시퀀스는 항상 텍스트 블록으로 구분되며, 그 중 일부는 정보가 없을 수도 있는 서식 있는 텍스트 문서의 구조를 떠올려 보세요.

주문서 양식 예시의 경우 이 부분의 문서 구조는 아래 표에 설명되어 있습니다:

참조 프레임 형식이 있는 프레임
블록A company
블록
블록321 City Street
블록
블록Industry 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");

topFrameeditor 의 최상위 프레임이며 문서 구조에는 표시되지 않습니다.

그런 다음 cursor 의 위치를 topFrame 의 마지막 위치로 다시 설정하고 범위 기반 for 루프를 사용하여 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);
    }

이제 cursortopFrame 으로 돌아왔으며 위 코드 부분의 문서 구조는 다음과 같습니다:

블록Donald
블록47338 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 프레임이 포함됩니다.

블록
블록
블록Date: 25 May 2007
블록
프레임과 bodyFrameFormat
블록I would like to place an order for the following items:
블록
블록

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

그런 다음 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 주문 테이블 형식
블록Product
블록Quantity
블록T-shirt
블록4
블록Badge
block3
blockReference book
block2
blockCoffee cup
블록 블록5

그런 다음 cursortopFramelastPosition()로 다시 이동하고 더 많은 표준 텍스트가 삽입됩니다.

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

이 부분의 문서 구조는

블록
블록Please update my...
블록
offersTable
블록I want to receive...
블록I do not want to receive...
블록 블록X

cursor 을 이동하여 고객 이름과 함께 "성심성의껏"을 삽입합니다. 띄어쓰기를 위해 더 많은 블록이 삽입됩니다. printAction 이 활성화되어 주문 양식을 인쇄할 수 있음을 나타냅니다.

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

    printAction->setEnabled(true);
}

문서 구조의 하단 부분은

블록
블록Sincerely,
블록
블록
블록
블록 블록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 의 세부 정보가 수락되면 dialog 에서 추출한 매개 변수를 사용하여 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() 함수가 포함되어 있습니다:

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
}

이 함수를 사용하면 전체 문서를 인쇄하는 대신 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();
}

예제 프로젝트 @ code.qt.io

© 2025 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.