QT聊天项目DAY12

0.注册界面完善

1. 密码加密

cpp 复制代码
#ifndef GLOBAL_H
#define GLOBAL_H
#include <QWidget>
#include <functional>
#include <QRegularExpression>
#include "QStyle"

#include <memory>
#include <iostream>
#include <mutex>

#include <QString>
#include <QSettings>

using namespace std;

// extern 声明此变量是在其他文件中定义的全局变量
extern function<void(QWidget*)> repolish;

extern function<QString(QString)> xorString;			// 密码加密

extern QString ConfigPath;								// 配置文件路径
extern QSettings* ConfigSettings;						// 配置文件对象

#endif // GLOBAL_H
cpp 复制代码
#include "Global.h"

#include <QDir>

// 初始化声明的全局变量
function<void(QWidget*)> repolish = [](QWidget* Widget)
{
	Widget->style()->unpolish(Widget);
	Widget->style()->polish(Widget);
};

/* 密码加密 */
function<QString(QString)> xorString = [](QString input)
	{
		QString result = input;
		int length = input.length();
		length %= 255;
		for (int i = 0; i < length; ++i)
		{
			result[i] = QChar(static_cast<ushort>(input[i].unicode() ^ static_cast<ushort>(length)));
		}
		return result;
	};

// 配置文件路径
QString ConfigPath = QDir::currentPath() + "/Config/Config.ini";

// 管理配置文件对象
QSettings* ConfigSettings = new QSettings(ConfigPath, QSettings::IniFormat);

2. 添加定时按钮

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

添加TimerBtn类

.h

cpp 复制代码
#ifndef TIMERBTN_H
#define TIMERBTN_H

#include <QPushButton>
#include <QTimer>

class TimerBtn  : public QPushButton
{
	Q_OBJECT

public:
	TimerBtn(QWidget *parent = 0);
	~TimerBtn();

public:
	virtual void mouseReleaseEvent(QMouseEvent *event) override;

private:
	QTimer *_timer = nullptr;
	int _counter = 0;
};
#endif // TIMERBTN_H

.cpp文件

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;
				setText(QString::fromLocal8Bit("获取"));
				setEnabled(true);										// 设置为可用
				return;
			}
			setText(QString::number(_counter));
		});
}

TimerBtn::~TimerBtn()
{
	_timer->stop();
}

void TimerBtn::mouseReleaseEvent(QMouseEvent * event)
{
	if (event->button() == Qt::LeftButton)
	{
		qDebug() << "TimerBtn::mouseReleaseEvent";
		setEnabled(false);												// 设置为不可用
		_timer->start(1000);											// 启动定时器
		emit clicked();
	}

	QPushButton::mouseReleaseEvent(event);
}

将获取按钮提升为TimerBtm

3. 检测输入是否合理

3.1 枚举错误类型

在Enum.h中添加下列枚举

cpp 复制代码
/* 标签错误类型 */
enum TipErr {
    TIP_SUCCESS = 0,
    TIP_EMAIL_ERR = 1,
    TIP_PWD_ERR = 2,
    TIP_CONFIRM_ERR = 3,
    TIP_PWD_CONFIRM = 4,
    TIP_VERIFY_ERR = 5,
    TIP_USER_ERR = 6
};

3.2 添加错误提示的接口

cpp 复制代码
QMap<TipErr, QString> _TipErrs;																	// 错误提示

void AddTipErr(TipErr err, const QString& tips);												// 添加错误提示


void DelTipErr(TipErr err);																		// 清除错误提示



/* 添加错误提示 */
void RegisterWidget::AddTipErr(TipErr err, const QString & tips)
{
	_TipErrs[err] = tips;
	ShowTipLabel(tips, "error");
}

/* 清空错误提示 */
void RegisterWidget::DelTipErr(TipErr err)
{
	_TipErrs.remove(err);
	if (_TipErrs.isEmpty())
	{
		ui.Tip_Label->clear();
		return;
	}

	ShowTipLabel(_TipErrs.first(), "error");
}

3.3 绑定文本输入的回调检查

cpp 复制代码
/* 检查输入是否合法 */
connect(ui.User_Edit, &QLineEdit::editingFinished, this, &RegisterWidget::CheckUserValid);
connect(ui.Email_Edit, &QLineEdit::editingFinished, this, &RegisterWidget::CheckEmailValid);
connect(ui.PassWord_Edit, &QLineEdit::editingFinished, this, &RegisterWidget::CheckPasswordValid);
connect(ui.Enter_Edit, &QLineEdit::editingFinished, this, &RegisterWidget::CheckEnsurePasswordValid);
connect(ui.Verify_Edit, &QLineEdit::editingFinished, this, &RegisterWidget::CheckVerifyCodeValid);

3.4 回调的实现

cpp 复制代码
/* 检查用户名是否合法 */
bool RegisterWidget::CheckUserValid()
{
	if (ui.User_Edit->text().isEmpty())
	{
		AddTipErr(TipErr::TIP_USER_ERR, QString::fromLocal8Bit("用户名不能为空"));
		return false;
	}

	/* 一切正常则删除错误提示 */
	DelTipErr(TipErr::TIP_USER_ERR);
	return true;
}

/* 检查邮箱是否合法 */
bool RegisterWidget::CheckEmailValid()
{
	QString emailText = ui.Email_Edit->text();
	// 邮箱地址的正则表达式
	QRegularExpression regex(R"((\w+)(\.|_)?(\w*)@(\w+)(\.(\w+))+)");
	bool match = regex.match(emailText).hasMatch();
	if (!match)
	{
		AddTipErr(TipErr::TIP_EMAIL_ERR, QString::fromLocal8Bit("邮箱格式不正确"));
		return false;
	}

	/* 一切正常则删除错误提示 */
	DelTipErr(TipErr::TIP_EMAIL_ERR);
	return true;
}

/* 检查密码是否合法 */
bool RegisterWidget::CheckPasswordValid()
{
	QString passText = ui.PassWord_Edit->text();
	if (passText.length() < 6 || passText.length() > 15)
	{
		AddTipErr(TipErr::TIP_PWD_ERR, QString::fromLocal8Bit("密码长度必须在6-15位之间"));
		return false;
	}

	/* 密码长度至少6位 可以是字母、数字、特定的特殊字符 */
	QRegularExpression regExp("^[a-zA-Z0-9!@#$%^&*]{6,15}$");
	bool match = regExp.match(passText).hasMatch();
	if (!match)
	{
		AddTipErr(TipErr::TIP_PWD_ERR, QString::fromLocal8Bit("不能包含非法字符"));
		return false;
	}

	/* 一切正常则删除错误提示 */
	DelTipErr(TipErr::TIP_PWD_ERR);
	return true;
}

/* 检查确认密码是否合法 */
bool RegisterWidget::CheckEnsurePasswordValid()
{
	QString enterText = ui.Enter_Edit->text();
	if (ui.PassWord_Edit->text() != enterText)
	{
		AddTipErr(TipErr::TIP_CONFIRM_ERR, QString::fromLocal8Bit("两次密码输入不一致"));
		return false;
	}

	/* 一切正常则删除错误提示 */
	DelTipErr(TipErr::TIP_CONFIRM_ERR);
	return true;
}

/* 检查验证码是否合法 */
bool RegisterWidget::CheckVerifyCodeValid()
{
	QString verifyText = ui.Verify_Edit->text();
	if (verifyText.isEmpty())
	{
		AddTipErr(TipErr::TIP_VERIFY_ERR, QString::fromLocal8Bit("验证码不能为空"));
		return false;
	}

	/* 一切正常则删除错误提示 */
	DelTipErr(TipErr::TIP_VERIFY_ERR);
	return true;
}

当确认按钮刷新时,检查输入是否合法

cpp 复制代码
/* 确认按钮点击 */
void RegisterWidget::OnConfirmButtonClicked()
{
	bool valid = CheckUserValid();
	if (!valid)
		return;
	valid = CheckEmailValid();
	if (!valid)
		return;
	valid = CheckPasswordValid();
	if (!valid)
		return;
	valid = CheckEnsurePasswordValid();
	if (!valid)
		return;
	valid = CheckVerifyCodeValid();
	if (!valid)
		return;

4. 隐藏和显示密码

4.1 QT样式表

css 复制代码
#Enter_Visible[state='visible_hover']
{
	border-image: url(:/Chat/Images/visible_hover.png);
}
css 复制代码
#PassWord_Visible[state='unvisible']
{
	border-image: url(:/Chat/Images/unvisible.png);
}

#PassWord_Visible[state='unvisible_hover']
{
	border-image: url(:/Chat/Images/unvisible_hover.png);
}

#PassWord_Visible[state='visible']
{
	border-image: url(:/Chat/Images/visible.png);
}

#PassWord_Visible[state='visible_hover']
{
	border-image: url(:/Chat/Images/visible_hover.png);
}

#Enter_Visible[state='unvisible']
{
	border-image: url(:/Chat/Images/unvisible.png);
}

#Enter_Visible[state='unvisible_hover']
{
	border-image: url(:/Chat/Images/unvisible_hover.png);
}

#Enter_Visible[state='visible']
{
	border-image: url(:/Chat/Images/visible.png);
}

#Enter_Visible[state='visible_hover']
{
	border-image: url(:/Chat/Images/visible_hover.png);
}

4.2 重写ClickedLabel

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

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

定义ClickLableState,包括两种状态,一个是普通状态,一个是选中状态。Label的六种状态都是基于这两种状态实现的

通过样式表来灵活切换标签的UI,只需要通过setProperty("state", str);设置对应的状态即可

然后自定义的控件只需要继承即可实现UI随着鼠标的进入离开以及点击时的切换

.h

cpp 复制代码
#ifndef CLICKEDLABEL_H
#define CLICKEDLABEL_H

#include <QLabel>
#include <QString>
#include "Enum.h"

class ClickedLabel  : public QLabel
{
	Q_OBJECT

public:
	ClickedLabel(QWidget *parent = 0);
	~ClickedLabel();

public:
	virtual void mousePressEvent(QMouseEvent *event) override;
	virtual void enterEvent(QEvent *event) override;
	virtual void leaveEvent(QEvent *event) override;

	ClickLabelState GetState() const;

signals:
	void clicked(void);

private:
	void UpdateStyleSheet(QString str);													// 刷新样式

private:
	QString _Normal;																	// 未选中
	QString _NormalHover;
	QString _NormalPress;
	
	QString _Selected;																	// 选中
	QString _SelectedHover;
	QString _SelectedPress;

	ClickLabelState _CurrState;															// 当前标签状态
};
#endif // CLICKEDLABEL_H

.cpp

cpp 复制代码
#include "ClickedLabel.h"

#include <QMouseEvent>
#include <QDebug>
#include "Global.h"

ClickedLabel::ClickedLabel(QWidget *parent)
	: QLabel(parent), _CurrState(ClickLabelState::Normal)
{
	_Normal = "unvisible";
	_NormalHover = "unvisible_hover";
	_NormalPress = "";

	_Selected = "visible";
	_SelectedHover = "visible_hover";
	_SelectedPress = "";
	UpdateStyleSheet(_Normal);
}

ClickedLabel::~ClickedLabel()
{}

/* 刷新样式 */
void ClickedLabel::UpdateStyleSheet(QString str)
{
	setProperty("state", str);
	repolish(this);
	update();
}

/* 鼠标按下事件 */
void ClickedLabel::mousePressEvent(QMouseEvent * event)
{
	if (event->button() == Qt::LeftButton)
	{
		if (_CurrState == ClickLabelState::Normal)
		{
			qDebug() << "clicked , change to selected hover" << _SelectedHover;
			_CurrState = ClickLabelState::Selected;
			UpdateStyleSheet(_SelectedHover);
		}
		else
		{
			qDebug() << "clicked , change to normal hover" << _NormalHover;
			_CurrState = ClickLabelState::Normal;
			UpdateStyleSheet(_NormalHover);
		}
		emit clicked();
	}

	QLabel::mousePressEvent(event);
}

/* 鼠标悬停进入事件 */
void ClickedLabel::enterEvent(QEvent* event)
{
	if (_CurrState == ClickLabelState::Normal)
	{
		qDebug() << "enter , change to hover" << _NormalHover;
		UpdateStyleSheet(_NormalHover);
	}
	else
	{
		qDebug() << "enter , change to selected hover" << _SelectedHover;
		UpdateStyleSheet(_SelectedHover);
	}

	QLabel::enterEvent(event);
}

/* 鼠标离开事件 */
void ClickedLabel::leaveEvent(QEvent* event)
{
	if (_CurrState == ClickLabelState::Normal)
	{
		qDebug() << "leave , change to normal" << _Normal;
		UpdateStyleSheet(_Normal);
	}
	else
	{
		qDebug() << "leave , change to selected" << _Selected;
		UpdateStyleSheet(_Selected);
	}
	QLabel::leaveEvent(event);
}

ClickLabelState ClickedLabel::GetState() const
{
	return _CurrState;
}

4.3 创建可视化控件

将该标签提升为自定义的控件

4.4 在注册窗口中绑定可视化控件的信号槽

cpp 复制代码
/* 初始化可视化控件 */
ui.PassWord_Visible->setCursor(Qt::PointingHandCursor);
ui.Enter_Visible->setCursor(Qt::PointingHandCursor);
BindSlotsFromClickedLabel();
cpp 复制代码
/* 绑定信号槽从ClickedLabel控件 */
void RegisterWidget::BindSlotsFromClickedLabel()
{
	/* 密码可见 */
	connect(ui.PassWord_Visible, &ClickedLabel::clicked, this, [this]()
		{
			auto state = ui.PassWord_Visible->GetState();
			if (state == ClickLabelState::Normal)
			{
				ui.PassWord_Edit->setEchoMode(QLineEdit::Password);
			}
			else if (state == ClickLabelState::Selected)
			{
				ui.PassWord_Edit->setEchoMode(QLineEdit::Normal);
			}
		});

	connect(ui.Enter_Visible, &ClickedLabel::clicked, this, [this]()
		{
			auto state = ui.Enter_Visible->GetState();
			if (state == ClickLabelState::Normal)
			{
				ui.Enter_Edit->setEchoMode(QLineEdit::Password);
			}
			else if (state == ClickLabelState::Selected)
			{
				ui.Enter_Edit->setEchoMode(QLineEdit::Normal);
			}
		});
}

4.5 注册成功的提示界面

首先添加两个标签,然后调整整体窗口布局为垂直布局,让两个标签水平居中,添加返回按钮并设置按钮最大最小高度为25,将按钮放置到窗口内,并设置窗口布局为水平

4.6 注册成功的界面切换和自动跳转登陆界面

4.7 绑定返回按钮和取消按钮的槽函数

4.8 在主窗口绑定登录界面和注册界面的切换

切换到注册界面时,先创建注册界面的对象,然后绑定信号方便后续切换回登陆界面

同理,切换回登录界面时,先创建登录界面的对象,然后绑定信号方便切换回注册界面

为什么不再构造函数中创建是因为,调用setCentralWidget会销毁别的闲置窗口,从注册界面再切回登录界面时,此时登陆界面就被销毁了,所以调用已销毁对象的槽函数,程序会崩溃

4.9 测试注册成功的逻辑

流程已跑通

相关推荐
用户0328472220708 小时前
如何搭建本地yum源(上)
运维
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz3 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈3 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造