简介我们把注册界面再优化完善一下一、增加定时按钮点击获取验证码后需要让按钮显示倒计时然后倒计时结束后再次可点击。添加TimberBtn类#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; };实现#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信号和处理逻辑//设定输入框输入后清空字符串 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定义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声明中添加QMapTipErr, QString _tip_errs;tip_errs用来缓存各个输入框输入完成后提示的错误如果该输入框错误清除后就显示剩余的错误每次只显示一条实现添加错误和删除错误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); }实现错误检测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; }除此之外修改之前点击确认按钮的逻辑改为检测所有条件成立后再发送请求//确认按钮 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.#ifndef CLICKEDLABEL_H #define CLICKEDLABEL_H #includeglobal.h #include QLabel class ClickedLabel : public QLabel { Q_OBJECT public: ClickedLabel(QWidget *parentnullptr); 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中的六种状态就是基于这两种状态嵌套实现的。enum ClickLbState{ Normal 0, Selected 1 };六种状态用qss写好这样我们只需要根据鼠标事件切换不同的qss就可以实现样式变换。#pass_visible[stateunvisible]{ border-image: url(:/res/unvisible.png); } #pass_visible[stateunvisible_hover]{ border-image: url(:/res/unvisible_hover.png); } #pass_visible[statevisible]{ border-image: url(:/res/visible.png); } #pass_visible[statevisible_hover]{ border-image: url(:/res/visible_hover.png); } #confirm_visible[stateunvisible]{ border-image: url(:/res/unvisible.png); } #confirm_visible[stateunvisible_hover]{ border-image: url(:/res/unvisible_hover.png); } #confirm_visible[statevisible]{ border-image: url(:/res/visible.png); } #confirm_visible[statevisible_hover]{ border-image: url(:/res/visible_hover.png); }我们实现ClickedLabel功能#include clickedlabel.h #includeQMouseEvent ClickedLabel::ClickedLabel(QWidget *parent):QLabel (parent),_curstate(ClickLbState::Normal) { setCursor(Qt::PointingHandCursor); } void ClickedLabel::mousePressEvent(QMouseEvent *e) { if(e-button()Qt::LeftButton) { if(_curstateClickLbState::Normal) { qDebug()clicked , change to selected hover: _selected_hover; _curstateClickLbState::Selected; setProperty(state,_selected_hover); repolish(this); update(); }else { qDebug()clicked , change to normal hover: _normal_hover; _curstateClickLbState::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点击的响应函数//设置浮动显示手形状 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函数内实现收到服务器注册回复的请求//注册注册用户回包逻辑 _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(); });页面切换逻辑void RegisterDialog::ChangeTipPage() { _countdown_timer-stop(); ui-stackedWidget-setCurrentWidget(ui-page_2); //启动定时器 _countdown_timer-start(1000); }在RegisterDialog.ui中stackwidget的page2添加标签和返回按钮在RegisterDialog构造函数中添加定时器回调// 创建定时器 _countdown_timer new QTimer(this); // 连接信号和槽 connect(_countdown_timer, QTimer::timeout, [this](){ if(_countdown0){ _countdown_timer-stop(); emit sigSwitchLogin(); return; } _countdown--; auto str QString(注册成功%1 s后返回登录).arg(_countdown); ui-tip_lb-setText(str); });除此之外在返回按钮的槽函数中停止定时器并发送切换登录的信号void RegisterDialog::on_return_btn_clicked() { _countdown_timer-stop(); emit sigSwitchLogin(); }取消注册也发送切换登录信号void RegisterDialog::on_cancel_btn_clicked() { _countdown_timer-stop(); emit sigSwitchLogin(); }四、界面跳转回到mainwindow构造函数简化只做登录界面初始化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); }在点击注册按钮的槽函数中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(); }切换登录界面//从注册界面返回登录界面 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); }这样登录界面和注册界面的切换逻辑就写完了。