当前位置:网站首页>Unfinished mathematics test paper ----- test paper generator (Qt includes source code)

Unfinished mathematics test paper ----- test paper generator (Qt includes source code)

2022-08-10 14:00:00 Three ray technology

        I'm helping my daughter with math problems recently,Found that her thinking logic is no problem,Not enough is to practice,Therefore, errors often occur when calculating fractions and multiplying negative numbers.,Makes the whole question wrong,In order to let my daughter to get plenty of exercise on mathematical calculation,I don't want her to be exposed to electronic devices too much while exercising.I developed an automatic generation of test paper(pdf)的程序,and can print directly,Then she'll be able to have papers that she can't finish writing..(My daughter thanked me so much)

 一、程序设计

1)Frame structure description

The whole program can be divided into several parts:界面部分、Generate topic section、写pdf文件部分.One of the most complex is writtenpdf文件,Because of the large number of position calculations involved.

界面部分:Used to present and collect user needs,Generate different test papers according to the needs of users.The interface design diagram is as follows:

Fractions and integers can be generated from probabilities,Generate negative and positive numbers based on probabilities,Random range with adjustable numerator and denominator.

设置文件保存的路径.

 生成题目:I only designed multiplication here,Therefore, the main difficulty of generating questions is to generate corresponding numbers according to the parameters passed in the interface part.,Because of the score,So we need to build our own class or structure to store the score.

写pdf部分:The content of this part is to draw the topic to the corresponding position according to the generated topic,This part involves the location and settlement so is more complex.

The final generated question is as follows:

 2)类图关系

        Students who are just starting to learn programming must be used to seeing class diagrams,Because the class diagram will allow you to quickly grasp the functions and relationships of each class.尤其是对于大型项目来说,It is unlikely that you can expect to understand the overall functionality just by reading the code.

 MainWindow:Handling key information transactions,Dispatch tasks to other classes to complete.

MainWindowPrivate:for handler layout,And get control information, etc.,Here, the initialization of the data is also placed in the general class(As business grows,Initialization of data should be managed by a separate class,And to provide fixed interfaceMainWindowPrivate使用)

NumItem:The question involves the calculation of scores,To save initialized data,and save the score,Therefore involving the pairNumItemInitialization of itself and data storage for processing.

PDFManager:PDF文件管理类,Draw the data according to the initialized data information to generate the corresponding topic,The drawing part can actually be split,绘制分数,draw integers,May increase draw percentage later,plot power exponents,Drawing such as the square root of,I in order to quickly complete code here,So handle it all in one class.

DataFactory:根据条件(Front-end user selection and input)Generate the corresponding digital data,for drawing formulas. 

二、源代码讲解

1 )MainWindow类

MainWindow 的内容很简单,It is enough to process two button information.,1. 生成dpf,将消息传递给DPFManager类,2. 获取文件存储路径.

// 写文件
void MainWindow::slotDoWrite()
{
    Q_D(MainWindow);

    QString file_path = QApplication::applicationDirPath() + "/text.pdf";
    QList<QList<NumItem>> list = d->initALLNumList();
    pdfManager.writePDF(file_path, list);
}

void MainWindow::slotPickPath() {
    // 获取文件路劲
    // 代码略
 }

2)MainWindowPrivate

For dealing with layout and other issues,As well as the internal display problems, for example:Sliding percentage display,Get the data to be initialized in the interface.

initUI():函数用来初始化UI布局问题

initConnec():Binding for handling signals and slot functions.

MainWindowPrivate::MainWindowPrivate(MainWindow *parent)
    : q_ptr(parent)
    , m_wdgMain(new QWidget())
    , m_sliderIntegerChance(new QSlider())
    , m_labIntegerChance(new QLabel(tr("Integer probability:")))
    , m_labIntegerChanceValue(new QLabel("50%"))
    , m_layoutIntegerChance(new QHBoxLayout)
    , m_sliderPositiveChance(new QSlider())
    , m_labPositiveChance(new QLabel(tr("Is negative probability:")))
    , m_labPositiveChanceValue(new QLabel(tr("50%")))
    , m_layoutPositiveChance(new QHBoxLayout)
    , m_lineEditMoleculeRange(new QLineEdit("10"))
    , m_labMoleculeRange(new QLabel(tr("The molecular range of values:")))
    , m_layoutMoleculeRange(new QHBoxLayout)
    , m_lineEditDenominatorRange(new QLineEdit("10"))
    , m_labDenominatorRange(new QLabel(tr("Denominator value range:")))
    , m_layoutDenominatorRange(new QHBoxLayout)
    , m_lineEditNum(new QLineEdit("4"))
    , m_labNum(new QLabel(tr("Number of numbers in a single line:")))
    , m_layoutNum(new QHBoxLayout)
    , m_lineEditPage(new QLineEdit("10"))
    , m_labPage(new QLabel(tr("Generate page numbers:")))
    , m_layoutPage(new QHBoxLayout)
    , m_btnSavePath(new QPushButton(tr("保存路径")))
    , m_lineEditSavePath(new QLineEdit())
    , m_hlayoutSavePath(new QHBoxLayout)
    , m_btnWritePDF(new QPushButton(tr("生成试卷")))
    , m_vlayoutMain(new QVBoxLayout)
{
    initUI();
    initConnect();
}

void MainWindowPrivate::initUI()
{
    //
    m_layoutIntegerChance->addWidget(m_labIntegerChance);
    m_layoutIntegerChance->addWidget(m_sliderIntegerChance);
    m_sliderIntegerChance->setValue(50);
    m_sliderIntegerChance->setMaximum(100);
    m_layoutIntegerChance->addWidget(m_labIntegerChanceValue);
    m_sliderIntegerChance->setOrientation(Qt::Orientation::Horizontal);
    m_vlayoutMain->addLayout(m_layoutIntegerChance);
    //
    m_layoutPositiveChance->addWidget(m_labPositiveChance);
    m_layoutPositiveChance->addWidget(m_sliderPositiveChance);
    m_sliderPositiveChance->setValue(50);
    m_sliderPositiveChance->setMaximum(100);
    m_layoutPositiveChance->addWidget(m_labPositiveChanceValue);
    m_sliderPositiveChance->setOrientation(Qt::Orientation::Horizontal);
    m_vlayoutMain->addLayout(m_layoutPositiveChance);
    //
    m_layoutMoleculeRange->addWidget(m_labMoleculeRange);
    m_layoutMoleculeRange->addWidget(m_lineEditMoleculeRange);
    m_lineEditMoleculeRange->setValidator(new QIntValidator(1, 1000, this));
    m_vlayoutMain->addLayout(m_layoutMoleculeRange);
    //
    m_layoutDenominatorRange->addWidget(m_labDenominatorRange);
    m_layoutDenominatorRange->addWidget(m_lineEditDenominatorRange);
    m_lineEditDenominatorRange->setValidator(new QIntValidator(1, 1000, this));
    m_vlayoutMain->addLayout(m_layoutDenominatorRange);

    m_layoutNum->addWidget(m_labNum);
    m_layoutNum->addWidget(m_lineEditNum);
    m_lineEditNum->setValidator(new QIntValidator(2, 8, this));
    m_vlayoutMain->addLayout(m_layoutNum);

    m_layoutPage->addWidget(m_labPage);
    m_layoutPage->addWidget(m_lineEditPage);
    m_lineEditPage->setValidator(new QIntValidator(1, 1000, this));
    m_vlayoutMain->addLayout(m_layoutPage);

    //
    m_hlayoutSavePath->addWidget(m_lineEditSavePath);
    m_hlayoutSavePath->addWidget(m_btnSavePath);
    m_vlayoutMain->addLayout(m_hlayoutSavePath);

    m_vlayoutMain->addWidget(m_btnWritePDF);
    m_vlayoutMain->addStretch();
    m_wdgMain->setLayout(m_vlayoutMain);
}
void MainWindowPrivate::initConnect()
{
    connect(m_sliderIntegerChance, &QSlider::valueChanged, this,
            &MainWindowPrivate::slotIntegerChance);
    connect(m_sliderPositiveChance, &QSlider::valueChanged, this,
            &MainWindowPrivate::slotPositiveChance);

    connect(m_btnWritePDF, &QPushButton::clicked, q_ptr,
            &MainWindow::slotDoWrite);
    connect(m_btnSavePath, &QPushButton::clicked, q_ptr,
            &MainWindow::slotPickPath);
}

Numrule MainWindowPrivate::getNumRule()
{
    Numrule item;
    item.IntegerChance = m_sliderIntegerChance->value();
    item.PositiveChance = m_sliderPositiveChance->value();
    item.MoleculeRange = m_lineEditMoleculeRange->text().toInt();
    item.DenominatorRange = m_lineEditDenominatorRange->text().toInt();
    item.page = m_lineEditPage->text().toInt();
    return item;
}

void MainWindowPrivate::slotIntegerChance(int value)
{
    QString str = QString::number(value) + "%";
    m_labIntegerChanceValue->setText(str);
}

void MainWindowPrivate::slotPositiveChance(int value)
{
    QString str = QString::number(value) + "%";
    m_labPositiveChanceValue->setText(str);
}

3)NumItem类

分数类,For recording score data,And operations such as initializing score data.If you need an answer later,There is also the need to add the calculation overload of the fraction.

代码如下:

#include "numitem.h"
#include <qrandom.h>
#include <QTime>
#include <QRandomGenerator>
NumItem::NumItem(Numrule rule)
    : m_bSymbol(POSITIVENUMBER)
    , m_iMolecule(1)
    , m_iDenominator(1)
    , m_numType(INTEGER)
{
    init(rule);
}

void NumItem::init(Numrule rule)
{
    // 1. 随机符号
    m_bSymbol = initSymbol(rule.PositiveChance);
    // 2. random integer or fraction.
    m_numType = initNumType(rule.IntegerChance);
    // 3. 随机数值
    m_iMolecule = initMolecule(rule.MoleculeRange);

    m_iDenominator = initMolecule(rule.DenominatorRange);
    // 整理 1. 约分
    Equivalency();
    // 整理 2. 分母为1 then an integer
    if (m_iDenominator == 1) {
        m_numType = INTEGER;
    }
}
void NumItem::Equivalency()
{
    int num = m_iMolecule > m_iDenominator ? m_iMolecule : m_iDenominator;
    for (int i = 2; i <= num; i++) {
        if (m_iDenominator % i == 0 && m_iMolecule % i == 0) {
            m_iDenominator = m_iDenominator / i;
            m_iMolecule = m_iMolecule / i;
            num = m_iMolecule > m_iDenominator ? m_iMolecule : m_iDenominator;
            i = 1;
        }
    }
}

NumItem::SymbolType NumItem::initSymbol(int rule)
{
    int random = QRandomGenerator::global()->bounded(100);
    // 概率为0means all negative numbers
    if (0 == rule) {
        return NEGATIVENUMBER;
    } else if (100 == rule) {  // 概率为100Indicates all positive numbers.
        return POSITIVENUMBER;
    } else if (random < rule) {
        return POSITIVENUMBER;
    } else {
        return NEGATIVENUMBER;
    }
}

NumItem::NumType NumItem::initNumType(int change)
{
    int random = QRandomGenerator::global()->bounded(100);
    if (0 == random) {
        return INTEGER;
    } else if (100 == random) {
        return FRACTION;
    } else if (random < change) {
        return INTEGER;
    } else {
        return FRACTION;
    }
}

int NumItem::initMolecule(int rule)
{
    return QRandomGenerator::global()->bounded(100) % (rule - 1) + 1;
}

int NumItem::initDenominator(int rule)
{
    return QRandomGenerator::global()->bounded(100) % (rule - 1) + 1;
}

NumItem::SymbolType NumItem::getSymbol() { return m_bSymbol; }

NumItem::NumType NumItem::getNumType() { return m_numType; }

int NumItem::getMolecule() { return m_iMolecule; }

int NumItem::getDenominator() { return m_iDenominator; }

4)PDFManager类

Drawing the printed page content section.绘制流程如下.

Drawing ideas are actually very simple,It is to first draw the information that students need to fill in,then plot by line,Plot numbers in each row,最后生成PDF文件.

代码如下:

#include "pdf_manager.h"
#include <QFile>
#include <QPdfWriter>
#include <QPainter>
#include <QUrl>
#include <QDesktopServices>
#include <QDebug>
PDFManager::PDFManager(QObject *parent)
    : QObject(parent)
{
}

int PDFManager::writePDF(QString file_path, Numrule rule)
{
    QFile pdfFile(file_path);
    pdfFile.open(QIODevice::WriteOnly);
    QPdfWriter *pWriter = new QPdfWriter(&pdfFile);
    pWriter->setPageSize(QPagedPaintDevice::A4);
    pWriter->setResolution(96);
    pWriter->setPageMargins(QMarginsF(35, 35, 35, 35));
    QPainter *pPainter = new QPainter(pWriter);
    int lastPage = rule.page;
    for (int i = 0; i <= (lastPage - 1); i++) {
        QList<QList<NumItem>> list = m_dataFactory.initALLNumList(rule);
        // Get random rules for generating data
        draw(list, pPainter);
        if (i != (lastPage - 1)) pWriter->newPage();
    }

    delete pPainter;
    delete pWriter;
    pdfFile.close();

    QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));
    return 0;
}

void PDFManager::draw(QList<QList<NumItem>> list, QPainter *pPainter)
{
    QFontMetrics fm(pPainter->fontMetrics());
    // 绘制 Fill in information of test paper.
    int w = fm.width("姓名:______________");
    pPainter->drawText(QRect(0, 0, w, fm.height()), Qt::AlignCenter,
                       QString((QString("姓名:______________"))));
    int startX = w;
    w = fm.width("日期:______________");
    pPainter->drawText(QRect(startX, 0, startX + w, fm.height()),
                       Qt::AlignCenter,
                       QString((QString("日期:______________"))));
    startX += w;
    w = fm.width("分数:______________");
    pPainter->drawText(QRect(startX, 0, startX + w, fm.height()),
                       Qt::AlignCenter,
                       QString((QString("分数:______________"))));

    // Iterate over the math formula that draws each row
    for (int index = 1; index <= list.size(); index++) {
        drawLine(index, list.at(index - 1), pPainter);
    }
}

void PDFManager::drawLine(int index, QList<NumItem> list, QPainter *pPainter)
{
    int w = 20;
    QFontMetrics fm(pPainter->fontMetrics());
    int startNumY = (LINE_HEIGHT * index) + (fm.height() / 2) + 2;
    QString strTitle = QString::number(index);
    // Draw a bid
    strTitle.append(".");
    int titleWidth = fm.width(strTitle);
    pPainter->drawText(
        QRect(0, startNumY, 0 + titleWidth, startNumY + fm.height()),
        Qt::AlignCenter, strTitle);

    for (int i = 0; i < list.size(); i++) {
        w += drawNum(index, w, list.at(i), pPainter);
        int width = fm.width(QString("*"));
        if (i == list.size() - 1) {
            pPainter->drawText(
                QRect(w, startNumY, w + width, startNumY + fm.height()),
                Qt::AlignCenter, QString((QString("="))));
            w += width;
        } else {
            pPainter->drawText(
                QRect(w, startNumY, w + width, startNumY + fm.height()),
                Qt::AlignCenter, QString((QString("*"))));
            w += width;
        }
    }
}
int PDFManager::drawNum(int index, int startPostionX, NumItem item,
                        QPainter *pPainter)
{
    QString str = "";
    QFontMetrics fm(pPainter->fontMetrics());
    QPen pen;
    pen.setWidth(4);
    pPainter->setPen(pen);

    // 起始的x坐标
    int startNumX = 0;
    // 起始的y坐标.
    int startNumY = 0;
    // 1. Get drawing information
    startNumX = startPostionX;

    if (item.getNumType() == NumItem::INTEGER) {
        startNumY = index * LINE_HEIGHT + fm.height() / 2;
    } else {
        startNumY = index * LINE_HEIGHT;
    }

    // 如果是正数
    if (item.getSymbol() == NumItem::POSITIVENUMBER) {
        // draw numbers directly
        if (item.getNumType() == NumItem::INTEGER) {
            QString strMolecule = QString::number(item.getMolecule());
            int w = fm.width(strMolecule);
            pPainter->drawText(QRect(startNumX, startNumY, startNumX + w,
                                     startNumY + fm.height()),
                               Qt::AlignCenter, QString(strMolecule));
        } else {
            QString strMolecule = QString::number(item.getMolecule());
            QString strDenominator = QString::number(item.getDenominator());
            int w = fm.width(strMolecule);
            // draw molecules
            pPainter->drawText(startNumX, startNumY, startNumX + w,
                               startNumY + fm.height(), Qt::AlignCenter,
                               QString(strMolecule));
            // Plot the denominator
            pPainter->drawText(startNumX, startNumY + fm.height(),
                               startNumX + w, startNumY + fm.height() * 2,
                               Qt::AlignCenter, strDenominator);
            // 分号
            pPainter->drawText(
                startNumX, index * LINE_HEIGHT + fm.height() / 2, startNumX + w,
                index * LINE_HEIGHT + fm.height() / 2 + fm.height(),
                Qt::AlignCenter, QString("-"));
        }
    } else {
        int w = fm.width(QString("("));
        pPainter->drawText(
            QRect(startNumX, index * LINE_HEIGHT + fm.height() / 2,
                  startNumX + w,
                  index * LINE_HEIGHT + fm.height() / 2 + fm.height()),
            Qt::AlignCenter, QString("("));
        startNumX += w;
        w = fm.width(QString("-"));

        pPainter->drawText(
            QRect(startNumX, index * LINE_HEIGHT + fm.height() / 2,
                  startNumX + w,
                  index * LINE_HEIGHT + fm.height() / 2 + fm.height()),
            Qt::AlignCenter, QString("-"));
        startNumX += w;
        if (item.getNumType() == NumItem::INTEGER) {
            QString strMolecule = QString::number(item.getMolecule());
            int w = fm.width(strMolecule);
            pPainter->drawText(QRect(startNumX, startNumY, startNumX + w,
                                     startNumY + fm.height()),
                               Qt::AlignCenter, QString(strMolecule));
            startNumX += w;
        } else {
            QString strMolecule = QString::number(item.getMolecule());
            QString strDenominator = QString::number(item.getDenominator());
            int w = fm.width(strMolecule);
            // draw molecules
            pPainter->drawText(QRect(startNumX, startNumY, startNumX + w,
                                     startNumY + fm.height()),
                               Qt::AlignCenter, QString(strMolecule));
            // Plot the denominator
            pPainter->drawText(
                QRect(startNumX, index * LINE_HEIGHT + fm.height(),
                      startNumX + w, index * LINE_HEIGHT + fm.height() * 2),
                Qt::AlignCenter, strDenominator);

            pPainter->drawText(
                startNumX, index * LINE_HEIGHT + fm.height() / 2, startNumX + w,
                index * LINE_HEIGHT + fm.height() / 2 + fm.height(),
                Qt::AlignCenter, QString("-"));
            startNumX += w;
        }
        w = fm.width(QString(")"));
        // startNumX += w;
        pPainter->drawText(
            QRect(startNumX, index * LINE_HEIGHT + fm.height() / 2,
                  startNumX + w,
                  index * LINE_HEIGHT + fm.height() + fm.height() / 2),
            Qt::AlignCenter, QString(")"));
    }
    return getNumWidth(item, pPainter);
}

int PDFManager::getNumWidth(NumItem item, QPainter *pPainter)
{
    // 1. Get the width of the pure digital.
    QFontMetrics fm(pPainter->fontMetrics());
    int numWidth = 0;
    // 2. 分数判断
    if (item.getNumType() == NumItem::INTEGER) {
        //  If it is the length of the directly obtained an integer number
        numWidth = fm.width(QString::number(item.getMolecule()));
    } else {
        // if fractional
        int iWidthMolecule = fm.width(QString::number(item.getMolecule()));
        int iWidthDenominator =
            fm.width(QString::number(item.getDenominator()));
        numWidth = iWidthMolecule > iWidthDenominator ? iWidthMolecule
                                                      : iWidthDenominator;
    }
    // 3. 正负数判断
    if (item.getSymbol() == NumItem::NEGATIVENUMBER) {
        int iWidthKL = fm.width(QString("("));
        int iWidthSymbol = fm.width(QString("-"));
        int iWidthKR = fm.width(QString(")"));
        numWidth += iWidthSymbol;
        numWidth += iWidthKL;
        numWidth += iWidthKR;
    }
    return numWidth;
}

三、源码地址

地址:TestPaper · master · 三雷科技 / QT博客案例 · GitCode

https://gitcode.net/arv002/qt/-/tree/master/TestPaper

原网站

版权声明
本文为[Three ray technology]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/222/202208101333055766.html