QT聊天项目(11)

简介

我们把注册界面再优化完善一下

一、增加定时按钮

点击获取验证码后需要让按钮显示倒计时,然后倒计时结束后再次可点击。

添加TimberBtn类

cpp 复制代码
#ifndef TIMERBTN_H
#define TIMERBTN_H
#include <QPushButton>
#include <QTimer>
class TimerBtn : public QPushButton
{
public:
    TimerBtn(QWidget *parent = nullptr);
    ~ TimerBtn();
    // 重写mouseReleaseEvent
    virtual void mouseReleaseEvent(QMouseEvent *e) override;
private:
    QTimer  *_timer;
    int _counter;
};

实现

cpp 复制代码
#include "timerbtn.h"
#include <QMouseEvent>
#include <QDebug>
TimerBtn::TimerBtn(QWidget *parent):QPushButton(parent),_counter(10)
{
    _timer = new QTimer(this);
    connect(_timer, &QTimer::timeout, [this](){
        _counter--;
        if(_counter <= 0){
            _timer->stop();
            _counter = 10;
            this->setText("获取");
            this->setEnabled(true);
            return;
        }
        this->setText(QString::number(_counter));
    });
}
TimerBtn::~TimerBtn()
{
    _timer->stop();
}
void TimerBtn::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton) {
        // 在这里处理鼠标左键释放事件
        qDebug() << "MyButton was released!";
        this->setEnabled(false);
         this->setText(QString::number(_counter));
        _timer->start(1000);
        emit clicked();
    }
    // 调用基类的mouseReleaseEvent以确保正常的事件处理(如点击效果)
    QPushButton::mouseReleaseEvent(e);
}

然后将注册界面获取按钮升级为TimerBtn

二、调整输入框错误提示

在RegisterDialog构造函数中删除原来的输入框editing信号和逻辑,添加editingFinished信号和处理逻辑

cpp 复制代码
//设定输入框输入后清空字符串
ui->err_tip->clear();
connect(ui->user_edit,&QLineEdit::editingFinished,this,[this](){
    checkUserValid();
});
connect(ui->email_edit, &QLineEdit::editingFinished, this, [this](){
    checkEmailValid();
});
connect(ui->pass_edit, &QLineEdit::editingFinished, this, [this](){
    checkPassValid();
});
connect(ui->confirm_edit, &QLineEdit::editingFinished, this, [this](){
    checkConfirmValid();
});
connect(ui->varify_edit, &QLineEdit::editingFinished, this, [this](){
        checkVarifyValid();
});

global.h中添加TipErr定义

cpp 复制代码
enum TipErr{
    TIP_SUCCESS = 0,
    TIP_EMAIL_ERR = 1,
    TIP_PWD_ERR = 2,
    TIP_CONFIRM_ERR = 3,
    TIP_PWD_CONFIRM = 4,
    TIP_VARIFY_ERR = 5,
    TIP_USER_ERR = 6
};

RegisterDialog声明中添加

cpp 复制代码
QMap<TipErr, QString> _tip_errs;

tip_errs用来缓存各个输入框输入完成后提示的错误,如果该输入框错误清除后就显示剩余的错误,每次只显示一条

实现添加错误和删除错误

cpp 复制代码
void ResetDialog::AddTipErr(TipErr te, QString tips)
{
    _tip_errs[te] = tips;
    showTip(tips, false);
}
void ResetDialog::DelTipErr(TipErr te)
{
    _tip_errs.remove(te);
    if(_tip_errs.empty()){
      ui->err_tip->clear();
      return;
    }
    showTip(_tip_errs.first(), false);
}

实现错误检测

cpp 复制代码
bool RegisterDialog::checkUserValid()
{
    if(ui->user_edit->text()=="")
    {
        AddTipErr(TipErr::TIP_USER_ERR,tr("用户名不能为空"));
        return false;
    }
    DelTipErr(TipErr::TIP_USER_ERR);
    return true;
}

bool RegisterDialog::checkEmailValid()
{
    //验证邮箱的地址正则表达式
    auto email = ui->email_edit->text();
    // 邮箱地址的正则表达式
    QRegularExpression regex(R"((\w+)(\.|_)?(\w*)@(\w+)(\.(\w+))+)");
    bool match = regex.match(email).hasMatch(); // 执行正则表达式匹配
    if(!match){
        //提示邮箱不正确
        AddTipErr(TipErr::TIP_EMAIL_ERR, tr("邮箱地址不正确"));
        return false;
    }

    DelTipErr(TipErr::TIP_EMAIL_ERR);
    return true;
}

bool RegisterDialog::checkPassValid()
{
    auto pass = ui->pass_edit->text();
    auto confirm = ui->confirm_edit->text();

    if(pass.length() < 6 || pass.length()>15){
        //提示长度不准确
        AddTipErr(TipErr::TIP_PWD_ERR, tr("密码长度应为6~15"));
        return false;
    }

    // 创建一个正则表达式对象,按照上述密码要求
    // 这个正则表达式解释:
    // ^[a-zA-Z0-9!@#$%^&*]{6,15}$ 密码长度至少6,可以是字母、数字和特定的特殊字符
    QRegularExpression regExp("^[a-zA-Z0-9!@#$%^&*.]{6,15}$");
    bool match = regExp.match(pass).hasMatch();
    if(!match){
        //提示字符非法
        AddTipErr(TipErr::TIP_PWD_ERR, tr("不能包含非法字符"));
        return false;;
    }

    DelTipErr(TipErr::TIP_PWD_ERR);

    if(pass != confirm){
        //提示密码不匹配
        AddTipErr(TipErr::TIP_PWD_CONFIRM, tr("密码和确认密码不匹配"));
        return false;
    }else{
        DelTipErr(TipErr::TIP_PWD_CONFIRM);
    }
    return true;
}

bool RegisterDialog::checkVarifyValid()
{
    auto pass = ui->varify_edit->text();
    if(pass.isEmpty()){
        AddTipErr(TipErr::TIP_VARIFY_ERR, tr("验证码不能为空"));
        return false;
    }

    DelTipErr(TipErr::TIP_VARIFY_ERR);
    return true;
}

bool RegisterDialog::checkConfirmValid()
{
    auto pass = ui->pass_edit->text();
    auto confirm = ui->confirm_edit->text();

    if(confirm.length() < 6 || confirm.length() > 15 ){
        //提示长度不准确
        AddTipErr(TipErr::TIP_CONFIRM_ERR, tr("密码长度应为6~15"));
        return false;
    }

    // 创建一个正则表达式对象,按照上述密码要求
    // 这个正则表达式解释:
    // ^[a-zA-Z0-9!@#$%^&*]{6,15}$ 密码长度至少6,可以是字母、数字和特定的特殊字符
    QRegularExpression regExp("^[a-zA-Z0-9!@#$%^&*.]{6,15}$");
    bool match = regExp.match(confirm).hasMatch();
    if(!match){
        //提示字符非法
        AddTipErr(TipErr::TIP_CONFIRM_ERR, tr("不能包含非法字符"));
        return false;
    }

    DelTipErr(TipErr::TIP_CONFIRM_ERR);

    if(pass != confirm){
        //提示密码不匹配
        AddTipErr(TipErr::TIP_PWD_CONFIRM, tr("确认密码和密码不匹配"));
        return false;
    }else{
        DelTipErr(TipErr::TIP_PWD_CONFIRM);
    }
    return true;
}

除此之外修改之前点击确认按钮的逻辑,改为检测所有条件成立后再发送请求

cpp 复制代码
//确认按钮
void RegisterDialog::on_sureBtn_clicked()
{
    bool valid = checkUserValid();
    if(!valid){
        return;
    }

    valid = checkEmailValid();
    if(!valid){
        return;
    }

    valid = checkPassValid();
    if(!valid){
        return;
    }

    valid = checkConfirmValid();
    if(!valid){
        return;
    }

    valid = checkVarifyValid();
    if(!valid){
        return;
    }


    //发送http请求注册用户
    QJsonObject json_obj;
    json_obj["user"] = ui->user_edit->text();
    json_obj["email"] = ui->email_edit->text();
    json_obj["passwd"] = ui->pass_edit->text();
    json_obj["confirm"] = xorString(ui->confirm_edit->text());
    json_obj["varifycode"] = xorString(ui->varify_edit->text());
    HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix+"/user_register"),
                                        json_obj, ReqId::ID_REG_USER,Modules::REGISTERMOD);
}

三、隐藏和显示密码

我们在输入密码时希望能通过点击可见还是不可见,显示密码和隐藏密码,这里先添加图片放入资源中,然后在Register.ui中添加两个label,分别命名为pass_visible和confirm_visible, 用来占据位置。

因为我们要做的点击后图片要有状态切换,以及浮动显示不一样的效果等,所以我们重写ClickedLabel,继承自QLabel.

cpp 复制代码
#ifndef CLICKEDLABEL_H
#define CLICKEDLABEL_H
#include<global.h>
#include <QLabel>

class ClickedLabel : public QLabel
{
     Q_OBJECT
public:
    ClickedLabel(QWidget *parent=nullptr);
    virtual void mousePressEvent(QMouseEvent *e) override;
    virtual void enterEvent(QEnterEvent *event)override;
    virtual void leaveEvent(QEvent *event)override;
    void SetState(QString normal="",QString hover="",QString press="",
                  QString select="",QString select_hover="",QString select_press="");

    ClickLbState GetCurState();

private:
    QString _normal;
    QString _normal_hover;
    QString _normal_press;

    QString _selected;
    QString _selected_hover;
    QString _selected_press;

    ClickLbState _curstate;

signals:
    void clicked(void);
};

#endif // CLICKEDLABEL_H

一个Label有六种状态,普通状态,普通的悬浮状态,普通的点击状态,选中状态,选中的悬浮状态,选中的点击状态。

当Label处于普通状态,被点击后,切换为选中状态,再次点击又切换为普通状态。

ClickLbState定义在global.h中,包含两种状态一个是普通状态,一个是选中状态。而Label中的六种状态就是基于这两种状态嵌套实现的。

cpp 复制代码
enum ClickLbState{
    Normal = 0,
    Selected = 1
};

六种状态用qss写好,这样我们只需要根据鼠标事件切换不同的qss就可以实现样式变换。

cpp 复制代码
#pass_visible[state='unvisible']{
   border-image: url(:/res/unvisible.png);
}
#pass_visible[state='unvisible_hover']{
   border-image: url(:/res/unvisible_hover.png);
}
#pass_visible[state='visible']{
   border-image: url(:/res/visible.png);
}
#pass_visible[state='visible_hover']{
   border-image: url(:/res/visible_hover.png);
}
#confirm_visible[state='unvisible']{
   border-image: url(:/res/unvisible.png);
}
#confirm_visible[state='unvisible_hover']{
   border-image: url(:/res/unvisible_hover.png);
}
#confirm_visible[state='visible']{
   border-image: url(:/res/visible.png);
}
#confirm_visible[state='visible_hover']{
   border-image: url(:/res/visible_hover.png);
}

我们实现ClickedLabel功能

cpp 复制代码
#include "clickedlabel.h"
#include<QMouseEvent>


ClickedLabel::ClickedLabel(QWidget *parent):QLabel (parent),_curstate(ClickLbState::Normal)
{
    setCursor(Qt::PointingHandCursor);
}

void ClickedLabel::mousePressEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        if(_curstate==ClickLbState::Normal)
        {
             qDebug()<<"clicked , change to selected hover: "<< _selected_hover;
            _curstate=ClickLbState::Selected;
            setProperty("state",_selected_hover);
            repolish(this);
            update();
        }else
        {
            qDebug()<<"clicked , change to normal hover: "<< _normal_hover;
            _curstate=ClickLbState::Normal;
            setProperty("state",_normal_hover);
            repolish(this);
            update();
        }
        emit clicked();
    }

    // 调用基类的mousePressEvent以保证正常的事件处理
    QLabel::mousePressEvent(e);
}
// 处理鼠标悬停进入事件
void ClickedLabel::enterEvent(QEnterEvent* event) {
    // 在这里处理鼠标悬停进入的逻辑
    if(_curstate == ClickLbState::Normal){
        qDebug()<<"enter , change to normal hover: "<< _normal_hover;
        setProperty("state",_normal_hover);
        repolish(this);
        update();
    }else{
        qDebug()<<"enter , change to selected hover: "<< _selected_hover;
        setProperty("state",_selected_hover);
        repolish(this);
        update();
    }
    QLabel::enterEvent(event);
}
// 处理鼠标悬停离开事件
void ClickedLabel::leaveEvent(QEvent* event){
    // 在这里处理鼠标悬停离开的逻辑
    if(_curstate == ClickLbState::Normal){
        qDebug()<<"leave , change to normal : "<< _normal;
        setProperty("state",_normal);
        repolish(this);
        update();
    }else{
        qDebug()<<"leave , change to normal hover: "<< _selected;
        setProperty("state",_selected);
        repolish(this);
        update();
    }
    QLabel::leaveEvent(event);
}
void ClickedLabel::SetState(QString normal, QString hover, QString press,
                            QString select, QString select_hover, QString select_press)
{
    _normal = normal;
    _normal_hover = hover;
    _normal_press = press;
    _selected = select;
    _selected_hover = select_hover;
    _selected_press = select_press;
    setProperty("state",normal);
    repolish(this);
}

ClickLbState ClickedLabel::GetCurState()
{
    return _curstate;
}

将label升级为ClickedLabel,然后在RegisterDialog的构造函数中添加label点击的响应函数

cpp 复制代码
//设置浮动显示手形状
ui->pass_visible->setCursor(Qt::PointingHandCursor);
ui->confirm_visible->setCursor(Qt::PointingHandCursor);
ui->pass_visible->SetState("unvisible","unvisible_hover","","visible",
                            "visible_hover","");
ui->confirm_visible->SetState("unvisible","unvisible_hover","","visible",
                                "visible_hover","");
//连接点击事件
connect(ui->pass_visible, &ClickedLabel::clicked, this, [this]() {
    auto state = ui->pass_visible->GetCurState();
    if(state == ClickLbState::Normal){
        ui->pass_edit->setEchoMode(QLineEdit::Password);
    }else{
            ui->pass_edit->setEchoMode(QLineEdit::Normal);
    }
    qDebug() << "Label was clicked!";
});
connect(ui->confirm_visible, &ClickedLabel::clicked, this, [this]() {
    auto state = ui->confirm_visible->GetCurState();
    if(state == ClickLbState::Normal){
        ui->confirm_edit->setEchoMode(QLineEdit::Password);
    }else{
            ui->confirm_edit->setEchoMode(QLineEdit::Normal);
    }
    qDebug() << "Label was clicked!";
});

四、注册成功提示页面

注册成功后要切换到提示页面,所以在initHandlers函数内实现收到服务器注册回复的请求

cpp 复制代码
//注册注册用户回包逻辑
_handlers.insert(ReqId::ID_REG_USER, [this](QJsonObject jsonObj){
    int error = jsonObj["error"].toInt();
    if(error != ErrorCodes::SUCCESS){
        showTip(tr("参数错误"),false);
        return;
    }
    auto email = jsonObj["email"].toString();
    showTip(tr("用户注册成功"), true);
    qDebug()<< "email is " << email ;
    qDebug()<< "user uuid is " <<  jsonObj["uuid"].toString();
    ChangeTipPage();
});

页面切换逻辑

cpp 复制代码
void RegisterDialog::ChangeTipPage()
{
    _countdown_timer->stop();
    ui->stackedWidget->setCurrentWidget(ui->page_2);
    //启动定时器
    _countdown_timer->start(1000);
}

在RegisterDialog.ui中stackwidget的page2添加标签和返回按钮

在RegisterDialog构造函数中添加定时器回调

cpp 复制代码
// 创建定时器
_countdown_timer = new QTimer(this);
// 连接信号和槽
connect(_countdown_timer, &QTimer::timeout, [this](){
    if(_countdown==0){
        _countdown_timer->stop();
        emit sigSwitchLogin();
        return;
    }
    _countdown--;
    auto str = QString("注册成功,%1 s后返回登录").arg(_countdown);
    ui->tip_lb->setText(str);
});

除此之外在返回按钮的槽函数中停止定时器并发送切换登录的信号

cpp 复制代码
void RegisterDialog::on_return_btn_clicked()
{
    _countdown_timer->stop();
    emit sigSwitchLogin();
}

取消注册也发送切换登录信号

cpp 复制代码
void RegisterDialog::on_cancel_btn_clicked()
{
    _countdown_timer->stop();
    emit sigSwitchLogin();
}

四、界面跳转

回到mainwindow,构造函数简化,只做登录界面初始化

cpp 复制代码
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建一个CentralWidget, 并将其设置为MainWindow的中心部件
    _login_dlg = new LoginDialog(this);
    _login_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
    setCentralWidget(_login_dlg);
    //连接登录界面注册信号
    connect(_login_dlg, &LoginDialog::switchRegister, this, &MainWindow::SlotSwitchReg);
    //连接登录界面忘记密码信号
    connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);
}

在点击注册按钮的槽函数中

cpp 复制代码
void MainWindow::SlotSwitchReg()
{
    _reg_dlg = new RegisterDialog(this);
    _reg_dlg->hide();
    _reg_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
     //连接注册界面返回登录信号
    connect(_reg_dlg, &RegisterDialog::sigSwitchLogin, this, &MainWindow::SlotSwitchLogin);
    setCentralWidget(_reg_dlg);
    _login_dlg->hide();
    _reg_dlg->show();
}

切换登录界面

cpp 复制代码
//从注册界面返回登录界面
void MainWindow::SlotSwitchLogin()
{
    //创建一个CentralWidget, 并将其设置为MainWindow的中心部件
    _login_dlg = new LoginDialog(this);
    _login_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
    setCentralWidget(_login_dlg);
   _reg_dlg->hide();
    _login_dlg->show();
    //连接登录界面注册信号
    connect(_login_dlg, &LoginDialog::switchRegister, this, &MainWindow::SlotSwitchReg);
    //连接登录界面忘记密码信号
    connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);
}

这样登录界面和注册界面的切换逻辑就写完了。

相关推荐
九尾狐ai2 小时前
从青鸟文化案例看校园文化建设的技术架构与实现方案
开发语言·python
先知后行。2 小时前
qt八股文
开发语言·qt
Nuopiane2 小时前
MyPal3(7)
java·开发语言
被AI抢饭碗的人2 小时前
高并发内存池实现
开发语言·c++
不光头强2 小时前
object所有方法及知识点
java·开发语言·jvm
.小小陈.2 小时前
C++进阶7:深入理解哈希表,从原理到 C++ 实践
开发语言·c++·学习·哈希算法
码云数智-大飞2 小时前
排序算法的终极博弈:从复杂度推导到工程选型实战
开发语言
森G2 小时前
9、交叉编译xvidcore----------Opencv移植Arm
qt
南 阳2 小时前
Python从入门到精通day48
开发语言·python