• 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
    4. #include
    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路径添加到环境变量中

     

    项目完成✌✌✌ 

  • 相关阅读:
    1480. Running Sum of 1d Array (Python)
    通过有序线性结构构造AVL树
    Day08
    Unity中使用VS常用技巧
    NVIDIA Jetson TX2 解决奥比中光 Astra pro相机的ros 打不开深度信息/camera/depth/image
    聚观早报 | 东方甄选推出独立 App;腾讯《冒险岛 2》即将停服
    串口通信原理及应用
    Java 基础常见知识点&面试题总结(下),2022 最新版!
    《Unity Shader入门精要》笔记05
    设计模式-单例模式-注册式单例模式-枚举式单例模式和容器式单例模式在Java中的使用示例
  • 原文地址:https://blog.csdn.net/qq_54169998/article/details/125871143