当前位置:网站首页>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;
}
三、源码地址
边栏推荐
- NAACL 2022 | 简单且高效!随机中间层映射指导的知识蒸馏方法
- [Gazebo Introductory Tutorial] Lecture 3 Static/Dynamic Programming Modeling of SDF Files
- 如何完成新媒体产品策划?
- laravel throws the error to Dingding
- 友邦人寿可观测体系设计与落地
- AWS 安全基础知识
- PHP 判断文件是否有内容,没有内容则复制另一个文件写入
- [target detection] small script: extract training set images and labels and update the index
- Error: Rule can only have one resource source (provided resource and test + include + exclude)
- Summary of Force Buckle Solution 640 - Solving Equations
猜你喜欢

Stream通过findFirst()查找满足条件的一条数据

Using data intelligence, Amazon cloud technology helps companies build endogenous brand growth

如何完成新媒体产品策划?

MySQL - storage engine for databases

池化技术有多牛?来,告诉你阿里的Druid为啥如此牛逼!

普林斯顿微积分读本05第四章--求解多项式的极限问题

3DS MAX batch export file script MAXScript with interface

【ECCV 2022|Millions of Prizes】PSG Competition: Pursuing the "Most Comprehensive" Scene Understanding

SenseTime self-developed robotic arm, the first product is an AI chess-playing robot: Guo Jingjing is also invited as an endorsement

MySQL - 数据库的存储引擎
随机推荐
[JS Advanced] Creating sub-objects and replacing this_10 in ES5 standard specification
Existing in the rain of PFAS chemical poses a threat to the safety of drinking water
Error: Rule can only have one resource source (provided resource and test + include + exclude)
YTU 2295: KMP pattern match one (string)
数据产品经理那点事儿 二
舵机内部结及工作原理浅析[通俗易懂]
Short read or OOM loading DB. Unrecoverable error, aborting now
MySQL - storage engine for databases
1W word detailed thread local storage ThreadLocal
[Gazebo Introductory Tutorial] Lecture 3 Static/Dynamic Programming Modeling of SDF Files
AWS Security Fundamentals
SenseTime self-developed robotic arm, the first product is an AI chess-playing robot: Guo Jingjing is also invited as an endorsement
2011年下半年 系统架构设计师 下午试卷 II
C# WPF image is displayed without problems, but the solution does not display the image at runtime
How does IT Xiaobai learn PHP systematically
WebView的优化与常见问题解决方案
【量化交易行情不够快?】一文搞定通过Win10 wsl2 +Ubuntu+redis+pickle实现股票行情极速读写
PHP judges whether the file has content, and if there is no content, copy another file to write
【POI 2008, BLO】割点
tensorflow安装踩坑总结