QT聊天项目(11)
简介我们把注册界面再优化完善一下一、增加定时按钮点击获取验证码后需要让按钮显示倒计时然后倒计时结束后再次可点击。添加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); }这样登录界面和注册界面的切换逻辑就写完了。

相关新闻

【SpringBoot】Spring Boot 项目的打包配置

【SpringBoot】Spring Boot 项目的打包配置

在 Spring Boot 项目中,打包是一个非常关键的过程,通常你会选择将应用打包成一个 JAR 或 WAR 文件,并且可能会包含一些额外的资源和配置。不同类型的项目(如标准 Spring Boot 项目、Spring Boot HTML 项目、Spring Boot Web 项目…

2026/7/3 22:57:17 阅读更多 →
汉明码 MATLAB Simulink 高斯白噪声信道(AWGN信道)以及BSC信道的误码率

汉明码 MATLAB Simulink 高斯白噪声信道(AWGN信道)以及BSC信道的误码率

汉明码 MATLAB Simulink 高斯白噪声信道(AWGN信道)以及BSC信道的误码率最近在折腾信道编码的仿真,发现用Simulink搞汉明码的误码率分析特别有意思。咱们先来瞅瞅汉明码在Simulink里怎么搭模型,顺便看看AWGN和BSC这两个经典信道怎么…

2026/7/3 9:00:47 阅读更多 →
自动化锡膏管理设备源头厂家

自动化锡膏管理设备源头厂家

引言随着电子制造业的快速发展,自动化锡膏管理设备在SMT(表面贴装技术)生产中的重要性日益凸显。本文将深入探讨自动化锡膏管理设备的行业现状、痛点分析、技术发展趋势以及旭同实业在该领域的创新解决方案。行业现状与痛点分析技术瓶颈突出传…

2026/5/17 8:02:09 阅读更多 →

最新新闻

LeetCode:买卖股票的最佳时机(1-3) - Python

LeetCode:买卖股票的最佳时机(1-3) - Python

121. Best Time to Buy and Sell Stock(买卖股票的最佳时机) 问题描述: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计…

2026/7/4 18:55:26 阅读更多 →
Git-Crypt与GitPod结合:云端IDE安全开发工作流实践

Git-Crypt与GitPod结合:云端IDE安全开发工作流实践

1. 项目概述:当云端IDE遇上加密仓库作为一名常年和代码、密钥、配置文件打交道的开发者,我深知一个痛点:如何在享受云端开发环境(如Gitpod)带来的极致便利时,又能确保敏感信息(如API密钥、数据库…

2026/7/4 18:53:26 阅读更多 →
高效率AI写专著:实用工具合集,轻松产出20万字优质专著!

高效率AI写专著:实用工具合集,轻松产出20万字优质专著!

学术专著写作难题与AI工具解决方案 对于那些第一次尝试撰写学术专著的研究者而言,写作过程就像一场在未知领域探险的旅程,充满了各式各样的挑战。选题的困扰让人感到无从下手,如何在“有意义”和“可行性”之间找到一个合适的平衡点成了难题…

2026/7/4 18:53:26 阅读更多 →
STM32F405RG与25CSM04 EEPROM的高效数据检索方案

STM32F405RG与25CSM04 EEPROM的高效数据检索方案

1. 项目背景与核心需求在嵌入式系统开发中,快速精确的数据检索是一个永恒的话题。当我们需要在资源受限的环境中实现高效数据存取时,选择合适的存储器件和控制器至关重要。25CSM04作为一款4Mbit的SPI接口EEPROM,与STM32F405RG这款高性能ARM C…

2026/7/4 18:49:25 阅读更多 →
Java面试通关⑨:SpringBoot核心全集

Java面试通关⑨:SpringBoot核心全集

📖 前言导读 SpringBoot是目前Java后端项目主流开发框架、面试高频核心考点,几乎所有企业新项目均基于SpringBoot搭建,是后端开发必备核心技能。多数开发者仅会简单引入依赖、编写业务代码,对SpringBoot自动配置原理、Starter机制…

2026/7/4 18:49:25 阅读更多 →
音乐情绪识别实战:从声学特征到VA坐标系的端到端落地

音乐情绪识别实战:从声学特征到VA坐标系的端到端落地

1. 这不是科幻,是正在发生的音乐情绪解码实践“Can AI Recognize Our Emotions Through the Music We Are Listening To?”——这个标题乍看像一篇哲学思辨或心理学论文的提问,但在我过去三年深度参与多个音频智能分析项目后,它早已不是假设…

2026/7/4 18:47:24 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻