前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fdog系列(五):使用Qt模仿QQ实现登录界面到主界面,功能篇。

Fdog系列(五):使用Qt模仿QQ实现登录界面到主界面,功能篇。

作者头像
花狗Fdog
发布2021-05-06 16:45:27
3K0
发布2021-05-06 16:45:27
举报
文章被收录于专栏:花狗在Qt

一. 前言

Fdog系列已写目录:

Fdog系列(一):思来想去,不如写一个聊天软件,那就从仿QQ注册页面开始吧。

Fdog系列(二):html写完注册页面之后怎么办,用java写后台响应呀。

Fdog系列(三):使用腾讯云短信接口发送短信,数据库写入,部署到服务器,web收尾篇。

Fdog系列(四):使用Qt框架模仿QQ实现登录界面,界面篇。

Fdog系列(五):使用Qt模仿QQ实现登录界面到主界面,功能篇。 当前篇

读完该篇,你将学会:

如何保存登录数据 实现是否记住密码 如何获取本地数据 登录界面中出现的下拉框 从下拉列表框删除账号 改变选项,实时显示

其中下拉框的自定义比较复杂,我看到有网友在评论区问了如何实现,别急,它来了!看完本文,你将学会他。

同时完整项目代码已上传github:Fdog即时通讯软件 求星星!


二. 正文

1. 如何保存第一次登录数据

先来分析一下:

要保存的数据,1.头像 2.账号 3.密码 4.是否记住密码,并且第三项密码依赖于第四项用户是否勾选记住密码。

如何保存数据,给大家提供两种方法:1.使用了文本保存,2.使用ini配置文件保存。这两种方法都是可以的。为了方便,这里使用文本保存,如果想使用ini保存,可以参考这篇:QT学习笔记之读取INI文件

文件存放的位置,这里使用绝对路径是不明智的,应当使用相对地址,思路是获取可执行文件的目录,然后在其目录至上创建用户文件。

用户文件结构:创建FdogUserFile文件夹,再创建以用户账号为名的文件夹,这个文件夹包括图片文件和一个文本文件,图片文件就是头像,也是以用户账号命名,文本文件命名为data,里面存放账号和密码,这里有一个格式,第一行是账号,第二行是密码,当第二行为空时,说明用户没有选择记住密码。

应当注意的是这里不应该重复创建文件,应有一个判断,判断当前用户信息文件是否创建,若不存在则创建,若存在则更新即可。

差点忘了还有我们数据库的事,在这一切之前,应当将用户和密码和数据库数据对比,如果匹配,就从服务器下载头像和执行上面的内容。

下面来代码实现这一构想,现在假设照片已经存在服务器,例如当你输入网址:https://www.fdogcsdn.cn/img/10001.jpg

网页将显示一张图片。

至于如何让网页显示图片,会在下面写出,现在只需要考虑如何用代码实现下载图片和与数据库内容做做对比。

创建一个数据库类:usersql

代码语言:javascript
复制
#include<QNetworkAccessManager>
#include<QNetworkReply>
#include<QPixmap>
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
//上面三行防止中文乱码
class Usersql
{
private:
	QSqlDatabase dbconn;//连接数据库
     QSqlQuery query;   //查询操作
     QString account;   //账号
     QString passwd;    //密码
     QString iconurl;   //地址
     QPixmap icon;      //头像
public:
     void conndata(); //连接数据库
     bool queryuser(QString user,QString password); //查询是否有该账户
     QPixmap geticonurl(QString url);//根据地址网上下载图片
     QPixmap getPixmapIcon(QString url);//url为账号,通过账号获取头像地址
};
代码语言:javascript
复制
void Usersql::conndata()
{
    if(QSqlDatabase::contains(QSqlDatabase::defaultConnection))
    {
        this->dbconn = QSqlDatabase::database(QSqlDatabase::defaultConnection);
    }
    else
    {
        this->dbconn = QSqlDatabase::addDatabase("QMYSQL");
    }
   this-> dbconn.setHostName("0.0.0.0");//主机名字 也是服务器ip 如果使用本地数据库测试,则使用127.0.0.1
   this-> dbconn.setDatabaseName("fdogsql");//数据库名字
    if(this->dbconn.open("root", "111111")) //用户名 密码
    {
    //如果判断为真,则连接成功
        //qDebug()<<"success";
    }
    this->query = (QSqlQuery)this->dbconn; //进行绑定 此后可以使用query对象对数据库进行操作。
}

bool Usersql::queryuser(QString user, QString password)
{
    this->query.exec("select * from user");
        while(query.next())
        {
            //遍历账户 value中的值代表字段,0就是第一个,1就是第二个
            qDebug()<<this->query.value(0).toString();
            if(user==(this->query.value(0).toString()))
            {
                if(password==(this->query.value(3).toString()))
                {
                   this->iconurl = this->query.value(5).toString();
                   qDebug()<<"该账户存在";
                   return true;
                }
                else
                {
                   qDebug()<<"该账户不存在";
                   return false;
                }
            }
        }
        return false;
}
QPixmap Usersql::geticonurl(QString url1)
{
    this->iconurl = url1;
    QUrl url(this->iconurl);
    qDebug()<<url;
        QNetworkAccessManager manager;
        QEventLoop loop;
        // qDebug() << "Reading picture form " << url;
        QNetworkReply *reply = manager.get(QNetworkRequest(url));
        //请求结束并下载完成后,退出子事件循环
        QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
        //开启子事件循环
        loop.exec();
        QByteArray jpegData = reply->readAll();
        this->icon.loadFromData(jpegData);
        return icon;
}

QPixmap Usersql::getPixmapIcon(QString url)
{
    QString strurl;
    //数据库查询该帐户
    this->query.exec("select * from user");
        while(query.next())
        {
            //遍历账户
            if(url==(this->query.value(0).toString()))
            {
                strurl = this->query.value(5).toString();
            }
        }
        qDebug()<<strurl;
    QPixmap a = geticonurl(strurl);
    a=this->icon.scaled(QSize(a.width(), a.height()), Qt::IgnoreAspectRatio);
    a=PixmapToRound(a, a.width()/2);
    return a;
}

然后来看下当点击登录按钮执行的代码

代码语言:javascript
复制
void Login::on_pushButton_clicked()
{	//ui->lineEdit_2是账号文本框 ui->lineEdit是密码文本框
	sqconn.conndata();//连接数据库
    bool isuser = sqconn.queryuser(ui->lineEdit_2->text(),ui->lineEdit->text());//输入内容查询
    //判断用户是否存在
    if(isuser)
    {
        //只获取账号
        QString account = ui->lineEdit_2->text(); //账户
        this->sqconn.queryUserInfo(account);//根据账户获取昵称,密码(如果用户选择记住密码,则保存密码),头像
        QString name = sqconn.getName();
        QString passwd="";
        if(ui->checkBox_2->isChecked())//判断用户是否保存密码
        {
            passwd = sqconn.getPasswd();
        }
        QPixmap icon = sqconn.getIcon();
        //获取程序当前运行目录
        QString fileName = QCoreApplication::applicationDirPath();
        //用户目录
        QString add = "//..//FdogUserFile";
        //创建用户文件夹
        fileName = fileName + add +QString("//%1").arg(account);
        //信息保存
        QDir * file = new QDir;
        //文件夹是否存在,若存在则表示信息已经存在,只需要更新内容即可。
        bool exist_1 = file->exists(fileName);
        if(exist_1)
        {
            //qDebug()<<"创建";
            QFile file(fileName +"//data.txt");
            qDebug()<<fileName +"//data.txt";
            if(file.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate))
            {
                //qDebug()<<"txt文件创建成功";
            }
            QTextStream stream(&file);
            //写入
            if(passwd=="")stream<<name;
            else stream<<name<<"\n"<<passwd;
            //qDebug()<<"tup:"<<account;
            icon.save(fileName+QString("//%1.jpg").arg(account),"JPG");
            file.close();
        }
        else
        {   //如果不存在则创建
            bool ok = file->mkpath(fileName);
            if(ok)
            {
                //qDebug()<<"创建";
                QFile file(fileName +"//data.txt");
                qDebug()<<fileName +"//data.txt";
                if(file.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate))
                {
                    //qDebug()<<"txt文件创建成功";
                }
                QTextStream stream(&file);
                if(passwd=="")stream<<name;
                else stream<<name<<"\n"<<passwd;
                icon.save(fileName+QString("//%1.jpg").arg(account),"JPG");
                file.close();
            }
            else
            {
                qDebug()<<"未创建成功";
            }
        }
        this->hide();//隐藏登录窗口
        systemtrayicon->hide();//隐藏系统托盘
        //初始化主界面 w是在头文件定义的主界面类,这里只是作为演示。
        w = new MainWindow(account);
        //显示主界面
        w->show();
        //显示系统托盘图标
        w->showicon();
    }
   else
    {
		//该用户不存在
    }
}

当登录账号后,创建效果:

当然使用明文保存密码是不安全的,可以参考之前我写的一篇加密:非对称性加密算法——RSA算法原理及C++实现

然后来说如何让服务器正确显示照片,在服务器根目录创建一个fdogpoject/fdog/img的路径,在img文件夹下存放我们的照片,当然这样,我们是无法通过https://www.fdogcsdn.cn/img/10001.jpg来显示图片的,还需要改一点东西。

我是使用tomcat搭建的服务,所以在conf文件夹下修改server.xml文件,添加:

代码语言:javascript
复制
<Context reloadable="true" docBase="/fdogpoject/fdog/img/" path="/img"/>

然后重启tomcat就可以访问了。


2. 如何获取已经登录过的账号信息,并完成自定义下列框

这一步相对于如何保存信息已经简单了许多, 只需要将头像和文件内容显示在下拉列表框即可。

登录界面类:

代码语言:javascript
复制
class Login : public QWidget
{
private:
	QVector<int> infoListsign;
	QSignalMapper * myMapper;
	QStringList infoList;               //用户账号
    QStringList infopasswd;             //用户密码
    QStringList icon;                   //头像保存地址
public:	
	explicit Login(QWidget *parent = 0);//构造函数
	QStringList GetDirNameList(const QString &strDirpath);//用于获取用户文件下所有账号(实际上是获取目录下所有文件名)
};
代码语言:javascript
复制
Login::Login(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Login)
{
	ui->setupUi(this);
	//获取exe运行目录
    QString fileName1 = QCoreApplication::applicationDirPath()+"//..//FdogUserFile";
    //获取目录
    infoList = GetDirNameList(fileName1);
    //加载自定义的下拉列表框
    m_AccountList = new QListWidget(this);
    m_AccountList->setFocusPolicy(Qt::NoFocus);
    ui->comboBox->setModel(m_AccountList->model());
    ui->comboBox->setView(m_AccountList);
    for(int i =0;i<infoList.size();i++)
    {
        //获取帐号
        QString local_account = infoList.at(i);
        //获取昵称
        QString local_name;
        //获取密码
        QString local_passwd;
        //获取头像
        QString ic = fileName1+QString("//%1//%2.jpg").arg(infoList.at(i)).arg(infoList.at(i));
        this->icon.append(ic);
        QIcon local_icon(ic);
        QFile file_my(fileName1+QString("//%1//data.txt").arg(infoList.at(i)));
        if(!file_my.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            qDebug()<<"文件打开失败"<<endl;
        }
        this->ispasswd = false;
        while(!file_my.atEnd())
        {
            infopasswd.append("");
            QByteArray line = file_my.readLine();
            QString str(QString::fromLocal8Bit(line.data()));
            //qDebug()<< str;
            if(this->ispasswd)
            {
                //还应该查看是否有密码存在,如果有则读取,并显示勾中
                infopasswd.insert(i,str);
                //qDebug()<<"有密码"<<str;
                local_passwd.append(str);
                this->ispasswd =false;
            }
            else
            {
                local_name.append(str);
                this->ispasswd =true;
            }
            qDebug()<<infopasswd;
        }
        QHBoxLayout *horLayout = new QHBoxLayout();//水平布局
        QLabel * la = new QLabel();
        QString s = ic;
        la->setStyleSheet(QString("border-image: url(%1);border-radius:17px;").arg(s));
        la->setFixedSize(34,34);
        QLabel * la2 = new QLabel(QString("%1\n%2").arg(local_name.left(local_name.length()-1),local_account));
        QPushButton * b1 = new QPushButton();
        b1->setFixedSize(32,32);
        b1->setStyleSheet("QPushButton{background:rgba(200,200,200,0);border-style:solid;border-image: url(:/lib/delete.png);}"
                          "QPushButton:hover{background:rgba(200,200,200,0);border-style:solid;border-image: url(:/lib/delete2.png);}");
        horLayout->addWidget(la);
        horLayout->addWidget(la2);
        horLayout->addWidget(b1);
        QWidget *widget =new QWidget(this);
        widget->setLayout(horLayout);
        QListWidgetItem * Listitem = new QListWidgetItem(m_AccountList);
        m_AccountList->setItemWidget(Listitem,widget);
    }
}

QStringList Login::GetDirNameList(const QString &strDirpath)
{
    QDir dir(strDirpath);
    QFileInfoList Info_list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    QListIterator<QFileInfo> Iterator(Info_list);
    QStringList strDirNameList;
    QFileInfo Info;
    while (Iterator.hasNext())
    {
        Info = Iterator.next();
        if (Info.isDir())
        {
            strDirNameList << QDir(Info.absoluteFilePath()).dirName();
        }
    }
    return strDirNameList;
}

自定义下拉列表框的重点是这些:

代码语言:javascript
复制
	QListWidget * m_AccountList;        //自定义下拉列表框
    //加载自定义的下拉列表框
    m_AccountList = new QListWidget(this);
    m_AccountList->setFocusPolicy(Qt::NoFocus);
    ui->comboBox->setModel(m_AccountList->model());
    ui->comboBox->setView(m_AccountList);
	//每有一个账号就执行一次下面的代码
	QHBoxLayout *horLayout = new QHBoxLayout();//水平布局
    QLabel * la = new QLabel();
    QString s = ic;
    la->setStyleSheet(QString("border-image: url(%1);border-radius:17px;").arg(s));
    la->setFixedSize(34,34);
    QLabel * la2 = new QLabel(QString("%1\n%2").arg(local_name.left(local_name.length()-1),local_account));
    QPushButton * b1 = new QPushButton();
    b1->setIcon(QIcon(":/lib/delete.png"));
    b1->setFixedSize(32,32);
    b1->setStyleSheet("background:rgba(200,200,200,0);border-style:solid;");
    horLayout->addWidget(la); //前面头像
    horLayout->addWidget(la2);//中间名字
    horLayout->addWidget(b1);//后面删除按钮
    QWidget *widget =new QWidget(this);
    widget->setLayout(horLayout);
    QListWidgetItem * Listitem = new QListWidgetItem(m_AccountList);
    m_AccountList->setItemWidget(Listitem,widget);

3. 从下拉列表框删除账号

为每个删除按钮绑定信号槽

代码语言:javascript
复制
void Login::deleteaccount(int i) //传进来的是标记数字
{
    //QMessageBox::information(NULL, "提示", "是否要删除本次该账号信息",QMessageBox::Yes|QMessageBox::No);
    QMessageBox *message = new QMessageBox(QMessageBox::Question,tr("提示"), tr("是否要删除该账号信息"), QMessageBox::Yes | QMessageBox::No,this);
    int result = message->exec();
    //infoListsign  0 1 2 3  0 1 2 3  0 1 2  1 2 3
    //这里有一个需要注意的点,当初给每个按钮标记的数字是不会变的,但是当我们删除列表中的一个item之后,列表中的其他item会自动排序
    //所以无法单靠当初标记的数字来进行简单删除,我们可以借助一个vector,将标记的数字存放,而正在要删除的值是标记数字的下标
    //如果单靠标记的数字来判断,比如我要删除第一行,就是0,这时它的下标也是0,这没有问题,但是第二次就会出问题,我要删除第二行,当我点击删除
    //其实传回的值应该是1,但是列表自动排序之后,原来的1变成了0,原来的2变了1.
    switch(result)
    {
    case QMessageBox::Yes:
        infoList.removeAt(infoListsign.indexOf(i));
        infopasswd.removeAt(infoListsign.indexOf(i));
        icon.removeAt(infoListsign.indexOf(i));

        //当前索引是否为删除对象,若是,则使用第一个
        if(infoListsign.indexOf(i)==ui->comboBox->currentIndex())
        {

            if(infoList.length()!=0)
            {
                ui->comboBox->setCurrentIndex(0);
            }
        }
        if(infoList.length()==0)
        {
            ui->lineEdit_2->setText("");
            ui->lineEdit->setText("");
            ui->label_4->setStyleSheet("border-image: url(:/lib/fdogicon.png);border-width:0px;border-style:solid;border-color: rgb(255, 255, 255);border-radius:33px;");
            this->m_AccountList->setItemHidden(this->m_AccountList->item(0),true);
            return;
        }
        //隐藏账号信息  this->m_AccountList->setItemHidden(this->m_AccountList->item(i),true);
        //删除账号信息
        QListWidgetItem * item;
        qDebug()<<"出错0";
        qDebug()<<"infoListsign.indexOf(i)"<<infoListsign.indexOf(i);
        item = this->m_AccountList->takeItem(infoListsign.indexOf(i));
        qDebug()<<"出错1";
        this->m_AccountList->removeItemWidget(item);
        qDebug()<<"出错2";
        delete item;
        infoListsign.erase(infoListsign.begin()+infoListsign.indexOf(i));
        break;
    case QMessageBox::No:
        //什么也不做
    break;
    }
}

4. 文本框显示正确内容

在上一篇说过,图中的账号文本框其实是由文本框加下拉列表框构成,如何在改变下列列表框的同时修改登录界面所显示的内容呢?

右击combox,转到槽,添加currentIndexChanged,代码如下

代码语言:javascript
复制
void Login::on_comboBox_currentIndexChanged(int index)
{
    ui->checkBox_2->setChecked(false);
    ui->lineEdit->setText("");
    ui->lineEdit_2->setText(infoList.at(index));
    if(infopasswd.at(index)!="")
    {
        ui->lineEdit->setText(infopasswd.at(index));
        ui->checkBox_2->setChecked(true);
    }
    QString icon1 = icon.at(index);
    ui->label_4->setStyleSheet(QString("border-image: url(%1);border-width:0px;border-style:solid;border-color: rgb(255, 255, 255);border-radius:33px;").arg(icon1));
    //开始考虑用户是否选择记住密码
}

如果有不了解QSignalMapper类用法的可以参考这篇:QT多个按钮信号绑定一个槽函数,执行不同业务逻辑。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/05/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 前言
  • 同时完整项目代码已上传github:Fdog即时通讯软件 求星星!
  • 二. 正文
    • 1. 如何保存第一次登录数据
      • 2. 如何获取已经登录过的账号信息,并完成自定义下列框
        • 3. 从下拉列表框删除账号
          • 4. 文本框显示正确内容
          相关产品与服务
          访问管理
          访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档