我要做的是一个驾照的科目一考试系统,通过在登陆界面进行账号密码匹配进入题库
一些基本的信息和流程
实现效果:
①.账号,密码,背景都是使用Lable来实现的
②.确定,取消都是使用push button
③.账号输入条和密码输入条用的是Line Edit
先添加Qt资源文件再点背景标签属性中的pixmap选项中设置。
标题:
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)是不完美的,因为它是对应窗口的长和宽进行填充的.
先要对确定按钮设置槽方法(点击"确定"会产生上面反应)
在槽方法中采用正则验证,具体操作都在代码中注释了
弹窗提示:
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,"提示","提取数据失败");
}
}
}
步骤:
点击密码输入条->属性->echoMode调成password
class ExamDialog : public QDialog
{
//支持信号与槽
Q_OBJECT
public:
//初始化父类设置默认值
ExamDialog(QWidget *parent=0);
void initTimer();
private:
QTimer *m_timer;//计时器
int m_timeGo;//考试已用时
private slots://下面是该类的槽方法,进行信号与槽的连接,所执行的方法
void freshTime();
};
//初始化时间类中的信息
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()));
}
//刷新时间操作
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
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
#include "logindialog.h"
#include "ui_logindialog.h"
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
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
#include "examdialog.h"
#include
#include
#include
#include
#include
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窗口
}
}
#include "logindialog.h"
#include
#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题,看备注),单选题以及判断题都可以加到自己的按钮分组中,多选题单独搞
我们的账号密码文件:
我们以’,’为分隔符把账号密码保存下来,再来进行比对