小项目带你轻松入门Qt——注释完整

前言

我要做的是一个驾照的科目一考试系统,通过在登陆界面进行账号密码匹配进入题库

一些基本的信息和流程

  • 账号密码保存在文件
  • 题库也是保存在文件
  • 通过正则表达式来验证邮箱的格式是否正确
  • 利用布局管理器来布局题库和选项
  • 提交答案统计分数

登陆界面

实现效果:
在这里插入图片描述
①.账号,密码,背景都是使用Lable来实现的
②.确定,取消都是使用push button
③.账号输入条和密码输入条用的是Line Edit


1.背景的设置:

先添加Qt资源文件再点背景标签属性中的pixmap选项中设置。
在这里插入图片描述

在这里插入图片描述


2.背景和对话框的大小和填充

标题:
this->setWindowTitle(“驾校科目一考试登陆”);

LoginDialog::LoginDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginDialog)//一些窗口信息和一些控件信息都可以由ui指针来操作和访问
{
    ui->setupUi(this);
    //窗体,标签,对话框保持一致
    ui->image_label->setScaledContents(true);//填充属性,使用填充,要把x,y设置成(0,0)

    //this是用于设置当前窗体的一些属性
    this->resize(ui->image_label->width(),ui->image_label->height());//设置窗体自己的长宽,

    //设置当前窗口标题,和风格
    this->setWindowTitle("驾校科目一考试登陆");
    //只要一个对话框还有一个关闭按钮
    this->setWindowFlags(Qt::Dialog|Qt::WindowCloseButtonHint);

    //设置固定宽和高
    this->setFixedSize(width(),height());
}

如过没有在填充的时候搞到坐标为(0,0)是不完美的,因为它是对应窗口的长和宽进行填充的.
在这里插入图片描述


验证邮箱

1.槽方法实现

先要对确定按钮设置槽方法(点击"确定"会产生上面反应)
在槽方法中采用正则验证,具体操作都在代码中注释了

弹窗提示:
QMessageBox::information(this,“提示”,“欢迎进入科目一考试系统”);

正则验证表达式:
QRegExp rx("^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\_]+\.)+[A-Za-z]{2,3}$");

void LoginDialog::on_login_buttle_clicked()//槽方法
{
    //QMessageBox::information(this,"提示","槽方法调用了");
    //正则验证:筛选账号是否是邮箱地址 用户名@域名
    //元字符解释:^表示规则字符串的开始¥表示规则字符串的结束
    //+表示匹配次数>=1 *表示匹配任意次数(可为0次){n,m}表示匹配次数至少n次至多m次
    QRegExp rx("^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\_]+\.)+[A-Za-z]{2,3}$");
    
    /*返回值bool
    *accountEdit为账号输入条
    *ui->accountEdit->text()为账号输入条的内容
    */
    
    bool res = rx.exactMatch(ui->accountEdit->text());
    if(!res){//匹配不成功
        QMessageBox::information(this,"提示","非法的邮箱地址重新输入");
        ui->accountEdit->clear();//清理账号
        ui->codeEdit->clear();//清理密码
        ui->accountEdit->setFocus();
        return;
        
    }else{
        QString filename;//数据文件
        QString strAccInput;//用户输入的账号
        QString strCode;//用户输入的密码
        QString strLine;//每次读取的一行数据
        QStringList strList;//字符串链表,保存分割读取的一行数据
        filename="../account.txt";//上级目录下acount
        strAccInput=ui->accountEdit->text();
        strCode=ui->codeEdit->text();
        QFile file(filename);//定义一个QFile类型的对象file初始化
        
        QTextStream stream(&file);//文件流操作
        
        if(file.open(QIODevice::ReadOnly|QIODevice::Text)){
            while(!stream.atEnd()){
            strLine=stream.readLine();//读一行返回QString类型
            strList=strLine.split(",");//以逗号分割
            if(strAccInput==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;
        }else{
            QMessageBox::information(this,"提示","提取数据失败");
        }
    }
}

2.密码的隐藏

步骤:
点击密码输入条->属性->echoMode调成password
在这里插入图片描述


考试时间

实现效果:
在这里插入图片描述

1.考试时间方法的设计:

class ExamDialog : public QDialog
{
    //支持信号与槽
    Q_OBJECT
public:
    //初始化父类设置默认值
    ExamDialog(QWidget *parent=0);
    void initTimer();
    
private:
    QTimer *m_timer;//计时器
    int m_timeGo;//考试已用时
    
private slots://下面是该类的槽方法,进行信号与槽的连接,所执行的方法
    void freshTime();
};

2.初始化时间的信息和操作

//初始化时间类中的信息
void ExamDialog::initTimer()
{
    m_timeGo=0;//考试时间为0
    m_timer=new QTimer(this);
    m_timer->setInterval(1000);//1秒钟触发一次timeout()
    m_timer->start();//计时开始
    
    /*信号与槽,timeout是在QTimer类中的,对应上面的1000毫秒
    *  1000毫秒扫描一次执行SLOT中的槽方法这里是指freshTime方法(刷新时间操作)
    */
    connect(m_timer,SIGNAL(timeout()),this,SLOT(freshTime()));
}

3.刷新时间方法freshTime

//刷新时间操作
void ExamDialog::freshTime()
{
    //刷新考试用时
    m_timeGo++;
    
    //因为显示在主题上面必须是字符串,QString::number()来将括号里的类型转换成QString类型
    QString min=QString::number(m_timeGo/60);
    QString sec=QString::number(m_timeGo%60);
    
    //更改标题
    setWindowTitle("考试用时:"+min+"分"+sec+"秒");
}

感觉这样如果全部写下去的话篇幅会很长所以下面就贴代码了,注释全部都在代码中

完整代码实现

登陆窗口的头文件

#ifndef LOGINDIALOG_H
#define LOGINDIALOG_H

#include <QDialog>

namespace Ui {
class LoginDialog;  //Ui_LoginDialog  描述登陆窗口界面信息(QT自动生成)
}

class LoginDialog : public QDialog
{
    //用于不同对象间的通信
    Q_OBJECT//当前的类支持信号与槽机制(只有类是继承自QOBJECT才可以使用信号与槽机制)


public:
    explicit LoginDialog(QWidget *parent = 0);
    ~LoginDialog();

private slots:
    void on_login_button_clicked();

    void on_canceal_button_clicked();

private:
    Ui::LoginDialog *ui;
};

#endif // LOGINDIALOG_H



登陆窗口的cpp文件

#include "logindialog.h"
#include "ui_logindialog.h"
#include<QMessageBox>
#include<QFile>
#include<QTextStream>
#include<QSplitter>
LoginDialog::LoginDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginDialog)//一些窗口信息和一些控件信息都可以由ui指针来操作和访问
{
    ui->setupUi(this);
    //窗体,标签,对话框保持一致
    ui->image_label->setScaledContents(true);//填充属性,使用填充,要把x,y设置成(0,0)

    //this是用于设置当前窗体
    this->resize(ui->image_label->width(),ui->image_label->height());//设置窗体自己的长宽,

    //设置当前窗口标题,和风格
    this->setWindowTitle("驾校科目一考试登陆");
    //只要一个对话框还有一个关闭按钮
    this->setWindowFlags(Qt::Dialog|Qt::WindowCloseButtonHint);

    //设置固定宽和高
    this->setFixedSize(width(),height());
}

LoginDialog::~LoginDialog()
{
    delete ui;
}

void LoginDialog::on_login_button_clicked()
{
    //QMessageBox::information(this,"提示","点击了登陆");//指定当前窗口为父窗口
    //正则验证邮箱地址用户名@域名
    //元字符解释:^表示规则字符串的开始 $表示规则字符串的结束
    //+表示匹配次数>=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->account_lineEdit->text());//用用户输入的账号进行筛选

    if(res==false){
        QMessageBox::information(this,"提示","非法邮箱地址!");
        ui->account_lineEdit->clear();
        ui->account_lineEdit->setFocus();
        ui->password_lineEdit->clear();
    }else{
         //验证账号以及密码
         QString filename;    //账号密码数据文件
         QString strAccInput; //用户输入的账号
         QString strCode;     //用户输入的密码
         QString strLine;     //文件读一行
         QStringList strList; //链表,字符串类型
         filename="account.txt";//因为项目路径为debug所以要../,我们是把文字放在debug同级而不是在debug下
         strAccInput=ui->account_lineEdit->text();
         strCode = ui->password_lineEdit->text();

         QFile file(filename);//初始化文件类型,保存路径
         QTextStream stream(&file);//文件流初始化

         if(file.open(QIODevice::ReadOnly|QIODevice::Text)){
             while(!stream.atEnd()){
             strLine = stream.readLine();//每次读一行
             strList=strLine.split(",");//以某种分隔符来分隔字符串,返回值为链表
             if(strAccInput==strList.at(0)){//账号匹配成功,逗号前的保存在at(0)
                 if(strCode==strList.at(1)){//密码匹配成功,逗号后的保存在at(1)
                     QMessageBox::information(this,"提示","欢迎登陆科目一考试系统");
                     //返回接受,关闭当前窗口
                     done(Accepted);              
                     return;
                 }

                 QMessageBox::information(this,"提示","密码有误请重新输入!");
                 ui->password_lineEdit->clear();
                 ui->password_lineEdit->setFocus();
                 file.close();
                 return;
             }

             }


             QMessageBox::information(this,"提示","你输入的账号有误请重新输入");
             ui->account_lineEdit->clear();
             ui->password_lineEdit->clear();
             ui->account_lineEdit->setFocus();
         }else{
             QMessageBox::information(this,"提示","读取账号数据文件失败");
             return;
         }

    }

}

void LoginDialog::on_canceal_button_clicked()
{
    //关闭当前模态窗口并且返回拒绝
    done(Rejected);//返回拒绝
}



具体题库的头文件

#ifndef EXAMDIALOG_H
#define EXAMDIALOG_H
#include<QDialog>
#include<QTimer>
#include <QString>
#include<QTextEdit>
#include<QLabel>
#include<QRadioButton>
#include<QCheckBox>
#include<QGridLayout>
#include<QButtonGroup>
class ExamDialog : public QDialog
{
    //支持信号与槽
    Q_OBJECT
public:
    ExamDialog(QWidget *parent=0);
    void initTimer();
    void initLayout();             //初始化布局管理器
    bool initTextEdit();           //初始化文本编辑器
    void initButtons();            //初始化按钮和标签
    bool hasNoSelect();            //判断题目是否有未完成的题目

private:
    QTimer *m_timer;  //计时器
    int m_timerGo;    //考试已用时

    QTextEdit *m_textEdit;         //考试题库显示(文本编辑器)
    QLabel *m_titleLable[10];      //题目标签,10个题目
    QRadioButton *m_radioBtns[32]; //单选题按钮

    QCheckBox *m_checkBtns[4];     //多选题按钮
    QRadioButton *m_radioA;        //判断题A选项
    QRadioButton *m_radioB;        //判断题B选项

    QGridLayout *m_layout;         //格线布局管理器

    QStringList m_answersList;     //答案链表

    QButtonGroup*m_btnGroup[9];    //按钮分组,主要是单选题的分组,要不然默认一个分组

private slots:
    void freshTime();
    void getScore();
};
#endif // EXAMDIALOG_H

具体题库的cpp文件

#include "examdialog.h"
#include<QFile>
#include<QTextStream>
#include<QMessageBox>
#include<QApplication>
#include<QPushButton>
ExamDialog::ExamDialog(QWidget *parent):QDialog(parent)
{
    //设置当前窗体对象字体大小
    QFont font;
    //设置窗体背景颜色
    setPalette(QPalette(QColor(209,215,255)));

    font.setPointSize(12);  //字号大小
    this->setFont(font);
    this->setWindowTitle("考试已用时:0分0秒");
    this->resize(800,900);


    //设置窗体风格
    this->setWindowFlags(Qt::Dialog|Qt::WindowCloseButtonHint);

    initTimer();
    initLayout();
}

void ExamDialog::initTimer()
{
    m_timerGo=0;
    m_timer=new QTimer(this);
    m_timer->setInterval(1000);//1秒钟触发一次timeout()
    m_timer->start();
    connect(m_timer,SIGNAL(timeout()),this,SLOT(freshTime()));
}

void ExamDialog::initLayout()
{
    m_layout=new QGridLayout(this);
    m_layout->setSpacing(10); //设置控件间的间距
    m_layout->setMargin(10);  //设置窗体与控件间的间隙
    if(!initTextEdit()){
        QMessageBox::information(this,"提示","初始化题库数据文件失败");
        //退出应用程序
        QTimer::singleShot(0,qApp,SLOT(quit()));//就会给当前应用程序发送结束信号,并且0秒后执行
    }
    initButtons();
    show();
}

//初始化文本编辑器
bool ExamDialog::initTextEdit()
{
    QString strLine;    //保存文件中读取到的一行数据
    QStringList strList;
    QString fileName("exam.txt");
    QFile file(fileName);
    QTextStream stream(&file);  //文件流
    stream.setCodec("UTF-8");//编码格式
    bool res=file.open(QIODevice::ReadOnly|QIODevice::Text);  //只读以及纯文本文件,返回值为bool类型

    if(res){
        //初始化
        this->m_textEdit=new QTextEdit(this);
        m_textEdit->setReadOnly(true);
        QString strText;//用于保存显示到文本编辑框中
        int nLines=0;   //看你读到第几行
        while(!stream.atEnd()){
            //过滤首行
            if(nLines==0){
                stream.readLine();//读取完之后就好
                nLines++;
                continue;
            }

            //过滤答案行
            if(((nLines>=6)&&(nLines<=6*9)&&(nLines%6==0))||
                    (nLines==6*9+4)){
                strLine=stream.readLine();
                nLines++;
                strList=strLine.split(" ");
                this->m_answersList.append(strList.at(1));
                strText+="\n";
                continue;
            }

            strText+=stream.readLine();
            strText+="\n";//读完一行+一个换行
            nLines++;
        }

        m_textEdit->setText(strText);//把这个文本输入到文本编辑框中

        m_layout->addWidget(m_textEdit,0,0,1,10);//从0行0列开始占据1行10列
        file.close();
        return true;
    }else{
        QMessageBox::information(this,"提示","打开文件失败");
        return false;
    }
}
//下面选项的布局
void ExamDialog::initButtons()
{
    //字符串链表
    QStringList strList={"A","B","C","D"};

    for(int i=0;i<10;i++){
        //标签的设置
        m_titleLable[i]=new QLabel(this);
        m_titleLable[i]->setText("第" +QString::number(i+1)+ "题");
        m_layout->addWidget(m_titleLable[i],1,i);//占据默认
        //判断题
        if(i==9){
            m_radioA=new QRadioButton(this);
            m_radioA->setText("正确");
            m_radioB=new QRadioButton(this);
            m_radioB->setText("错误");

            m_layout->addWidget(m_radioA,2,9);
            m_layout->addWidget(m_radioB,3,9);
            m_btnGroup[8]=new QButtonGroup(this);
            m_btnGroup[8]->addButton(m_radioA);
            m_btnGroup[8]->addButton(m_radioB);
            continue;
        }

        if(i<8){
            m_btnGroup[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{

            //构造到第几个选项
            m_radioBtns [4*i+j]=new QRadioButton(this);
            m_radioBtns [4*i+j]->setText(strList.at(j));
            m_layout->addWidget(m_radioBtns [4*i+j],2+j,i);
            m_btnGroup[i]->addButton(m_radioBtns[4*i+j]);
        }
      }

    }

    QPushButton*submitBtn=new QPushButton(this);
    submitBtn->setText("提交");
    submitBtn->setFixedSize(100,35);
    //提交按钮进行信号与槽连接
    connect(submitBtn,SIGNAL(clicked(bool)),this,SLOT(getScore()));
    m_layout->addWidget(submitBtn,6,9);
}

//看是否题目全部做完了
bool ExamDialog::hasNoSelect()
{
    int radioSelects=0;//计数
    for(int i=0;i<8;i++){
        //分组中是否有按钮有给选中的
        if(m_btnGroup[i]->checkedButton()){
            radioSelects++;
        }
    }

    if(radioSelects!=8){
        return true;
    }

    //多选题的判断(是否有选)
    int checkSelects=0;
    for(int i=0;i<4;i++){
        if(m_checkBtns[i]->isChecked()){
            checkSelects++;
        }
    }

    if(checkSelects<=1){
        return true;
    }

    //最后的判断题
    if(!m_radioA->isCheckable()&&!m_radioB->isChecked()){
        return true;
    }

    return false;

}

void ExamDialog::freshTime()
{
    //刷新考试用时
    m_timerGo++;
    QString minute=QString::number(m_timerGo/60);
    QString sec=QString::number(m_timerGo%60);
    this->setWindowTitle("考试已用时:"+minute+"分"+sec+"秒");
}


void ExamDialog::getScore()
{
    if(hasNoSelect()){
        QMessageBox::information(this,"提示","您有未完成的题目,请继续考试","是");
        return;
    }

    int scores=0;
    for(int i=0;i<10;i++){
        //单选题
        if(i<8){
            //按钮分组所选中的按钮的文本和答案进行匹配
            if(m_btnGroup[i]->checkedButton()->text()==m_answersList.at(i)){
                scores+=10;
            }
        }

        //多项选择题
        if(i==8){
            QString answer=m_answersList.at(i);  //先用一个字符串拿来保存多选题答案
            bool hasA=false;
            bool hasB=false;
            bool hasC=false;
            bool hasD=false;
            //在字符串中是否包含某个字符串
            if(answer.contains("A"))hasA=true;
            if(answer.contains("B"))hasB=true;
            if(answer.contains("C"))hasC=true;
            if(answer.contains("D"))hasD=true;
            //获取状态
            bool checkA= m_checkBtns[0]->checkState();
            bool checkB= m_checkBtns[1]->checkState();
            bool checkC= m_checkBtns[2]->checkState();
            bool checkD= m_checkBtns[3]->checkState();

            //看答案状态和勾选的答案进行匹配看错没错,一错就continue
            if(hasA!=checkA)continue;
            if(hasB!=checkB)continue;
            if(hasC!=checkC)continue;
            if(hasD!=checkD)continue;

            scores+=10;
        }
        //判断题计分
        if(i==9){
            if(m_btnGroup[8]->checkedButton()->text()==m_answersList.at(i)){
                scores+=10;
            }
        }
    }

    QString str="你的分数是:"+QString::number(scores)+"分,是否重新考试";
    int res=QMessageBox::information(this,"提示",str,QMessageBox::Yes|QMessageBox::No);
    if(res==QMessageBox::Yes){
        return;
    }else{
        close();  //关闭当前this窗口
    }
}

main方法

#include "logindialog.h"
#include <QApplication>
#include "examdialog.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    LoginDialog logDialog;  //登陆窗口的对象
    int res=logDialog.exec();  //模态显示w,会有两种返回结果:接受以及拒绝
    if(res==QDialog::Accepted){
        //防止生命周期的问题
        ExamDialog *examDialog;
        examDialog=new ExamDialog();
    }else{
        return 0;
    }
    
    return a.exec();//应用程序对象的exec()方法,使当前的应用程序进行消息循环
}


我们再来看看我的题库的文件是什么样的,我们的题库布局就是要按里面的顺序来(不完整共10题,看备注),单选题以及判断题都可以加到自己的按钮分组中,多选题单独搞
在这里插入图片描述
我们的账号密码文件:
我们以’,’为分隔符把账号密码保存下来,再来进行比对
在这里插入图片描述

运行结果以及测试

登陆窗口:
在这里插入图片描述
匹配账号密码成功后转入:
在这里插入图片描述
看下测试吧:
未完成全部选择的情况
在这里插入图片描述
看下分数:
在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值