QT实现登录界面(利用MySQL保存数据和邮箱辅助注册)

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: QT实现登录界面(利用MySQL保存数据和邮箱辅助注册)

在这里插入图片描述

@TOC


1. 前言

       断断续续黑框框下的学生管理系统,也写了不下三次,总在黑框框下呆着也不是一回事,想挑战用qt做一个界面版的学生管理系统,至于为什么选用qt+C++,而不用C#之类的,是因为我目前学习C++,手头只接触了qt,所以想着挑战一下自己。从他人博客获取知识,为我所用,最后再输出到博客,供他人学习,这种感觉真的很棒。这几篇文章重在出效果,大牛勿喷,边学边做边发博,文中的代码可能不完整,我是拿出单个功能来说的,或许会出现错误,不用着急啦,需要用到的资源以及代码,我都会打包上传,如有需要的,可自行下载,当然如果你没有积分,我也能够体谅,你可以加我QQ2506897252,我免费发给你,禁止白嫖,原创不易,记得关注我,你们的关注才是我创造的动力!

操作环境:

  • QT5.10.0
  • MySQL8.0.19

遇到的错误及解决方法:

资源刚上传可能会显示404 ,因为需要审核。

后续系列:

  • 暂无

2. 效果图

2.1 大体

在这里插入图片描述


2.2 主界面

在这里插入图片描述


2.3 注册图

在这里插入图片描述

由于图片大小问题,具体操作就不录了,不过放心,都在代码里,嘿嘿。


3. 流程图

在这里插入图片描述


4. 代码实现

4.1 使用Mysql及邮箱发送

4.1.1 连接Mysql 并使用

为了方便,这里我用了Navicat Preminum 对MySQL进行可视化操作,我们在名为test的数据库下创建一个表名为user的表来存储用户数据:
在这里插入图片描述
四个字段分别为账户,密码,是否记住密码,以及注册时填写的邮箱。当然你会命令行更好了!
下面是连接我们的数据库:

#include<QSqlDatabase>
#include<QSqlQuery>
QSqlDatabase    dbconn;
QSqlQuery       query;
if(QSqlDatabase::contains(QSqlDatabase::defaultConnection))
{
    dbconn = QSqlDatabase::database(QSqlDatabase::defaultConnection);
}
else
{
    dbconn = QSqlDatabase::addDatabase("QMYSQL");
}
dbconn.setHostName("127.0.0.1");//主机名字
dbconn.setDatabaseName("test");//数据库名字
dbconn.open("root", "xxxxxx"); //第一个参数写用户名,这里我们就写root就可以,第二个参数密码是mysql的登陆密码。
//可以使用如下语句判断是否连接成功
if(dbconn.open("root", "xxxxxx"))
{
//如果判断为真,则连接成功
}
query = (QSqlQuery)dbconn; //进行绑定 此后可以使用query对象对数据库进行操作。

有很多人可能会提示数据库头文件的问题,如果你已经配置好了mysql,上翻开头的错误链接。


4.1.2 邮箱发送

邮箱发送的代码是我从网上抄下来的,原因是我还不太熟悉,大牛勿喷。
Qt5基于smtp服务发送电子邮件

想要通过代码登录我们的邮箱,需要开通stmp服务,登录我们的QQ邮箱,依次设置——账户——下拉开头stmp服务,这时它会出现一个授权码,这就是我们通过第三方登录的密码。
在这里插入图片描述
设置好以后上代码:
创建一个名为stmp的头文件:

#ifndef SMTP_H
#define SMTP_H
#include<QByteArray>
#include<QString>
#include<QTcpSocket>
class Smtp
{
public:
    Smtp(QByteArray username,QByteArray password);
    ~Smtp();
    void send(QByteArray recvaddr,QString subject,QString content);
private:
    QTcpSocket * clientsocket;
    QByteArray username;
    QByteArray password;
    QByteArray recvaddr;
    QByteArray mailfrom = "mail from:<";
    QByteArray rcptto = "rcpt to:<";
    QByteArray prefrom = "from:";
    QByteArray preto = "to:";
    QByteArray presubject ="subject:";
    QString subject;                //主题
    QString content;                //发送内容
    QByteArray recvdata;            //接收到的数据
};
#endif // SMTP_H

创建一个名为stmp的C++文件:

#include "smtp.h"
#include<qDebug>
Smtp::Smtp(QByteArray username,QByteArray password) //初始化发送者
{
    if(username.contains("@qq"))
    {
        this->username = username;
        this->password = password;
    }
    else
        qDebug()<<"NOT qq";
}
void Smtp::send(QByteArray recvaddr,QString subject,QString content) //发送邮件
{
    this->recvaddr = recvaddr;
    this->subject = subject;
    this->content = content;
    QByteArray usernametmp = this->username;
    QByteArray recvaddrtmp = this->recvaddr;
    clientsocket=new QTcpSocket();
    this->clientsocket->connectToHost("smtp.qq.com",25,QTcpSocket::ReadWrite);
    this->clientsocket->waitForConnected(1000);
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    this->clientsocket->write("HELO smtp.qq.com\r\n");
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    this->clientsocket->write("AUTH LOGIN\r\n");
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    qDebug()<<"username:"<<username;
    this->clientsocket->write(username.toBase64().append("\r\n"));
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    qDebug()<<"password:"<<password;
    this->clientsocket->write(password.toBase64().append("\r\n"));
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    this->clientsocket->write(mailfrom.append(usernametmp.append(">\r\n")));
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    //发送邮箱
    //qDebug()<<"mail from:"<<mailfrom.append(usernametmp.append(">\r\n"));
    this->clientsocket->write(rcptto.append(recvaddrtmp.append(">\r\n")));
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    //接收邮箱
    //qDebug()<<"rcp to:"<<rcptto.append(recvaddrtmp.append(">\r\n"));
    //data表示开始传输数据
    this->clientsocket->write("data\r\n");
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    usernametmp = this->username;
    recvaddrtmp = this->recvaddr;
    this->clientsocket->write(prefrom.append(usernametmp.append("\r\n")));
    this->clientsocket->write(preto.append(recvaddrtmp.append("\r\n")));
    this->clientsocket->write(presubject.append(subject.toLocal8Bit().append("\r\n")));
    this->clientsocket->write("\r\n");
    this->clientsocket->write(content.toLocal8Bit().append("\r\n"));
    this->clientsocket->write(".\r\n");
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
    this->clientsocket->write("quit\r\n");
    this->clientsocket->waitForReadyRead(1000);
    recvdata = clientsocket->readAll();
    qDebug()<<recvdata;
}
Smtp::~Smtp()
{
    delete this->clientsocket;
}

原文作者是使用163邮箱,我这里使用qq邮箱,如使用163,只需要将代码中qq替换为163即可,下面是如何发送邮件:

//第一个参数是发送者邮箱,第二个授权码,并不是邮箱密码,要注意
Smtp smtp("2506897252@qq.com","XXXXXXXXXXXXX");  
//发送邮件
smtp.send("接收者邮箱地址","标题","内容");
//稍后要发送的验证码只需要对字符串进行简单改动即可。

4.2 登录主界面


4.2.1 界面背景及按钮效果

界面背景是我自己用ps画的,我们将图片资源统一放到一个名为lib的文件夹里,方便管理。
如何在qt添加资源文件呢?右键项目创建qt Resource File 这个就是资源文件,建好以后,添加我们做好的背景资源。
在这里插入图片描述
然后双击我们的ui文件,打开设计师界面,右键我们的窗体,打开改变样式表,添加资源,选择border-image,添加我们的刚才加载好资源文件,如果仅仅是这样的话,添加之后,你会发现不单单是界面,连界面上的一些控件都有了背景颜色,这个时候我就应该约束一下,让资源仅作用于界面:

border-image: url(:/new/prefix1/C:/Users/fdog/Desktop/lib/mainblack.jpg);
//改为如下形式 只作用于MyDialog_2 
#MyDialog_2
{
border-image: url(:/new/prefix1/C:/Users/fdog/Desktop/lib/mainblack.jpg);
}

在这里插入图片描述
不出意外的话的,现在显示正常了,别高兴的太早,如果你现在运行该窗体,可能会不显示背景,解决方法是,打开左栏的项目,如果Sha build 有打勾的话,去掉他,就可以显示背景了。

下面来说说鼠标悬浮按钮上的的效果
在这里插入图片描述
如果使用默认按钮的话,其实是有效果的,但一旦加入我们自己的资源,默认效果就没了,怎么做呢,在这个窗体的cpp文件构造函数中加入如下代码:

//这里的pushBuuton_2是按钮的名字,
    ui->pushButton_2->setStyleSheet("QPushButton{border-image: url(:/new/prefix1/C:/Users/fdog/Desktop/lib/blackButton_1.jpg);}"
                                 "QPushButton:hover{border-image:url(:/new/prefix1/C:/Users/fdog/Desktop/lib/blackButton_2_2.jpg);}"
                             "QPushButton:checked{border-image:url(:/new/prefix1/C:/Users/fdog/Desktop/lib/blackButton_2_3.jpg);}");
                             //代表这按钮的三种状态显示 ,分别是未选中, 悬浮,点击

还想到一点就是按钮点击时有焦点框的,可以加入代码去除:

ui->pushButton_3->setStyleSheet("padding: -1"); //不单单适用于按钮哦

4.2.2 账户/密码校验

账户/密码校验思路就是将用户输入的账户和密码的文本框数据保存下来,与数据库做对比,如果正确,则登录成功,否则提示错误。
在这里插入图片描述
这里账户之所以不使用文本框而使用了下拉列表框,是为了显的更加真实。
这里有一个知识点,刚创建的下拉列表框是不具备输入的功能,在属性里面将editable打勾即可。

    //这两行代码是限制输入的,写在构造函数
    ui->comboBox->setValidator(new QRegExpValidator(QRegExp("[0-9]+$")));//只能输入数字
    ui->lineEdit_2->setEchoMode(QLineEdit::Password);//以黑点显示

    //遍历表user 连接数据库代码上面已说,这里不再列出,query已定义
    query.exec("select * from user"); 
    QStringList mysqlMonth; //用于保存遍历到的账号,这个类型类似于数组和集合
    while(query.next())
    {
        //将所有账户进行保存
        mysqlMonth<<query.value(0).toString();
    }
    //显示所有账户
    ui->comboBox->addItems(mysqlMonth); //将遍历的账号写入下拉列表框
    dbconn.close();//断开连接

4.2.3 记住密码实现

实现思路是登录的时候判断复选框是否被勾选,二次登陆时遍历数据库的tf字段,如果是"1"则显示密码。"0"则不显示密码,这样就起到了记住密码的功能。

//提取的账户和密码
 username = ui->comboBox->lineEdit()->text();
 userpassword = ui->lineEdit_2->text();
//查询数据库是否有匹配账户
QString S =QString("select * from user where username='%1' and userpassworld='%2' ").arg(username).arg(userpassword);
if(query.exec(S)&&query.next()) //网上很多教程只用了query.exec(S),这样是否匹配到都会返回真是无法达到预期效果的
{
    if(ui->checkBox->isChecked())  //判断复选框是否被勾选,如果勾选,数据库标标记记住密码
    {
        //更新数据库将字段为username的那一行,修改tf为"1"代表记住密码
        S = QString("update user set tf = '%1' where username = '%2'").arg("1").arg(username);
        query.exec(S);
    }
    else 
    {
        //否则 tf写入"0"代表不记住密码
         S = QString("update user set tf = '%1' where username = '%2'").arg("0").arg(username);
        query.exec(S);
    }
}

//启动软件加载账户以及密码
//在构造函数加入槽函数
QObject::connect(ui->comboBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(printfText()));//通过槽 将账号与密码进行关联
//该函数加载账户和密码
void MyDialog::printfText()
{
    //遍历账户
    query.exec("select * from user");
    //保存账号 假定最多5个账户
    QString account_qstr[5];
    //保存账户是否记录密码
    QString remember_qstr[5];
    //用于数组
    int i = 0;
    int j = 0;
    while(query.next())
    {        
            //保存遍历值
            account_qstr[i++] = query.value(1).toString();//第二个字段 userpassworld
            remember_qstr[j++] = query.value(2).toString();//第三个字段 tf
    }
    //ui->comboBox->currentIndex()==0代表下拉列表框第一个选项 再判断tf是否为"1" 如果条件成立,则显示密码
    if(ui->comboBox->currentIndex()==0 && remember_qstr[0]=="1")
    {
        ui->lineEdit_2->setText(account_qstr[0]);
    }
    if(ui->comboBox->currentIndex()==1 && remember_qstr[1]=="1")
    {
        ui->lineEdit_2->setText(account_qstr[1]);
    }
    if(ui->comboBox->currentIndex()==2 && remember_qstr[2]=="1")
    {
        ui->lineEdit_2->setText(account_qstr[2]);
    }
    if(ui->comboBox->currentIndex()==3 && remember_qstr[3]=="1")
    {
        ui->lineEdit_2->setText(account_qstr[3]);
    }
    if(ui->comboBox->currentIndex()==4 && remember_qstr[4]=="1")
    {
        ui->lineEdit_2->setText(account_qstr[4]);
    }
}

4.2.4 登录/注册/忘记密码按钮响应

思路就是创建槽函数,来发出信号,隐藏主界面,来显示次界面。

//就拿登录来说
//主窗口头文件
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include <QDialog>
namespace Ui {
class MyDialog;
}
class MyDialog : public QDialog
{
    Q_OBJECT
public:
    explicit MyDialog(QWidget *parent = 0);
    ~MyDialog();
signals:
    void showmainwindow();//显示主窗口信号
private:
    Ui::MyDialog *ui;
};
#endif // MYDIALOG_H

//然后在主窗口cpp写
void MyDialog::on_pushButton_clicked()//登录按钮被触发
{
        this->hide();//隐藏主界面
        emit showmainwindow();//发出显示登录对话框
}

//绑定槽函数显示登录对话框
//d是主界面对象 w是登录对话框对象,如果showmainwindow()发出信号,w的成员函数receivelogin()会被执行
QObject::connect(&d,SIGNAL(showmainwindow()),&w,SLOT(receivelogin()));

//然后在登录对话框cpp中编写receivelogin()函数
void MainWindow::receivelogin()
{
    this->show();//显示登录对话框
}

4.3 注册账户界面

4.3.1 发送验证码

void MyDialog_2::on_pushButton_clicked()  //发送验证码
{
    //邮箱地址
    QString mailaddress_qstr = ui->lineEdit_3->text(); //获取用户输入的邮箱地址
    std::string mailaddress_str = mailaddress_qstr.toStdString();
    const char * mailaddress_c = mailaddress_str.c_str(); 
    //这里因为抄下来的代码是char * 类型的,所以我们需要转换一下
    //随机生成验证码
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    verificationcode = qrand()%(99999 - 10000) + 10000;//产生一个5位数的随机数
    //将验证码加入字符串
    QString verificationcode_qstr = QString("验证码为%1,若非本人操作,请勿告诉他人。——————信息来自花狗科技").arg(verificationcode);
    std::string verificationcode_str = verificationcode_qstr.toStdString();
    const char * verificationcode_c = verificationcode_str.c_str();
    //登录邮箱
    Smtp smtp("2506897252@qq.com","XXXXXXXXX");  //第一个参数是发送者邮箱,第二个授权码,并不是邮箱密码
    //发送邮件
    smtp.send(mailaddress_c,"验证信息",verificationcode_c);

}

4.3.2 验证码验证

void MyDialog_2::on_pushButton_2_clicked() //注册(成功则写入数据库)
{
    QString register_qstr = ui->lineEdit_4->text();//获取用户输入的验证码
    //将字符串转为整形进行验证码判断
    int verificationcode_2 = register_qstr.toInt();
    if(verificationcode_2 == verificationcode)
    {
        QMessageBox::about(this,"About",QString::fromUtf8("注册成功"));
        QString username = ui->lineEdit->text();//获取账户
        QString userpassword = ui->lineEdit_2->text();//获取密码
        QString mail = ui->lineEdit_3->text();//获取注册邮箱,用作找回密码时的凭证
        QString str = QString("INSERT INTO user (username,userpassworld,tf,mail) VALUES ('%1','%2','%3','%4')").arg(username,userpassword,"0",mail);
        query.exec(str);
        dbconn.close();
    }
   else
    {
        QMessageBox::about(this,"About",QString::fromUtf8("验证码错误"));
    }
}

4.3.3显示倒计时

在这里插入图片描述
大家也应该知道发送验证码之后会有一个时间显示,为了逼真,我们也来做一个

#include"QTimer"
#include"QTime"
QTimer * timer;
int count = 60;          //倒计时
connect(timer,SIGNAL(timeout()),this,SLOT(myslot()));  //绑定槽函数

void MyDialog_2::on_pushButton_clicked()  //发送验证码
{
    //这行代码也是写在上一个函数中的,只不过我拆下来方便理解
    //间隔1秒触发myslot() 来显示剩余秒数
    timer->start(1000);
}

void MyDialog_2::myslot()  //显示倒计时
{

    QString count_qstr = QString("剩余%1秒").arg(count--);
    ui->label_5->setText(count_qstr);//将剩余秒数写在标签上
    if(count == -1)
    {
     //如果用户没有返回登陆,倒计时结束应停止计时
     timer->stop();
    }
}

4.4 忘记代码界面

忘记代码其实和注册代码差不多,只是这次的邮箱需要我们从数据库获取

oid MyDialog_3::on_pushButton_clicked()  //发送验证码
{
    QString username = ui->lineEdit->text();//获取账户
    QString S =QString("select * from user where username='%1' ").arg(username);
    if(query.exec(S)&&query.next())
    {
    //有该账户,则查询该账户注册时的邮箱地址
    QString mailaddress_qstr = query.value(3).toString();
    //这里的地址需要通过数据库记录的邮箱地址来确定
    std::string mailaddress_str = mailaddress_qstr.toStdString();
    const char * mailaddress_c = mailaddress_str.c_str();
    //随机生成验证码
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    verificationcode_2 = qrand()%(99999 - 10000) + 10000;
    //将验证码加入字符串
    QString verificationcode_qstr = QString("验证码为%1,若非本人操作,请勿告诉他人。——————信息来自花狗科技").arg(verificationcode_2);
    std::string verificationcode_str = verificationcode_qstr.toStdString();
    const char * verificationcode_c = verificationcode_str.c_str();
    //登录邮箱
    Smtp smtp("2506897252@qq.com","XXXXXXX");  //第一个参数是发送者邮箱,第二个授权码,并不是邮箱密码
    //发送邮件
    smtp.send(mailaddress_c,"验证信息",verificationcode_c);
    }
    else
    {
        QMessageBox::about(this,"About",QString::fromLocal8Bit("\n该账户未注册!"));
    }
}

好了,登录界面就这些啦,我们下期再见!

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
14天前
|
关系型数据库 MySQL Linux
Qt连接Mysql出现问题(一):“Driver not loaded Driver not loaded“
第一眼看见这张图我也觉得很奇怪,显示有QMYSQL但是又说没有,这不自相矛盾吗!
|
4天前
|
SQL 关系型数据库 MySQL
MySQL DDL(数据定义语言)深度解析
MySQL DDL(数据定义语言)深度解析
|
2天前
|
SQL 关系型数据库 MySQL
实时计算 Flink版操作报错合集之从mysql读数据写到hive报错,是什么原因
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
1天前
|
DataWorks 监控 关系型数据库
利用 DataWorks 数据推送定期推播 MySQL 或 StarRocks Query 诊断信息
DataWorks 近期上线了数据推送功能,能够将数据库查询的数据组织后推送到各渠道 (如钉钉、飞书、企业微信及 Teams),除了能将业务数据组织后推送,也能将数据库自身提供的监控数据组织后推送,这边我们就以 MySQL (也适用于StarRocks) 为例,定期推播 MySQL 的数据量变化等信息,帮助用户掌握 MySQL 状态。
23 1
|
3天前
|
关系型数据库 MySQL 数据库
实时计算 Flink版产品使用问题之如何排除某个列进行同步MySQL数据
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
8天前
|
关系型数据库 MySQL
MySQL 保姆级教程(五):数据过滤
MySQL 保姆级教程(五):数据过滤
|
8天前
|
关系型数据库 MySQL
MySQL 保姆级教程(四):过滤数据
MySQL 保姆级教程(四):过滤数据
|
8天前
|
关系型数据库 MySQL
MySQL 保姆级教程(三):排序检索数据
MySQL 保姆级教程(三):排序检索数据
|
8天前
|
关系型数据库 MySQL 数据库
MySQL 保姆级教程(二):使用 MySQL 检索数据
MySQL 保姆级教程(二):使用 MySQL 检索数据
|
2天前
|
DataWorks NoSQL 关系型数据库
DataWorks产品使用合集之如何从Tablestore同步数据到MySQL
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。

推荐镜像

更多