Qt驾校科目考试系统
霸道小明 人气:01.设置登录界面
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoginDialog) { ui->setupUi(this); ui->imgLabel->setScaledContents(true);//设置填充 this->resize(ui->imgLabel->width(),ui->imgLabel->height());//设置窗口大小 //设置窗口标题 this->setWindowTitle(("驾校科目一考试登录")); //设置窗口风格 this->setWindowFlags(Qt::Dialog |Qt::WindowCloseButtonHint); }
2.登录功能实现
2.1验证邮箱地址
首先判断输入的账号是否符合邮箱格式 ,如果符合则进行登录验证,否则提示格式错误
void LoginDialog::on_loginButton_clicked() { //首先通过正则表达式判断账号是否是一个合法的邮箱格式 //^:表示规则字符串开始、$:表示规则字符串的结束 //+:表示匹配次数≥1次、*:表示匹配任意次数(可为0次) {n,m}表示匹配次数最少n次 最多m次 QRegExp rx("^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$");//初始化时指定规则字符串 bool res = rx.exactMatch(ui->accountEdit->text());//对用户输入的账号进行匹配 if(res){//匹配成功 QMessageBox::information(this,"提示","欢迎登录科目一考试科目系统"); }else{//匹配不成功 QMessageBox::information(this,"提示","非法邮箱地址!请您重新输入"); ui->accountEdit->clear();//清空账号输入框 ui->codeEdit->clear();//清空密码输入框 ui->accountEdit->setFocus();//账号输入框聚焦 return ; } }
2.2账号密码登录
当账号格式合法后,读取保存账号和密码的文档进行匹配
if(res){//匹配成功 //QMessageBox::information(this,"提示","欢迎登录科目一考试科目系统"); QString filename;//文件路径 QString AccInput;//用户输入账号 QString strCode;//用户输入密码 QString strLine;//每次读取一行数据 QStringList strList;//分割读取一行数据 filename="../account.txt";//设置文件路径 AccInput=ui->accountEdit->text();//获取账号 strCode=ui->codeEdit->text();//获取密码 QFile file(filename);//初始化 QTextStream stream(&file);//文本类型文件流 //指定只读方式打开,指定文件类型式文本类型 if(file.open(QIODevice::ReadOnly|QIODevice::Text)){ //打开成功 while(!stream.atEnd()){ strLine=stream.readLine();//每次读取一行 strList = strLine.split(",");//将读取到的行按","分割 if(AccInput==strList.at(0)){ if(strCode==strList.at(1)){ //账号和密码匹配成功 QMessageBox::information(this,"提示","欢迎进入科目一考试系统"); file.close(); return ; }else{ //账号匹配成功&密码不成功 QMessageBox::information(this,"提示","密码错误!请重新输入!"); ui->codeEdit->clear(); ui->codeEdit->setFocus(); file.close(); return ; } } } QMessageBox::information(this,"提示","输入账号有误!请重新输入!"); ui->accountEdit->clear();//清空账号输入框 ui->codeEdit->clear();//清空密码输入框 ui->accountEdit->setFocus();//账号输入框聚焦 file.close(); return; }
2.3密码隐藏
将密码控件的echoMode属性值改为Password即可
3.考试界面开发
3.1考试用时
首先添加一个考试对话框类;在考试对话框类中添加QTimer和int属性用于表示计时器和已用时;设定计时器的时间间隔为1s,通过connect函数将计时器与已用时显示信号槽连接起来
examDialog.h
#ifndef EXAMDIALOG_H #define EXAMDIALOG_H #include<QDialog> #include<QTimer> class ExamDialog : public QDialog { Q_OBJECT//使用信号槽 public: ExamDialog(QWidget* parent=0); void initTimer(); private: QTimer*m_timer;//计时器 int m_timeGo;//考试已用时 private slots: //私有槽方法 void freshTime(); }; #endif // EXAMDIALOG_H
examDialog.cpp
#include "examdialog.h" ExamDialog::ExamDialog(QWidget* parent):QDialog(parent) { setWindowTitle("考试已用时:0分0秒"); initTimer(); } void ExamDialog::initTimer() { m_timeGo=0; m_timer=new QTimer(this); m_timer->setInterval(1000);//设置计时器时间间隔1s m_timer->start();//启动计时器 //通过connect方法,连接信号槽。m_timer每发送timeout()信号时,由this执行槽方法freshTime() connect(m_timer,SIGNAL(timeout()),this,SLOT(freshTime())); } void ExamDialog::freshTime() { //刷新考试用时 m_timeGo++; QString min=QString::number(m_timeGo/60);//计算分钟,将整数转QString QString sec=QString::number(m_timeGo%60); setWindowTitle("考试已用时:"+min+"分"+sec+"秒"); }
3.2题目布局
添加属性以及初始化方法
public: void initLayout(); //初始化布局管理器 bool initTextEdit();//初始化文本编辑器 void initButton(); //初始化按钮及标签 private: QTextEdit*m_textEdit; //考试题库显示 QLabel*m_titleLabels[10]; //题目标签 QRadioButton *m_radioBtns[32];//单选按钮 QCheckBox *m_checkBtn[4]; //多选题按钮 QRadioButton *m_radioA; //判断题A选项 QRadioButton *m_radioB; //判断题B选项 QGridLayout*m_layout; //布局管理器 QStringList m_answerList; //答案
在initLayout函数中,设置当前窗口为为管理器父窗口,规定控件间的距离以及控件和窗体间的距离
void ExamDialog::initLayout() { //以当前窗口作为父窗口 m_layout=new QGridLayout(this); m_layout->setSpacing(10);//设置控件之间的间距 m_layout->setMargin(10);//设置窗体与控件键的边距 }
在initTextEdit函数中,读取文本中的题目信息并将答案行单独存储,将读取到的题目信息显示在控件中,并将控件交给布局管理器在窗体上布局。
bool ExamDialog::initTextEdit() { QString strLine;//保存文件答案行数据 QStringList strList;//保存读取到的答案行 QString fileName("../exam.txt"); QFile file(fileName); QTextStream stream(&file);//文本类型文件流 stream.setCodec("UTF-8");//指定编码 if(file.open(QIODevice::ReadOnly|QIODevice::Text)){ //以当前窗口作为父窗口 m_textEdit=new QTextEdit(this); m_textEdit->setReadOnly(true);//设置文本只读 QString strText;//用于保存显示到文本编辑器的数据 int nLines = 0; while(!stream.atEnd()){ if(nLines==0){//过滤首行 stream.readLine(); nLines++; continue; }else if((nLines%6==0&&nLines>=6&&nLines<=6*9)||nLines==6*9+4){//过滤答案行 strLine = stream.readLine(); strList=strLine.split(" ");//通过空格分割出答案行的答案 m_answerList.append(strList.at(1));//获得答案 strText+="\n"; nLines++; continue; } strText+= stream.readLine();//读取一行 strText+="\n"; nLines++; } //显示文本内容 m_textEdit->setText(strText); //把控件添加到布局管理器中 m_layout->addWidget(m_textEdit,0,0,1,10); file.close(); return true;//读取完 }else{ return false; } }
在初始化函数中对窗体背景和字体进行一些设置
ExamDialog::ExamDialog(QWidget* parent):QDialog(parent) { //设置字体大小 QFont font; font.setPointSize(12); setFont(font);//设置当前窗口字体 //设置窗体背景颜色 setPalette(QPalette(QColor(209,215,255))); setWindowTitle("考试已用时:0分0秒"); setWindowFlags(Qt::Dialog|Qt::WindowCloseButtonHint); resize(800,900);//设置窗体大小 initTimer(); initLayout(); if(initTextEdit()){ }else{ //读取失败 QMessageBox::information(this,"提示","初始化题库数据文件失败!"); //当前应用程序立刻响应槽方法quit QTimer::singleShot(0,qApp,SLOT(quit())); } }
3.3按钮布局
布局方法就是先将控件初始化,然后交给布局管理器进行布局。但要注意的是单选按钮的分组:每个单选题的四个按钮为一组,判断题的两个按钮为一组
新添加QbuttonGroup属性
private: QButtonGroup* m_btnGroups[9];//单选按钮分组
在初始换按钮函数中
void ExamDialog::initButton() { QStringList strList={"A","B","C","D"}; for(int i=0;i<10;++i){ //题目标签 m_titleLabels[i]=new QLabel(this);//当前窗口作为父窗口初始化 m_titleLabels[i]->setText("第"+QString::number(i+1)+"题"); m_layout->addWidget(m_titleLabels[i],1,i);//交给布局管理器,默认是占据一行一列 //判断题 if(i==9){ m_radioA=new QRadioButton(this); m_radioB=new QRadioButton(this); m_radioA->setText("正确"); m_radioB->setText("错误"); m_layout->addWidget(m_radioA,2,9); m_layout->addWidget(m_radioB,3,9); //判断题按钮分组 m_btnGroups[8]=new QButtonGroup(this); m_btnGroups[8]->addButton(m_radioA); m_btnGroups[8]->addButton(m_radioB); } if(i<8){ //单选题按钮分组 m_btnGroups[i]=new QButtonGroup(this); } //选择题 for(int j=0;j<4;++j){ if(i==8){ //多选题 m_checkBtns[j]=new QCheckBox(this); m_checkBtns[j]->setText(strList.at(j)); m_layout->addWidget(m_checkBtns[j],2+j,8); }else if(i<8){ //单选题 m_radioBtns[i*4+j]=new QRadioButton(this); m_radioBtns[i*4+j]->setText(strList.at(j)); m_layout->addWidget(m_radioBtns[i*4+j],2+j,i); m_btnGroups[i]->addButton(m_radioBtns[i*4+j]); } } //提交按钮 QPushButton*submitBtn=new QPushButton(this); submitBtn->setText("提交"); submitBtn->setFixedSize(100,35); m_layout->addWidget(submitBtn,6,9); } }
3.4提交分数
首先将提交按钮与信号槽连接
connect(submitBtn,SIGNAL(clicked(bool)),this,SLOT(getScore()));
在计算分数前先判断题目是否全部完成,然后根据读取的答案计算分数
bool ExamDialog::hasNoSelect() { int radioSelects=0; //统计单选题完成数量 for(int i=0;i<8;++i){ if(m_btnGroups[i]->checkedButton()){ radioSelects++; } } //判断单选题是否全部完成 if(radioSelects!=8){ return true; } //统计多选题选择数量 int checkSelect=0; for(int i=0;i<4;++i){ if(m_checkBtns[i]->isChecked()){ checkSelect++; } } //多选题未选或只选了一个也算未完成 if(checkSelect==0||checkSelect==1){ return true; } //判断题 if(!m_radioA->isChecked()&&!m_radioB->isChecked()){ return true; } return false; } void ExamDialog::getScore() { if(hasNoSelect()){ //有未完成的题目 QMessageBox::information(this,"提示","您有未完成的题目!请完成考试","是"); return ; }else{ //开始统分 int scores =0; for(int i=0;i<10;++i){ //单选题统分 if(i<8){ //判断选中的按钮与答案的是否一致 if(m_btnGroups[i]->checkedButton()->text()==m_answerList.at(i)){ scores+=10; } } //多选题统分 if(i==8){ QString answer=m_answerList.at(i);//获得多选题答案 bool hasA=false; bool hasB=false; bool hasC=false; bool hasD=false; hasA=answer.contains("A"); hasB=answer.contains("B"); hasC=answer.contains("C"); hasD=answer.contains("D"); bool checkA=m_checkBtns[0]->checkState(); bool checkB=m_checkBtns[1]->checkState(); bool checkC=m_checkBtns[2]->checkState(); bool checkD=m_checkBtns[3]->checkState(); if(hasA!=checkA||hasB!=checkB||hasC!=checkC||hasD!=checkD){ //答案有不一致的 continue; }else{ scores+=10; } } //判断题统分 if(i==9){ if(m_btnGroups[8]->checkedButton()->text()==m_answerList.at(i)){ scores+=10; } } } int res = QMessageBox::information(this,"提示","您所得分数为:"+QString::number(scores)+"分。是否重新考试?",QMessageBox::Yes|QMessageBox::No); if(res==QMessageBox::Yes){ //重新考试 return; }else{ //不想重新考试 close(); } } }
3.5窗口交互
实现登录窗口与考试窗口的连接
当用户登录成功后通过done函数发送一个Accepted,当用户点击取消时发送一个Rejected,然后在主函数中进行判断
int main(int argc, char *argv[]) { QApplication a(argc, argv); LoginDialog loginDialog; int res = loginDialog.exec(); //以模态方式运行 if(res==QDialog::Accepted){ ExamDialog examDialog; return a.exec(); }else{ return 0; } return a.exec(); }
4.发布项目
4.1更改编译路径
将当前编译路径由debug转为当前文件所在路径
4.2设置图标
在项目文件中通过 RC_ICONS加载图标
RC_ICONS += examsys.ico
图标已经更改过来了
4.3通过dos进行项目打包
将项目由DeBug模式改为Release模式,并编译
建立一个文件夹作为发布文件,将Release下生成的.exe文件和需要的文档拷贝进来
进入要发布文件夹路径,然后输入windeployqt 文件名.exe,点击回车
打包完成
如果提示 windeployqt不是命令的话需要将安装的qt的bin路径添加到环境变量中
加载全部内容