当前位置:网站首页>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;
}
三、源码地址
边栏推荐
猜你喜欢
借数据智能,亚马逊云科技助力企业打造品牌内生增长力
Short read or OOM loading DB. Unrecoverable error, aborting now
缺少比较器,运放来救场!(运放当做比较器电路记录)
Interface Automation Testing Basics
发送post请求前台无法获取数据
高数_证明_弧微分公式
WebView的优化与常见问题解决方案
Cloud Migration Practice of Redis
[target detection] small script: extract training set images and labels and update the index
【Gazebo入门教程】第三讲 SDF文件的静/动态编程建模
随机推荐
学习日记8
Error: Rule can only have one resource source (provided resource and test + include + exclude)
[Gazebo Introductory Tutorial] Lecture 3 Static/Dynamic Programming Modeling of SDF Files
MySQL - storage engine for databases
商汤自研机械臂,首款产品是AI下棋机器人:还请郭晶晶作代言
malloc 函数详解
镜像瘦身:每一层都不能放过
Stream通过findFirst()查找满足条件的一条数据
Makefile missing separator. Stop.怎么解决「建议收藏」
数据产品经理那点事儿 二
1W word detailed thread local storage ThreadLocal
【Gazebo入门教程】第三讲 SDF文件的静/动态编程建模
普林斯顿微积分读本05第四章--求解多项式的极限问题
awk的简单使用
d为何用模板参数
2022年五大云虚拟化趋势
file system design
MySQL - 数据库的存储引擎
Fragment's show and hide
Open source SPL wipes out tens of thousands of database intermediate tables