关于背景渐变色的说明:
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0
rgba(255, 255, 0, 255), stop:1 rgba(255,255,202, 255));
QPushButton{
color: rgb(0, 0, 0); //设置文本颜色为黑色
border:0px groove gray; //设置边框样式为0像素宽的灰色凹槽边框
border-radius:5px; //设置圆角矩形的圆角的旋转半径为5个像素(半径越小越尖锐
padding:2px 4px; //设置按钮的内边距为2像素上下和4像素左右的空白。
border-style: outset; //设置边框样式为凸起的效果,使按钮的边框呈现立体效果。
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 226, 78), stop:1 rgba(255, 236, 125, 255)); //渐变背景色
}
通过重写鼠标移动相关函数来实现,即在loginForm.h
中重新声明这些函数:
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
在loginForm.cpp
中的实现如下:
void LoginForm::mousePressEvent(QMouseEvent *event)
{
position = event->globalPos() - this->pos();
}
position
为LoginForm
类中的私有成员。
使用 event->globalPos()
获取了当前鼠标按下的全局坐标位置,即鼠标相对于整个屏幕的位置。然后减去this->pos()
,即当前窗口的位置,可以得到鼠标相对于窗口的位置。
void LoginForm::mouseMoveEvent(QMouseEvent *event)
{
move(event->globalPos() - position);
}
使用 event->globalPos()
获取了鼠标相对于整个屏幕的位置。减去之前保存的 position
变量,即鼠标相对于窗口最初按下位置的偏移量,得到了鼠标的新位置。
调用 move
函数,将窗口移动到新的位置。
void LoginForm::mouseReleaseEvent(QMouseEvent *event)
{
}
release
函数不需要多余的操作。
LoginForm
和主窗口Widget
,然后将Widget
隐藏,LoginForm
显示。RecordFile
。Widget
,如果错误则弹出提示窗口InfoForm
。Widget w;//主窗口
LoginForm login;//登录窗口
w.hide();//一开始,主窗口隐藏
login.show();//登录窗口显示
login.connect(&login, SIGNAL(login(const QString&, const QByteArray&)),
&w, SLOT(on_show(const QString&, const QByteArray&)));
//若登录成功,则展示主窗口
将login
信号和主窗口的on_show
槽函数connect
在一起,若成功登录,则显示主窗口。
提供了使用 RSA 加密和解密技术从文件中读取和写入 JSON 数据的功能。主要表现为,根据文件路径读取文件内容,进行解密和解析操作,将解析结果存储到成员变量中。在解析成功时直接返回,否则设置默认值后返回。
//配置和信息记录文件类
class RecordFile
{
public:
RecordFile(const QString& path);
~RecordFile();
QJsonObject& config();
bool save();
private:
QJsonObject m_config;
QString m_path;
SslTool tool;
};
在构造函数中,传入QString类型的path,使用do…while(false),当出错时及时收手,并保证无论如何都有初始值。
RecordFile::RecordFile(const QString& path)
{
QFile file(path);
m_path = path;
do
{
...
}
while(false);
//读取失败,则设置默认值
...
return;
}
以只读方式打开文件,注意判错:
QByteArray data = file.readAll();
if(data.size() <= 0)break;
使用 RSA 解码函数 (tool.rsaDecode()
) 对 data
进行解码,然后进行分析。
data = tool.rsaDecode(data);
int i = 0;
for(; i < data.size(); i++)
{
if((int)data[i] >= (int)0x7F || (int)data[i] < (int)0x0A)
{
data.resize(i);
break;
}
}
使用 QJsonDocument::fromJson()
函数将处理后的 data 解析为 JSON 文档。如果解析成功且文档非空,则将该对象赋给成员变量 m_config,然后返回。
否则输出错误信息。
QJsonParseError json_error;
QJsonDocument doucment = QJsonDocument::fromJson(data, &json_error);
if (json_error.error == QJsonParseError::NoError)
{
if (doucment.isObject())
{
m_config = doucment.object();
return;//成功了,直接返回
}
}
else
{
qDebug().nospace() << __FILE__ << "(" << __LINE__ << "):" << json_error.errorString() << json_error.error;
}
当读取失败时,设置默认值。
//读取失败,则设置默认值
file.close();
QJsonValue value = QJsonValue();
m_config.insert("user", value);
m_config.insert("password", value);
m_config.insert("date", "2024-01-01 10:10:10");
return;
record = new RecordFile("xxxxxxxx");//path
this->setWindowFlag(Qt::FramelessWindowHint);
ui->nameEdit->setPlaceholderText(u8"用户名/手机号/邮箱");
ui->nameEdit->setFrame(false);
ui->pwdEdit->setPlaceholderText(u8"填写密码");
ui->pwdEdit->setFrame(false);
ui->pwdEdit->setEchoMode(QLineEdit::Password);
net = new QNetworkAccessManager(this);
connect(net, SIGNAL(finished(QNetworkReply*)),
this, SLOT(slots_login_request_finshed(QNetworkReply*)));
info.setWindowFlag(Qt::FramelessWindowHint);//无头窗口
info.setWindowModality(Qt::ApplicationModal);
QSize sz = size();
info.move((sz.width() - info.width()) / 2, (sz.height() - info.height()) / 2);//调整大小
当登录按钮被按下
void LoginForm::on_logoButton_released()
{
QString user = ui->nameEdit->text();
//检查用户名的有效性
if(user.size() == 0 || user == u8"用户名/手机号/邮箱")
{
info.set_text(u8"用户不能为空\r\n请输入用户名", u8"确认").show();
ui->nameEdit->setFocus();
return;
}
//检查密码的有效性
QString pwd = ui->pwdEdit->text();
if(pwd.size() == 0 || pwd == u8"填写密码")
{
info.set_text(u8"密码不能为空\r\n请输入密码", u8"确认").show();
ui->pwdEdit->setFocus();
return;
}
check_login(user, pwd);
}
检查用户名和密码的有效性,如果用户名或密码输入为空,则对InfoForm设置对应的文字,并将其show()。
比如:当为填写用户名
随机输入一个md5_key
const char* MD5_KEY = "*&^%$#@b.v+h-b*g/h@n!h#n$d^ssx,.kl;
bool LoginForm::check_login(const QString& user, const QString& pwd)
{
QCryptographicHash md5(QCryptographicHash::Md5);
QNetworkRequest request;
QString url = QString(HOST) + "/login?";
QString salt = QString::number(QRandomGenerator::global()->bounded(10000, 99999));
QString time = getTime();
md5.addData((time + MD5_KEY + pwd + salt).toUtf8());
QString sign = md5.result().toHex();
url += "time=" + time + "&";
url += "salt=" + salt + "&";
url += "user=" + user + "&";
url += "sign=" + sign;
request.setUrl(QUrl(url));
record->config()["password"] = ui->pwdEdit->text();
record->config()["user"] = ui->nameEdit->text();
this->setEnabled(false);//防止用户狂点登录,将窗口禁用
net->get(request);// ---->slots_login_request_finshed
return true;
}
调用 net->get(request)
发起网络请求,进入异步的网络请求流程,请求完成后将会触发 slots_login_request_finshed
槽函数
void LoginForm::slots_login_request_finshed(QNetworkReply* reply)
this->setEnabled(true)
将登录窗体重新启用。 this->setEnabled(true);
reply->errorString()
获取错误信息,并使用 info.set_text()
设置一个提示框的文本,显示登录发生错误的提示信息。 if(reply->error() != QNetworkReply::NoError)
{
info.set_text(u8"登录发生错误\r\n" + reply->errorString(), u8"确认").show();
return;
}
reply->readAll()
返回的 QByteArray
对象中。 QByteArray data = reply->readAll();
QJsonDocument::fromJson()
解析响应数据为 QJsonDocument
对象,并检查解析错误的信息,如果解析错误码为 QJsonParseError::NoError
,则表示解析成功。 QJsonParseError json_error;
QJsonDocument doucment = QJsonDocument::fromJson(data, &json_error);
obj.contains()
判断响应对象中是否包含特定的键和值。如果包含 status
和 message
键,则进一步获取对应的值,并检查登录状态是否为成功状态(status.toInt(-1) == 0
)。如果登录成功,则将 LOGIN_STATUS
设置为 true
,发射 login()
信号,隐藏登录窗体,并标记登录成功,然后更新登录时间,最后保存记录对象的配置信息。if (json_error.error == QJsonParseError::NoError)
{
if (doucment.isObject())
{
const QJsonObject obj = doucment.object();
if (obj.contains("status") && obj.contains("message"))
{
QJsonValue status = obj.value("status");
QJsonValue message = obj.value("message");
if(status.toInt(-1) == 0) //登录成功
{
LOGIN_STATUS = status.toInt(-1) == 0;
emit login(record->config()["user"].toString(), QByteArray());
hide();
login_success = true;
char tm[64] = "";
time_t t;
time(&t);
strftime(tm, sizeof(tm), "%Y-%m-%d %H:%M:%S", localtime(&t));
record->config()["date"] = QString(tm);//更新登录时间
record->save();
}
}
}
}
QJsonParseError::NoError
,则表示解析失败,通过 info.set_text()
设置提示框的文本,显示登录失败的提示信息。info.set_text(u8"登录失败\r\n无法解析服务器应答!", u8"确认").show();
info.set_text()
设置提示框的文本,显示用户名或密码错误的提示信息。if(!login_success)
{
info.set_text(u8"登录失败\r\n用户名或者密码错误!", u8"确认").show();
}
reply->deleteLater()
删除响应对象。reply->deleteLater();
当登陆成功,将会emit login
信号。在构造函数中已将login
与Widget::on_show()
conncet在一起,所以登录成功则会显示主窗口。