跟着b站up恋恋风辰zack的视频一起做.视频连接
1 创建项目搭建界面
1.1 创建项目
创建一个Qwidget项目,使用MinGW的构建套件
选择"工具"-"外部"-"配置"-"文本编辑器"-"片段",输入以下代码,后边直接在代码里输入header-custome就会出现整段的内容,感觉和宏的功能类似
/******************************************************************************
*
* @file %{CurrentDocument:FileName}
* @brief XXXX Function
*
* @author 纯真丁一郎
* @date %{CurrentDate:yyyy\/MM\/dd}
* @history
*****************************************************************************/
添加新文件,里边用来存放图标和其他图片,自己添加一些上去
在.pro文件里添加上这两行,一个是软件图标,一个是输出文件的路径,然后调整主窗口大小为300*500
RC_ICONS = ./icon.ico
DESTDIR = ./bin
1.2 搭设登录界面
可以自行发挥搭设界面,新建文件qt界面师类
对于相同的控件,选中控件点击绿色+号-字符串,对于每个的控件输入btn来统一命名,随后可以在最开始的对象那右击,编辑样式表,统一修改他们的样式
搭建一下界面,名称一一对应
在mainwindow.h中添加LoginDialog指针成员,然后在构造函数将LoginDialog设置为中心部件,这样我们直接运行就能看见
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
_login_dlg = new Login_Dialog();
setCentralWidget(_login_dlg);
_login_dlg->show();
}
1.3 搭设注册界面
依旧是新建一个页面,过程和上边类似,可以先运用整体的样式表,在单独修改取消的样式
QPushButton { background-color: red; }
接下来在LoginDialog类里新建切换注册界面的信号,在LoginDialog的构造函数里连接按钮点击事件,
signals:
void switchRegister();
connect(ui->reg_btn, &QPushButton::clicked, this, &LoginDialog::switchRegister);
点击注册按钮后,发出信号,在Mainwidow里切换界面,首先也是初始化注册窗口,新建一个槽函数来实现切换
private:
Register_Dialog * _register_dlg;
public slots:
void slots_switch_register();
在构造函数中声明注册页面,并且绑定信号,编写槽函数功能隐藏和显示页面
connect(_login_dlg,&Login_Dialog::switchRegister,this,&MainWindow::slots_switch_register);
//切换为注册页面的槽函数
void MainWindow::slots_switch_register()
{
setCentralWidget(_register_dlg); // 设置主窗口的中心部件为注册对话框
_login_dlg->hide(); // 隐藏登录对话框
_register_dlg->show(); // 显示注册对话框
}
2 单例模板和http管理类
2.1 完善注册类界面
先在注册类构造函数里添加lineEdit的模式为密码模式,添加一下代码,这样输入密码的时候就会被隐藏
ui->le_password1->setEchoMode(QLineEdit::Password);
ui->le_password2->setEchoMode(QLineEdit::Password);
同时把两个界面都嵌入mainwindow,修改mainwindow的构造函数
_login_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
_register_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
2.2 qss样式切换
新建一个.qss文件,将他放去resources里,在注册类里添加上一个label(err_tip)用于显示错误信息
qss里边写入切换的颜色
err_trp[state = 'normal']{
color: green;
}
err_trp[state = 'err']{
color: red;
}
在新建一个全局的头文件和全局的cpp文件,gloabl.h,gloabl.cpp,global.h声明repolish函数,global.cpp用来定义这个函数。
extern表示repolish是一个在其他地方定义的全局变量,表示变量可以在多个文件中共享.void(QWidget) 表示这个 std::function 对象可以存储一个接受一个 QWidget 参数并且没有返回值的函数或函数对象。在cpp中使用lambda表达式给他赋值
.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <QWidget>
#include <functional>
#include "QStyle"
/**
* @brief repolish 用于刷新样式
*/
extern std::function<void(QWidget *)> repolish;
#endif // GLOBAL_H
.cpp
#include "global.h"
std::function<void(QWidget *)> repolish = [](QWidget *widget) {
// 使用 widget 的样式(style)对象进行 unpolish 操作
// unpolish 通常用于取消对 widget 的样式优化
widget->style()->unpolish(widget);
// 使用 widget 的样式(style)对象进行 polish 操作
// polish 通常用于重新应用样式优化
widget->style()->polish(widget);
};
需要给register类里的err_tip赋值出事状态,在构造函数中添加代码
// 设置错误提示标签的状态属性为 "normal",这可能会影响其样式或行为
ui->err_tip->setProperty("state", "normal");
// 调用全局函数 repolish,刷新错误提示标签的样式
repolish(ui->err_tip);
2.3 验证邮箱按钮
在ui里右击获取按钮-转到槽,新建一个按钮的点击槽函数,使用正则表达式来判断邮箱输入的正确.
(\w+):匹配任意数字或字母
(.|_)?:匹配.或者|,?表示出现0次或1次
(\w*): 匹配零个或多个字母数字字符
(.(\w+))+: 匹配一个或多个由点号分隔的字母数字字符序列
// 当点击获取验证码按钮时触发的方法
void Register_Dialog::on_btn_get_code_clicked()
{
// 获取用户在邮件输入框中输入的内容
auto email = ui->le_mailbox->text();
// 定义一个正则表达式来验证邮箱地址格式
QRegularExpression regex(R"((\w+)(\.|_)?(\w*)@(\w+)(\.(\w+))+)");
// 使用正则表达式检查邮箱地址是否匹配
bool match = regex.match(email).hasMatch();
if (match) {
// 如果邮箱地址格式正确,则发送 HTTP 请求以获取验证码
// 这里应当实现发送 HTTP 请求的逻辑,但此处仅做注释说明
// 发送http请求验真码
} else {
// 如果邮箱地址格式不正确,则提示用户
showTip("邮箱地址不正确", false);
}
}
在RegisterDialog中添加showTip函数
// 显示提示信息的方法
void Register_Dialog::showTip(QString str, bool b_ok)
{
// 设置错误提示标签的状态属性
if (b_ok) {
// 如果 b_ok 为 true,设置状态为正常
ui->err_tip->setProperty("state", "normal");
} else {
// 如果 b_ok 为 false,设置状态为错误
ui->err_tip->setProperty("state", "err");
}
// 设置错误提示标签的文本为传入的字符串
ui->err_tip->setText(str);
// 调用全局函数 repolish 刷新错误提示标签的样式
repolish(ui->err_tip);
}
测试发现qss没有生效,暂时不知道啥原因
2.4 单例类封装
网络请求类要做成一个单例类,这样方便在任何需要发送http请求的时候调用,我们先实现单例类,添加singleton.h实现如下
#ifndef SINGLETON_H
#define SINGLETON_H
#include <global.h>
// 单例模式模板类
template <typename T>
class Singleton {
protected:
// 私有构造函数,防止外部实例化
Singleton() = default;
// 删除拷贝构造函数,防止拷贝
Singleton(const Singleton<T>&) = delete;
// 删除赋值操作符,防止赋值
Singleton& operator=(const Singleton<T>& st) = delete;
// 静态成员变量,用于保存单例的实例
static std::shared_ptr<T> _instance;
public:
// 获取单例实例的静态成员函数
static std::shared_ptr<T> GetInstance(){
// 使用 std::once_flag 和 std::call_once 保证线程安全地初始化实例
static std::once_flag s_flag;
std::call_once(s_flag,[&](){
// 初始化单例实例
_instance = std::shared_ptr<T>(new T);
});
return _instance;
}
// 打印单例实例的地址
void PrintAddress(){
std::cout << _instance.get()<< std::endl;
}
~Singleton(){
std::cout << "this is singleton destruct"<< std::endl;
}
};
//由于 _instance 是一个静态成员变量,它必须在类外定义。
template <typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;
std::call_once 函数接受三个参数:
第一个参数是 std::once_flag 对象 s_flag,用于标记初始化操作是否已经执行过。
第二个参数是一个函数对象(这里是一个 lambda 表达式),当 std::once_flag 表明操作尚未执行时,会调用这个函数对象。
如果 std::once_flag 已经表明操作执行过,那么 std::call_once 不会再次调用该函数对象。这样来确保单例实例只会被创建一次.