当前位置:   article > 正文

QT驾校科目考试系统——从实现到发布_qt 实现科目一刷题

qt 实现科目一刷题

目录

1.设置登录界面  

2.登录功能实现

2.1验证邮箱地址

2.2账号密码登录 

2.3密码隐藏 

3.考试界面开发 

3.1考试用时

3.2题目布局 

3.3按钮布局 

3.4提交分数 

3.5窗口交互 

4.发布项目 

4.1更改编译路径

4.2设置图标 

4.3通过dos进行项目打包


1.设置登录界面  

  1. LoginDialog::LoginDialog(QWidget *parent) :
  2. QDialog(parent),
  3. ui(new Ui::LoginDialog)
  4. {
  5. ui->setupUi(this);
  6. ui->imgLabel->setScaledContents(true);//设置填充
  7. this->resize(ui->imgLabel->width(),ui->imgLabel->height());//设置窗口大小
  8. //设置窗口标题
  9. this->setWindowTitle(("驾校科目一考试登录"));
  10. //设置窗口风格
  11. this->setWindowFlags(Qt::Dialog |Qt::WindowCloseButtonHint);
  12. }

2.登录功能实现

2.1验证邮箱地址

首先判断输入的账号是否符合邮箱格式 ,如果符合则进行登录验证,否则提示格式错误

  1. void LoginDialog::on_loginButton_clicked()
  2. {
  3. //首先通过正则表达式判断账号是否是一个合法的邮箱格式
  4. //^:表示规则字符串开始、$:表示规则字符串的结束
  5. //+:表示匹配次数≥1次、*:表示匹配任意次数(可为0次) {n,m}表示匹配次数最少n次 最多m次
  6. QRegExp rx("^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$");//初始化时指定规则字符串
  7. bool res = rx.exactMatch(ui->accountEdit->text());//对用户输入的账号进行匹配
  8. if(res){//匹配成功
  9. QMessageBox::information(this,"提示","欢迎登录科目一考试科目系统");
  10. }else{//匹配不成功
  11. QMessageBox::information(this,"提示","非法邮箱地址!请您重新输入");
  12. ui->accountEdit->clear();//清空账号输入框
  13. ui->codeEdit->clear();//清空密码输入框
  14. ui->accountEdit->setFocus();//账号输入框聚焦
  15. return ;
  16. }
  17. }

2.2账号密码登录 

 当账号格式合法后,读取保存账号和密码的文档进行匹配

  1. if(res){//匹配成功
  2. //QMessageBox::information(this,"提示","欢迎登录科目一考试科目系统");
  3. QString filename;//文件路径
  4. QString AccInput;//用户输入账号
  5. QString strCode;//用户输入密码
  6. QString strLine;//每次读取一行数据
  7. QStringList strList;//分割读取一行数据
  8. filename="../account.txt";//设置文件路径
  9. AccInput=ui->accountEdit->text();//获取账号
  10. strCode=ui->codeEdit->text();//获取密码
  11. QFile file(filename);//初始化
  12. QTextStream stream(&file);//文本类型文件流
  13. //指定只读方式打开,指定文件类型式文本类型
  14. if(file.open(QIODevice::ReadOnly|QIODevice::Text)){
  15. //打开成功
  16. while(!stream.atEnd()){
  17. strLine=stream.readLine();//每次读取一行
  18. strList = strLine.split(",");//将读取到的行按","分割
  19. if(AccInput==strList.at(0)){
  20. if(strCode==strList.at(1)){
  21. //账号和密码匹配成功
  22. QMessageBox::information(this,"提示","欢迎进入科目一考试系统");
  23. file.close();
  24. return ;
  25. }else{
  26. //账号匹配成功&密码不成功
  27. QMessageBox::information(this,"提示","密码错误!请重新输入!");
  28. ui->codeEdit->clear();
  29. ui->codeEdit->setFocus();
  30. file.close();
  31. return ;
  32. }
  33. }
  34. }
  35. QMessageBox::information(this,"提示","输入账号有误!请重新输入!");
  36. ui->accountEdit->clear();//清空账号输入框
  37. ui->codeEdit->clear();//清空密码输入框
  38. ui->accountEdit->setFocus();//账号输入框聚焦
  39. file.close();
  40. return;
  41. }

2.3密码隐藏 

将密码控件的echoMode属性值改为Password即可

3.考试界面开发 

3.1考试用时

首先添加一个考试对话框类;在考试对话框类中添加QTimer和int属性用于表示计时器和已用时;设定计时器的时间间隔为1s,通过connect函数将计时器与已用时显示信号槽连接起来

examDialog.h

  1. #ifndef EXAMDIALOG_H
  2. #define EXAMDIALOG_H
  3. #include<QDialog>
  4. #include<QTimer>
  5. class ExamDialog : public QDialog
  6. {
  7. Q_OBJECT//使用信号槽
  8. public:
  9. ExamDialog(QWidget* parent=0);
  10. void initTimer();
  11. private:
  12. QTimer*m_timer;//计时器
  13. int m_timeGo;//考试已用时
  14. private slots: //私有槽方法
  15. void freshTime();
  16. };
  17. #endif // EXAMDIALOG_H

examDialog.cpp 

  1. #include "examdialog.h"
  2. ExamDialog::ExamDialog(QWidget* parent):QDialog(parent)
  3. {
  4. setWindowTitle("考试已用时:0分0秒");
  5. initTimer();
  6. }
  7. void ExamDialog::initTimer()
  8. {
  9. m_timeGo=0;
  10. m_timer=new QTimer(this);
  11. m_timer->setInterval(1000);//设置计时器时间间隔1s
  12. m_timer->start();//启动计时器
  13. //通过connect方法,连接信号槽。m_timer每发送timeout()信号时,由this执行槽方法freshTime()
  14. connect(m_timer,SIGNAL(timeout()),this,SLOT(freshTime()));
  15. }
  16. void ExamDialog::freshTime()
  17. {
  18. //刷新考试用时
  19. m_timeGo++;
  20. QString min=QString::number(m_timeGo/60);//计算分钟,将整数转QString
  21. QString sec=QString::number(m_timeGo%60);
  22. setWindowTitle("考试已用时:"+min+"分"+sec+"秒");
  23. }

3.2题目布局 

添加属性以及初始化方法

  1. public:
  2. void initLayout(); //初始化布局管理器
  3. bool initTextEdit();//初始化文本编辑器
  4. void initButton(); //初始化按钮及标签
  5. private:
  6. QTextEdit*m_textEdit; //考试题库显示
  7. QLabel*m_titleLabels[10]; //题目标签
  8. QRadioButton *m_radioBtns[32];//单选按钮
  9. QCheckBox *m_checkBtn[4]; //多选题按钮
  10. QRadioButton *m_radioA; //判断题A选项
  11. QRadioButton *m_radioB; //判断题B选项
  12. QGridLayout*m_layout; //布局管理器
  13. QStringList m_answerList; //答案

 在initLayout函数中,设置当前窗口为为管理器父窗口,规定控件间的距离以及控件和窗体间的距离

  1. void ExamDialog::initLayout()
  2. {
  3. //以当前窗口作为父窗口
  4. m_layout=new QGridLayout(this);
  5. m_layout->setSpacing(10);//设置控件之间的间距
  6. m_layout->setMargin(10);//设置窗体与控件键的边距
  7. }

在initTextEdit函数中,读取文本中的题目信息并将答案行单独存储,将读取到的题目信息显示在控件中,并将控件交给布局管理器在窗体上布局。

  1. bool ExamDialog::initTextEdit()
  2. {
  3. QString strLine;//保存文件答案行数据
  4. QStringList strList;//保存读取到的答案行
  5. QString fileName("../exam.txt");
  6. QFile file(fileName);
  7. QTextStream stream(&file);//文本类型文件流
  8. stream.setCodec("UTF-8");//指定编码
  9. if(file.open(QIODevice::ReadOnly|QIODevice::Text)){
  10. //以当前窗口作为父窗口
  11. m_textEdit=new QTextEdit(this);
  12. m_textEdit->setReadOnly(true);//设置文本只读
  13. QString strText;//用于保存显示到文本编辑器的数据
  14. int nLines = 0;
  15. while(!stream.atEnd()){
  16. if(nLines==0){//过滤首行
  17. stream.readLine();
  18. nLines++;
  19. continue;
  20. }else if((nLines%6==0&&nLines>=6&&nLines<=6*9)||nLines==6*9+4){//过滤答案行
  21. strLine = stream.readLine();
  22. strList=strLine.split(" ");//通过空格分割出答案行的答案
  23. m_answerList.append(strList.at(1));//获得答案
  24. strText+="\n";
  25. nLines++;
  26. continue;
  27. }
  28. strText+= stream.readLine();//读取一行
  29. strText+="\n";
  30. nLines++;
  31. }
  32. //显示文本内容
  33. m_textEdit->setText(strText);
  34. //把控件添加到布局管理器中
  35. m_layout->addWidget(m_textEdit,0,0,1,10);
  36. file.close();
  37. return true;//读取完
  38. }else{
  39. return false;
  40. }
  41. }

在初始化函数中对窗体背景和字体进行一些设置

  1. ExamDialog::ExamDialog(QWidget* parent):QDialog(parent)
  2. {
  3. //设置字体大小
  4. QFont font;
  5. font.setPointSize(12);
  6. setFont(font);//设置当前窗口字体
  7. //设置窗体背景颜色
  8. setPalette(QPalette(QColor(209,215,255)));
  9. setWindowTitle("考试已用时:0分0秒");
  10. setWindowFlags(Qt::Dialog|Qt::WindowCloseButtonHint);
  11. resize(800,900);//设置窗体大小
  12. initTimer();
  13. initLayout();
  14. if(initTextEdit()){
  15. }else{
  16. //读取失败
  17. QMessageBox::information(this,"提示","初始化题库数据文件失败!");
  18. //当前应用程序立刻响应槽方法quit
  19. QTimer::singleShot(0,qApp,SLOT(quit()));
  20. }
  21. }

3.3按钮布局 

布局方法就是先将控件初始化,然后交给布局管理器进行布局。但要注意的是单选按钮的分组:每个单选题的四个按钮为一组,判断题的两个按钮为一组

新添加QbuttonGroup属性  

  1. private:
  2. QButtonGroup* m_btnGroups[9];//单选按钮分组

在初始换按钮函数中 

  1. void ExamDialog::initButton()
  2. {
  3. QStringList strList={"A","B","C","D"};
  4. for(int i=0;i<10;++i){
  5. //题目标签
  6. m_titleLabels[i]=new QLabel(this);//当前窗口作为父窗口初始化
  7. m_titleLabels[i]->setText("第"+QString::number(i+1)+"题");
  8. m_layout->addWidget(m_titleLabels[i],1,i);//交给布局管理器,默认是占据一行一列
  9. //判断题
  10. if(i==9){
  11. m_radioA=new QRadioButton(this);
  12. m_radioB=new QRadioButton(this);
  13. m_radioA->setText("正确");
  14. m_radioB->setText("错误");
  15. m_layout->addWidget(m_radioA,2,9);
  16. m_layout->addWidget(m_radioB,3,9);
  17. //判断题按钮分组
  18. m_btnGroups[8]=new QButtonGroup(this);
  19. m_btnGroups[8]->addButton(m_radioA);
  20. m_btnGroups[8]->addButton(m_radioB);
  21. }
  22. if(i<8){
  23. //单选题按钮分组
  24. m_btnGroups[i]=new QButtonGroup(this);
  25. }
  26. //选择题
  27. for(int j=0;j<4;++j){
  28. if(i==8){
  29. //多选题
  30. m_checkBtns[j]=new QCheckBox(this);
  31. m_checkBtns[j]->setText(strList.at(j));
  32. m_layout->addWidget(m_checkBtns[j],2+j,8);
  33. }else if(i<8){
  34. //单选题
  35. m_radioBtns[i*4+j]=new QRadioButton(this);
  36. m_radioBtns[i*4+j]->setText(strList.at(j));
  37. m_layout->addWidget(m_radioBtns[i*4+j],2+j,i);
  38. m_btnGroups[i]->addButton(m_radioBtns[i*4+j]);
  39. }
  40. }
  41. //提交按钮
  42. QPushButton*submitBtn=new QPushButton(this);
  43. submitBtn->setText("提交");
  44. submitBtn->setFixedSize(100,35);
  45. m_layout->addWidget(submitBtn,6,9);
  46. }
  47. }

3.4提交分数 

首先将提交按钮与信号槽连接 

connect(submitBtn,SIGNAL(clicked(bool)),this,SLOT(getScore()));

在计算分数前先判断题目是否全部完成,然后根据读取的答案计算分数

  1. bool ExamDialog::hasNoSelect()
  2. {
  3. int radioSelects=0;
  4. //统计单选题完成数量
  5. for(int i=0;i<8;++i){
  6. if(m_btnGroups[i]->checkedButton()){
  7. radioSelects++;
  8. }
  9. }
  10. //判断单选题是否全部完成
  11. if(radioSelects!=8){
  12. return true;
  13. }
  14. //统计多选题选择数量
  15. int checkSelect=0;
  16. for(int i=0;i<4;++i){
  17. if(m_checkBtns[i]->isChecked()){
  18. checkSelect++;
  19. }
  20. }
  21. //多选题未选或只选了一个也算未完成
  22. if(checkSelect==0||checkSelect==1){
  23. return true;
  24. }
  25. //判断题
  26. if(!m_radioA->isChecked()&&!m_radioB->isChecked()){
  27. return true;
  28. }
  29. return false;
  30. }
  31. void ExamDialog::getScore()
  32. {
  33. if(hasNoSelect()){
  34. //有未完成的题目
  35. QMessageBox::information(this,"提示","您有未完成的题目!请完成考试","是");
  36. return ;
  37. }else{
  38. //开始统分
  39. int scores =0;
  40. for(int i=0;i<10;++i){
  41. //单选题统分
  42. if(i<8){
  43. //判断选中的按钮与答案的是否一致
  44. if(m_btnGroups[i]->checkedButton()->text()==m_answerList.at(i)){
  45. scores+=10;
  46. }
  47. }
  48. //多选题统分
  49. if(i==8){
  50. QString answer=m_answerList.at(i);//获得多选题答案
  51. bool hasA=false;
  52. bool hasB=false;
  53. bool hasC=false;
  54. bool hasD=false;
  55. hasA=answer.contains("A");
  56. hasB=answer.contains("B");
  57. hasC=answer.contains("C");
  58. hasD=answer.contains("D");
  59. bool checkA=m_checkBtns[0]->checkState();
  60. bool checkB=m_checkBtns[1]->checkState();
  61. bool checkC=m_checkBtns[2]->checkState();
  62. bool checkD=m_checkBtns[3]->checkState();
  63. if(hasA!=checkA||hasB!=checkB||hasC!=checkC||hasD!=checkD){
  64. //答案有不一致的
  65. continue;
  66. }else{
  67. scores+=10;
  68. }
  69. }
  70. //判断题统分
  71. if(i==9){
  72. if(m_btnGroups[8]->checkedButton()->text()==m_answerList.at(i)){
  73. scores+=10;
  74. }
  75. }
  76. }
  77. int res = QMessageBox::information(this,"提示","您所得分数为:"+QString::number(scores)+"分。是否重新考试?",QMessageBox::Yes|QMessageBox::No);
  78. if(res==QMessageBox::Yes){
  79. //重新考试
  80. return;
  81. }else{
  82. //不想重新考试
  83. close();
  84. }
  85. }
  86. }

3.5窗口交互 

 实现登录窗口与考试窗口的连接

当用户登录成功后通过done函数发送一个Accepted,当用户点击取消时发送一个Rejected,然后在主函数中进行判断

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication a(argc, argv);
  4. LoginDialog loginDialog;
  5. int res = loginDialog.exec(); //以模态方式运行
  6. if(res==QDialog::Accepted){
  7. ExamDialog examDialog;
  8. return a.exec();
  9. }else{
  10. return 0;
  11. }
  12. return a.exec();
  13. }

4.发布项目 

4.1更改编译路径

将当前编译路径由debug转为当前文件所在路径

4.2设置图标 

在项目文件中通过 RC_ICONS加载图标

RC_ICONS += examsys.ico

 

图标已经更改过来了

4.3通过dos进行项目打包

将项目由DeBug模式改为Release模式,并编译

建立一个文件夹作为发布文件,将Release下生成的.exe文件和需要的文档拷贝进来

进入要发布文件夹路径,然后输入windeployqt 文件名.exe,点击回车

打包完成 

 

如果提示 windeployqt不是命令的话需要将安装的qt的bin路径添加到环境变量中

 

项目完成✌✌✌ 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/848907
推荐阅读
相关标签
  

闽ICP备14008679号