一 登录界面
- 创建项目的时候选择
Dialog
- 图片大小和背景框不匹配时,可以勾选
scaledContents
1 正则表达式 验证用户密码和邮箱
// \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写
\w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
//正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
//[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为
\\-,以确保它被当作普通字符而不是范围符号,
//, +表示前面字符出现一次或多次, +不需要转义字符
[]
cpp
void DialogLogin::on_pushButtonLogin_clicked()
{
//要求必须以字母开头,6-18个字符,\w表示匹配字母数字或下划线
// \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写 \w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
//正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
//[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为 \\-,以确保它被当作普通字符而不是范围符号,
//, +表示前面字符出现一次或多次, +不需要转义字符
QRegExp mail("^[a-zA-Z][\\w]{5,17}@([a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,6}$");
bool res = mail.exactMatch(ui->lineEditLogin->text());
if(!res){ //验证邮箱的合法性
QMessageBox::warning(this,"提示","非法邮箱,请重新输入");
ui->lineEditLogin->clear();
ui->lineEditLogin->setFocus();
}else{ //验证用户和密码的一致性
QString code = ui->lineEditCode->text();
QString user = ui->lineEditLogin->text();
if(code.isEmpty()){
QMessageBox::warning(this,"提示","密码不能为空");
}
//QString filename = FILENAME;
QString filename = "I:/QT_PRACTICE/myExam22/account.txt";
if(!filename.isEmpty()){
//把文本编辑器的内容保存到文本
QFile qfile(filename);
qDebug() << filename;
if(! qfile.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << "文件打开失败";
return;
}
QStringList codeList;
QStringList userList;
QTextStream stream(&qfile);
while (!stream.atEnd()) {
QString line = stream.readLine();
// 分割邮箱和密码
QStringList list = line.split(',');
if (list.size() == 2) {
QString email = list.at(0);
QString password = list.at(1);
codeList.append(password);
userList.append(email);
}
}
qfile.close();
bool succeedLabel = 0;
for(int i = 0; i< codeList.count() ; i++){
if(code == codeList[i] && user == userList[i]){
QMessageBox::information(this,"提示","成功登录");
succeedLabel = 1;
break;
}
}
if(succeedLabel == 0){
QMessageBox::warning(this,"提示","用户名或密码错误");
ui->lineEditCode->clear();
ui->lineEditLogin->clear();
ui->lineEditLogin->setFocus();
}
}
}
}
2 知识点
- 知识点1 :只有第一次调用的时候需要设置起始点,之后就不需要了,否则位置坐标不对
m_layout->addWidget(m_textEdit,0,0,1,10); //起始位置 0行,0列,行宽和列宽分别是1和10
,后面调用 ``m_layout->addWidget(title,1,k);
,` - 知识点2 :自定义组件我看有两种方式,一种是在头文件定义指针之后,在函数里面使用的时候,重新new一个对象,另一种是,头文件定义指针之后,cpp文件就直接用,不再new了。啥时候要new新的对象,啥时候不需要new呢?
(a)头文件定义类对象指针,但是并没有分配内存空间,所以后面用的时候是必须要newm_textEdit = new QTextEdit(this);
来进行空间的分配的,否则就成页指针了
(b)至于什么时候new,那取决于是整个程序只有一个组件,还是有多个组件,如果是多个组件,那么在不同地方调用的时候,都需要new一个新的内存空间,但是如果只是一个组件,那就只需要在构造函数里面进行一次空间分配,然后后面函数就可以直接使用了

-
知识点3 :什么时候需要定义信号,当前案例里面,自定义的
pushButon
按钮实现考试的提交,也是属于Qt按钮1.只有自己写的类才需要定义自己的信号,如果调用已有的组件,是不需要自己来写信号的
2.QT内部组件会自动发送信号,不需要手动发送信号
-
知识点4 :什么时候需要创建c++类文件,什么时候需要创建Qt设计师界面类,
.cpp
类文件的情况::Graphics View组件对应的类对象是QGraphicsView,是标准类对象,无法进行修改,需要自定义一个视图,去重载该类的鼠标事件

-
知识点5 :如果要控制一个界面A运行完毕,关闭当前界面A然后打开另一个界面B,在
main
函数里面做如下实现,同时在界面A的相应确认打开界面B的位置添加done(Accepted);
语句 -
知识点6 :
QVector<Question> Data; //存放题库
和QVector<QButtonGroup*> btnGroup;
,定义指针和不定义指针的情况有什么区别,调用的时候都是通过.
来调用,而不是->
,以下是查到的区别,对于动态分配内存这一点的理解是:前者调用的时候不需要使用new
来为对象分配内存空间,后者需要
cpp
private:
QVector<Question> Data; //存放题库
QVector<QButtonGroup*> btnGroup;
cpp
当前案例使用
//判断是单选还是多选
QButtonGroup *group = new QButtonGroup;
if(Data[k].isSigleQuestion()){ //单选
for(int i =0; i< Data[k].get_choiceNum(); i++){
QRadioButton *btn = new QRadioButton;
btn->setText(Data[k].quesChoices[i].first);
group->addButton(btn);
m_layout->addWidget(btn,rwoIndex + i + 1,k%eachLineShow_num); //
}
}else{ //多选
group->setExclusive(false);
for(int i =0; i< Data[k].get_choiceNum(); i++){
QCheckBox *checkBox = new QCheckBox;
checkBox->setText(Data[k].quesChoices[i].first);
group->addButton(checkBox);
m_layout->addWidget(checkBox,rwoIndex + i + 1,k%eachLineShow_num); //
}
}
btnGroup.append(group);
bash
case READ_ANSER:
if(line.at(0) == '!'){
line.remove(0,1);
//qDebug() << "答案:"<< line;
anse.append(line);
Question ques(titelLable, anse);
//qDebug() << "choiceLabel.size():"<< choiceLabel.size();
for(int i =0; i< choiceLabel.size(); i++){
QPair<QString, int> temp;
ques.quesChoices.push_back(QPair<QString, int>(choiceLabel[i],0));
}
Data.append(ques);
}
cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DialogLogin w;
if(w.exec() == QDialog::Accepted){
QDialogExamUI ed;
ed.show();
return a.exec();
}
return a.exec();
}
3 代码及文件创建 add->new->cpp
,选择或者填入继承QDialog
类,创建.cpp文件,注意,头文件要做相应的修改
- 效果

"dialoglogin.h"
cpp
#ifndef DIALOGLOGIN_H
#define DIALOGLOGIN_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class DialogLogin; }
QT_END_NAMESPACE
class DialogLogin : public QDialog
{
Q_OBJECT
public:
DialogLogin(QWidget *parent = nullptr);
~DialogLogin();
private slots:
void on_pushButtonLogin_clicked();
void on_pushButtonExit_clicked();
private:
Ui::DialogLogin *ui;
};
#endif // DIALOGLOGIN_H
"qdialogexamui.h"
cpp
#ifndef QDIALOGEXAMUI_H
#define QDIALOGEXAMUI_H
#include <QDialog>
#include <QTextEdit>
#include <QGridLayout>
#include <QVector>
#include "question.h"
#include <QCheckBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QAbstractButton> //是 QRadioButton和 QCheckBox的父类
#include <QPushButton>
enum State{
BEGIN,
READ_TILTE,
READ_CHOICE,
READ_ANSER
};
class QDialogExamUI : public QDialog
{
Q_OBJECT
public:
QDialogExamUI(QWidget *parent = nullptr);
~QDialogExamUI();
private:
QTextEdit *m_textEdit; //显示考试内容
QGridLayout *m_layout; //布局管理器
void initUI(); //初始化组件
void initSlot();
void initContent(); //解析文本内容
void initShowContent();
double getScore();
QVector<Question> Data; //存放题库
QVector<QButtonGroup*> btnGroup;
QPushButton *m_submitBtn;
int eachLineShow_num = 10;
private slots: //槽函数
void on_m_submitBtn_clicked(bool clicked);
};
#endif // QDIALOGEXAMUI_H
"question.h"
cpp
#ifndef QUESTION_H
#define QUESTION_H
#include <QString>
#include <QStringList>
#include <QVector>
#include <QPair>
class Question
{
public:
Question(const QString quesIndex, const QString quesAnser);
Question();
~Question();
Question(const Question &ques); //拷贝构造函数
// QVector<QPair<QString,QString>> quesChoices; // <QPair <选项索引,选项内容>> 考虑到绘制题目的时候不需要选项内容
QVector<QPair<QString,int>> quesChoices; // <QPair <选项索引,选项是否被选中>>
bool isSigleQuestion(); //判断是否是单选
QString get_quesIndex();
QStringList get_quesAnser();
int get_choiceNum();
private:
QString quesIndex; //题目编号
QString quesAnser; //题目答案
int choiceNum; //选项数量
bool is_single_ques; //1 单选,0多选
};
#endif // QUESTION_H
"dialoglogin.cpp"
cpp
#include "dialoglogin.h"
#include "ui_dialoglogin.h"
#include <QDebug>
#include <QMessageBox>
#include <QFile>
#define FILENAME "account.txt"
DialogLogin::DialogLogin(QWidget *parent)
: QDialog(parent)
, ui(new Ui::DialogLogin)
{
ui->setupUi(this);
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); //关掉默认的右上角的叉叉
setWindowTitle("驾校考试系统");
}
DialogLogin::~DialogLogin()
{
delete ui;
}
void DialogLogin::on_pushButtonLogin_clicked()
{
//要求必须以字母开头,6-18个字符,\w表示匹配字母数字或下划线
// \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写 \w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
//正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
//[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为 \\-,以确保它被当作普通字符而不是范围符号,
//, +表示前面字符出现一次或多次, +不需要转义字符
QRegExp mail("^[a-zA-Z][\\w]{5,17}@([a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,6}$");
bool res = mail.exactMatch(ui->lineEditLogin->text());
if(!res){ //验证邮箱的合法性
QMessageBox::warning(this,"提示","非法邮箱,请重新输入");
ui->lineEditLogin->clear();
ui->lineEditLogin->setFocus();
}else{ //验证用户和密码的一致性
QString code = ui->lineEditCode->text();
QString user = ui->lineEditLogin->text();
if(code.isEmpty()){
QMessageBox::warning(this,"提示","密码不能为空");
}
//QString filename = FILENAME;
QString filename = "I:/QT_PRACTICE/myExam22/account.txt";
if(!filename.isEmpty()){
//把文本编辑器的内容保存到文本
QFile qfile(filename);
qDebug() << filename;
if(! qfile.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << "文件打开失败";
return;
}
QStringList codeList;
QStringList userList;
QTextStream stream(&qfile);
while (!stream.atEnd()) {
QString line = stream.readLine();
// 分割邮箱和密码
QStringList list = line.split(',');
if (list.size() == 2) {
QString email = list.at(0);
QString password = list.at(1);
codeList.append(password);
userList.append(email);
}
}
qfile.close();
bool succeedLabel = 0;
for(int i = 0; i< codeList.count() ; i++){
if(code == codeList[i] && user == userList[i]){
QMessageBox::information(this,"提示","成功登录");
succeedLabel = 1;
done(Accepted);
break;
}
}
if(succeedLabel == 0){
QMessageBox::warning(this,"提示","用户名或密码错误");
ui->lineEditCode->clear();
ui->lineEditLogin->clear();
ui->lineEditLogin->setFocus();
}
}
}
}
void DialogLogin::on_pushButtonExit_clicked()
{
this->close();
}
"qdialogexamui.cpp"
cpp
#include "qdialogexamui.h"
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
#include <QFont>
#include <QDebug>
#include <QLabel>
#include <QAbstractButton>
QDialogExamUI::QDialogExamUI(QWidget *parent):QDialog(parent)
{
m_textEdit = new QTextEdit(this);
m_layout = new QGridLayout(this);
m_submitBtn = new QPushButton(this);
initUI();
initContent();
initShowContent();
initSlot();
}
QDialogExamUI::~QDialogExamUI()
{
}
void QDialogExamUI::initUI()
{
//-----------------主窗体初始化----------------------------
this->setMinimumSize(800, 600); // 设置窗体的最小大小为 800x600
//-----------------布局管理器边界设置的初始化----------------------------
m_layout->setMargin(10);
m_layout->setSpacing(10);
//-----------------文本编辑器----------------------------
// //显示文本
// m_textEdit->setMaximumWidth(200);
// m_textEdit->setMaximumHeight(900);
//设置显示文本的字体和大小
QFont font = m_textEdit->font(); //先将字体取出来
font.setPointSize(12); //字体
font.setFamily("微软雅黑");
m_textEdit->setFont(font);
//m_layout->addWidget(m_textEdit,0,1);
m_layout->addWidget(m_textEdit,0,0,1,10); //起始位置 0行,0列,行宽和列宽分别是1和10
}
void QDialogExamUI::initSlot()
{
//-----------------考试提交按钮----------------------------
connect(m_submitBtn,SIGNAL(clicked(bool)),this,SLOT(on_m_submitBtn_clicked(bool)));
}
void QDialogExamUI::initShowContent()
{
QFont font = m_textEdit->font(); //先将字体取出来
font.setPointSize(12); //字体
font.setFamily("微软雅黑");
m_textEdit->setFont(font);
//-----------------显示题目和选项按钮----------------------------
int maxChoiceNum =0;
int rwoIndex =1;
if(eachLineShow_num > Data.size()){
eachLineShow_num = Data.size();
}
for(int k = 0; k < Data.size(); k++){
if(k % eachLineShow_num == 0)
{
rwoIndex += maxChoiceNum +1;
}
//获取每行显示题目数量的最大题目数
for(int i =0; i< eachLineShow_num; i++){
if(Data[i].get_choiceNum() > maxChoiceNum){
maxChoiceNum = Data[i].get_choiceNum();
}
}
//显示标题
QLabel *title = new QLabel(this);
title->setText("第" + Data[k].get_quesIndex()+ "题");
title->setFont(font);
// m_layout->addWidget(title,0,0,1,k);
m_layout->addWidget(title,rwoIndex,k%eachLineShow_num); //
//显示选项
//判断是单选还是多选
QButtonGroup *group = new QButtonGroup;
if(Data[k].isSigleQuestion()){ //单选
for(int i =0; i< Data[k].get_choiceNum(); i++){
QRadioButton *btn = new QRadioButton;
btn->setText(Data[k].quesChoices[i].first);
group->addButton(btn);
m_layout->addWidget(btn,rwoIndex + i + 1,k%eachLineShow_num); //
}
}else{ //多选
group->setExclusive(false);
for(int i =0; i< Data[k].get_choiceNum(); i++){
QCheckBox *checkBox = new QCheckBox;
checkBox->setText(Data[k].quesChoices[i].first);
group->addButton(checkBox);
m_layout->addWidget(checkBox,rwoIndex + i + 1,k%eachLineShow_num); //
}
}
btnGroup.append(group);
}
//-----------------考试提交按钮----------------------------
m_submitBtn->setText("提交");
m_submitBtn->setFont(font);
m_layout->addWidget(m_submitBtn,rwoIndex+maxChoiceNum+1,eachLineShow_num-1);
}
double QDialogExamUI::getScore()
{
double socre = 0;
for(int k= 0; k <Data.size(); k++){
QList<QAbstractButton*> buttons = btnGroup[k]->buttons();
QStringList ans = Data[k].get_quesAnser();
//判断是单选还是多选
if(Data[k].isSigleQuestion()){
for(int i =0; i <buttons.size();i ++){
if(buttons[i]->isChecked() && buttons[i]->text() == ans[0]){
socre += 10;
break;
}
}
}else{
int ans_num = ans.size();
int counter = 0;
for(int i =0; i <buttons.size();i ++){
//遍历答案进行对比
if(buttons[i]->isChecked()){
for(int a =0; a <ans_num;a ++){
// qDebug() <<"buttons[i]->text() = " << buttons[i]->text() << ",a = " << a << ",ans[a] = " << ans[a];
if(buttons[i]->text() == ans[a]){
counter += 1;
break;
}
}
}
}
if(counter == ans_num){
socre += 10;
}
}
}
return socre;
}
void QDialogExamUI::on_m_submitBtn_clicked(bool clicked)
{
bool ret = QMessageBox::information(this,"提示","是否确认提交",QMessageBox::Ok | QMessageBox::Cancel);
qDebug() << "ret = " << ret;
bool done = true;
if(ret){
//判断是否题目没有完成
for(int k= 0; k <Data.size(); k++){
QAbstractButton *checkedButton = btnGroup[k]->checkedButton();
if(!checkedButton){
QMessageBox::warning(this,"提示","还有选项为完成!",QMessageBox::Ok);
done = false;
break;
}
}
if(done){
double score = getScore();
bool ret2 =QMessageBox::information(this,"提示","您的考试分数是:" + QString::number(score) + ",是否退出考试?",QMessageBox::Ok,QMessageBox::NoButton);
if(ret2){
this->close();
}
}
}
}
void QDialogExamUI::initContent()
{
//当前函数,读取考题内容
QString filename = "I:/QT_PRACTICE/myExam22/exam.txt";
QFile file(filename);
if(! file.open( QIODevice::ReadOnly | QIODevice::Text)){
QMessageBox::warning(this,"警告","文件打开失败");
return;
}
State currentState = State::BEGIN;
bool readNew = true;
QString line;
QString titelLable;
QStringList choiceLabel;
QString anse;
QTextStream stream(&file);
stream.setCodec("UTF-8");
while(1){
if(!readNew){
readNew = true;
}else{
if(stream.atEnd()){
break;
}
line = stream.readLine();
}
if(line.isEmpty()) continue;
if(line.at(0) == "#") continue;
//状态机将题目存储进题库
switch(currentState){
case BEGIN:
titelLable.clear();
choiceLabel.clear();
anse.clear();
//开始状态要求必须是题目
if(line.at(0) == 'Q'){
currentState = State::READ_TILTE;
readNew = false;
}
else{
QMessageBox::warning(this,"警告","BEGIN, 题库格式错误");
}
break;
case READ_TILTE:
if(line.at(0) == 'Q'){
line.remove(0,1);
m_textEdit->append(line);
titelLable.append(line.split('.').first());
// qDebug() << "题目:" <<line.split('.').first()<< "内容" << line;
}else if(line.at(0) == '.'){
currentState = State::READ_CHOICE;
readNew = false;
}else{
QMessageBox::warning(this,"警告","READ_TILTE, 题库格式错误");
}
break;
case READ_CHOICE:
if(line.at(0) == '.'){
line.remove(0,1);
m_textEdit->append(line);
choiceLabel.append(line.at(0));
//qDebug() << "选项:" <<line.at(0) << "内容" << line;
}else if(line.at(0) == '!'){
currentState = State::READ_ANSER;
readNew = false;
}else{
QMessageBox::warning(this,"警告","READ_CHOICE, 题库格式错误");
}
break;
case READ_ANSER:
if(line.at(0) == '!'){
line.remove(0,1);
//qDebug() << "答案:"<< line;
anse.append(line);
Question ques(titelLable, anse);
//qDebug() << "choiceLabel.size():"<< choiceLabel.size();
for(int i =0; i< choiceLabel.size(); i++){
QPair<QString, int> temp;
ques.quesChoices.push_back(QPair<QString, int>(choiceLabel[i],0));
}
Data.append(ques);
}
else if(line.at(0) == 'Q'){
currentState = State::BEGIN;
readNew = false;
m_textEdit->append("\n");
}else{
QMessageBox::warning(this,"警告","READ_ANSER, 题库格式错误");
}
break;
}
}
file.close();
}
"question.cpp"
cpp
#include "question.h"
Question::Question(const QString quesIndex, const QString quesAnser)
{
this->quesIndex = quesIndex;
this->quesAnser = quesAnser;
this->choiceNum = 0;
quesChoices.clear();
QStringList list = quesAnser.split("");
// 移除第一个空字符串
if (!list.isEmpty() && list.first().isEmpty()) {
list.removeFirst();
}
for(int i =0; i< list.size();i ++){
if(list[i] == "" || list[i] == " "){
list.removeAt(i);
}
}
if(list.size() > 1){
this->is_single_ques = false;
}else if(list.size() == 1)
{
this->is_single_ques = true;
}
}
Question::Question()
{
}
Question::~Question()
{
}
Question::Question(const Question &ques)
{
this->quesIndex = ques.quesIndex;
this->quesAnser = ques.quesAnser;
this->is_single_ques = ques.is_single_ques;
this->choiceNum = ques.choiceNum;
// // 分配足够的内存空间
// this->quesChoices.resize(ques.quesChoices.size());
// for(int i =0;i < ques.quesChoices.size(); i++){
// this->quesChoices[i].first = ques.quesChoices[i].first;
// this->quesChoices[i].second = ques.quesChoices[i].second;
// }
this->quesChoices = ques.quesChoices;
}
bool Question::isSigleQuestion()
{
return is_single_ques;
}
QString Question::get_quesIndex()
{
return quesIndex;
}
QStringList Question::get_quesAnser()
{
QStringList list = quesAnser.split("");
// 移除第一个空字符串
if (!list.isEmpty() && list.first().isEmpty()) {
list.removeFirst();
}
for(int i =0; i< list.size();i ++){
if(list[i] == "" || list[i] == " "){
list.removeAt(i);
}
}
return list;
}
int Question::get_choiceNum()
{
this->choiceNum = quesChoices.size();
return this->choiceNum;
}