QT聊天项目DAY12

发布于:2025-05-24 ⋅ 阅读:(23) ⋅ 点赞:(0)

0.注册界面完善

1. 密码加密

#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
#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

#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文件

#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中添加下列枚举

/* 标签错误类型 */
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 添加错误提示的接口

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 绑定文本输入的回调检查

/* 检查输入是否合法 */
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 回调的实现

/* 检查用户名是否合法 */
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;
}

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

/* 确认按钮点击 */
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样式表

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

#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

#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

#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 在注册窗口中绑定可视化控件的信号槽

/* 初始化可视化控件 */
ui.PassWord_Visible->setCursor(Qt::PointingHandCursor);
ui.Enter_Visible->setCursor(Qt::PointingHandCursor);
BindSlotsFromClickedLabel();
/* 绑定信号槽从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 测试注册成功的逻辑

流程已跑通


网站公告

今日签到

点亮在社区的每一天
去签到