效果图

主要功能模块
界面绘制
标准界面
界面主要由设计界面进行设置




代码:
主要是信号槽的连接,主要使用的lambda表达式
登录界面:
void LoginWindow::initializeLogInWindow()
{
connect(m_ui->tb_close, &QToolButton::clicked, this, [&]() {
saveUserInfo();
close();
});
connect(m_ui->tb_minimality, &QToolButton::clicked, [&]() {
hide();
systemTray->show();
});
//自动登录的取消按钮
connect(m_ui->pb_cancal, &QPushButton::clicked, [&]() {
m_ui->sw->setCurrentIndex(0);
});
//gif播放
gifMove = new QMovie(":/resource/background.gif");
m_ui->l_upGif->setMovie(gifMove);
gifMove->start();
connect(m_ui->le_user, &QLineEdit::cursorPositionChanged, [&]() {
m_ui->l_user->setStyleSheet(QStringLiteral("image: url(:/resource/user.png);"));
});
connect(m_ui->le_password, &QLineEdit::cursorPositionChanged, [&]() {
m_ui->l_password->setStyleSheet(QStringLiteral("image: url(:/resource/password.png);"));
m_ui->l_keyboard->setStyleSheet(QStringLiteral("image: url(:/resource/keyboard2.png);"));
});
connect(m_ui->pb_login, &QPushButton::clicked, [&]() {
m_ui->pb_login->setText("登录中");
makeSenderJson();
m_user->startConnect(sendJson);
});
}
注册界面:
使用正则表达式对输入进行一定的限制
void LoginWindow::initializeRegister()
{
connect(m_ui->pb_register, &QPushButton::clicked, [&]() {
m_ui->sw->setCurrentIndex(3);
//设置id和密码的正则表达式确保用户输入的符合要求
QRegExp regxId("[a-zA-Z0-9]{8}");
QValidator *validatorId = new QRegExpValidator(regxId);
m_ui->le_registerId->setValidator(validatorId);
QRegExp regxPwd("[a-zA-Z0-9]{16}");
QValidator *validatorPwd = new QRegExpValidator(regxPwd);
m_ui->le_registerPwd->setValidator(validatorPwd);
m_ui->le_registerPwd2->setValidator(validatorPwd);
});
connect(m_ui->pb_registerOk, &QPushButton::clicked, [&]() {
//对注册信息进行一个初步的判断
if (m_ui->le_registerId->text().size() < 4 || m_ui->le_registerPwd->text().size() < 8)
{
QMessageBox::warning(this, "警告", "你的输入不符合要求!", QMessageBox::Cancel);
m_ui->le_registerId->clear();
m_ui->le_registerPwd->clear();
m_ui->le_registerPwd2->clear();
return;
}
if (m_ui->le_registerPwd->text() != m_ui->le_registerPwd2->text())
{
QMessageBox::warning(this, "警告", "请确认两次密码输入相同!", QMessageBox::Ok);
m_ui->le_registerPwd->clear();
m_ui->le_registerPwd2->clear();
return;
}
if (!m_ui->le_registerPwd->text().contains(QRegExp("[A-Za-z]")))//查看用户的密码是否为单纯数字
{
QMessageBox::warning(this, "警告", "密码过于简单!", QMessageBox::Ok);
m_ui->le_registerPwd->clear();
m_ui->le_registerPwd2->clear();
return;
}
makeSenderJson();
m_user->startConnect(sendJson);
});
connect(m_ui->pb_registerCancal, &QPushButton::clicked, [&]() {
m_ui->sw->setCurrentIndex(0);
m_ui->le_registerId->clear();
m_ui->le_registerPwd->clear();
m_ui->le_registerPwd2->clear();
});
}
由于是无系统边框还需实现窗口移动,这里用到重写鼠标事件:
//拖拽操作
void LoginWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)//只相应鼠标左键的按下
{
_move = true;
mouseStartPoint = event->globalPos();//鼠标的初始位置
windowStartPoint = this->frameGeometry().topLeft(); //窗口的初始位置
}
}
void LoginWindow::mouseMoveEvent(QMouseEvent *event)
{
if (_move)
{
QPoint mouseMove = event->globalPos() - mouseStartPoint; //获得鼠标移动的距离
this->move(windowStartPoint + mouseMove); //改变窗口的位置
}
}
void LoginWindow::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)//判断左键抬起关闭获取鼠标移动
_move = false;
}
自定义的下拉框
下拉框由QListWidgetItem来实现:
由于下拉框中有删除操作,所以为了方便执行删除,使用QSignalMapper 处理信号,以获取执行删除操作的是哪一个窗口。
void LoginWindow::initializeUserInfo()
{
//读取文件中的JSON数组
//打开文件
QFile file(".\\resource\\info\\userInfo.json");
file.open(QIODevice::ReadWrite | QIODevice::Text);
QByteArray userData = file.readAll();//读取文件
file.close();
QJsonDocument jsonD = QJsonDocument::fromJson(userData);
QVariantList jsonList = jsonD.toVariant().toList();//将数组中的每个json转换
//遍历
for (size_t i = 0; i < jsonList.count(); i++)
{
QVariantMap map = jsonList[i].toMap();
userId.append(map["id"].toString());
userPwd.append(decodePwd(map["pwd"].toString()));
rememberPwd.append(map["remember"].toString());
if (i == 0)
if (map["logIn"] == "yes")
log_in = true;
else
log_in = false;
}
//创建下拉框
if (userId.isEmpty())//没有用户不创建下拉框
return;
QListWidget *list = new QListWidget(this);
list->setFocusPolicy(Qt::NoFocus);
m_ui->cb_user->setModel(list->model());
m_ui->cb_user->setView(list);
QSignalMapper *signalMapper = new QSignalMapper(this);
for (size_t i = 0; i < userId.size(); i++)
{
//创建下拉框中的窗口
QListWidgetItem *listItem = new QListWidgetItem(list);
QHBoxLayout *layout = new QHBoxLayout();//创建布局
QPushButton *delButton = new QPushButton(this);//创建下拉框中的删除按钮
delButton->setIcon(QIcon(":/resource/close2.png"));
delButton->setStyleSheet("QPushButton::hover{background-color: rgb(255,0, 0);} QPushButton{border:0 px;}");
QLabel *lable = new QLabel(QString("%1").arg(userId.at(i)), this);//设置用户id
QLabel *icon = new QLabel(this);//设置图片(预留功能设置头像)
icon->setFixedSize(35, 35);
icon->setStyleSheet(QLatin1String("border-image: url(:/resource/user2.png)"));
lable->setFixedSize(270, 30);
layout->addWidget(icon);
layout->addSpacing(3);
layout->addWidget(lable);
layout->addSpacing(1);
layout->addWidget(delButton);
QWidget *widget = new QWidget(this);
widget->setStyleSheet("QWidget::hover{color: #2e9aff;}");
widget->setLayout(layout);//将布局加入窗口
list->setItemWidget(listItem, widget);
//连接删除按钮的信号槽 这里交由QSignalMapper来处理 用QSignalMapper转发窗口的序号用于删除操作
connect(delButton, &QPushButton::clicked, signalMapper, static_cast<void(QSignalMapper::*)()>(&QSignalMapper::map));
signalMapper->setMapping(delButton, i);//设置转发消息的参数
index.append(i);//加入对应值处理删除工作
}
//连接删除按钮的信号槽
connect(signalMapper, static_cast<void(QSignalMapper::*)(int)>(&QSignalMapper::mapped), [&](int i) {
m_ui->sw->setCurrentIndex(1);
delUserPos = index.indexOf(i);
});
m_ui->le_user->setText(m_ui->cb_user->currentText());//初始化将第一个设为显示
//此信号会发送更换后的文本信息
connect(m_ui->cb_user, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged), [&](const QString &text) {
if (judge)return;//信号已交由按钮处理
m_ui->le_user->setText(text);
m_ui->cb_voluntarily->setChecked(false);//非0的所以下拉框成员都不是自动登录
int currentIndex = m_ui->cb_user->currentIndex();
m_ui->le_user->setText(userId.at(currentIndex));
if (userPwd.at(userId.indexOf(userId.at(currentIndex))) != "null")
{
m_ui->le_password->setText(userPwd.at(userId.indexOf(userId.at(currentIndex))));
if (rememberPwd.at(userId.indexOf(userId.at(currentIndex))) == "yes")
m_ui->cb_remember->setChecked(true);
}
else
{
m_ui->cb_remember->setChecked(false);
m_ui->le_password->clear();
}
});
}
系统托盘
//系统托盘
systemTray = new QSystemTrayIcon(this);
//设置系统托盘
systemTray->setIcon(QIcon(":/resource/Icon.png"));
systemTray->setToolTip("CJJMail");
setWindowIcon(QIcon(":/resource/Icon.png"));
connect(systemTray, &QSystemTrayIcon::activated, [&](QSystemTrayIcon::ActivationReason reason) {
if (reason == QSystemTrayIcon::DoubleClick)
this->showNormal();
//恢复窗口
});
//为系统托盘设置菜单
QMenu *systemTrayMenu = new QMenu(this);
systemTrayMenu->setStyleSheet(QString("background-color: #a7a7a7;"));
//设置菜单按钮
QAction *_closeAction = new QAction("退出", this);
_closeAction->setIcon(QIcon(":/resource/exit.png"));
systemTrayMenu->addAction(_closeAction);
systemTray->setContextMenu(systemTrayMenu);
//连接托盘Action信号槽
connect(_closeAction, &QAction::triggered, this, &LoginWindow::close);
功能模块
读写用户预留信息
使用JSON保存用户信息:
保存操作:
void LoginWindow::saveUserInfo()
{
for (size_t i = 1; i < userId.size(); i++)
{
if (userId.at(i) == m_ui->le_user->text())
{
userId.pop_back();
userPwd.pop_back();
rememberPwd.pop_back();
break;
}
}
QJsonArray jsonArray;//因为有多个用户记录所以生成json数组
for (size_t i = 0; i < userId.size(); i++)
{
QJsonObject jsonObj;
jsonObj.insert("id", userId.at(i));
jsonObj.insert("pwd", encryptPwd(userPwd.at(i)));
jsonObj.insert("remember", rememberPwd.at(i));
if (i == 0)
if (m_ui->cb_voluntarily->isChecked())
jsonObj.insert("logIn", "yes");
else
jsonObj.insert("logIn", "no");
jsonArray.append(jsonObj);
}
QJsonDocument jsonD;
jsonD.setArray(jsonArray);
QFile file(".\\resource\\info\\userInfo.json");
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(jsonD.toJson());
file.close();
}
明文保存会泄露密码所以用ASCII码进行简单的转义:
QString LoginWindow::encryptPwd(QString unencryptPwd)
{
//用char 的ASCII码进行加密
//先将字符串转为char 再将char转为int即可
char* _pwd;
QByteArray ba = unencryptPwd.toLatin1(); // must
_pwd = ba.data();
QString tmpPwd;
for (size_t i = 0; i < unencryptPwd.length(); i++)
{
int tmp = *_pwd;
tmpPwd.append(QString::number(tmp));
if (i + 1 != unencryptPwd.length())
{
tmpPwd.append("-");
++_pwd;
}
}
return tmpPwd;
}
读取:
//读取文件中的JSON数组
//打开文件
QFile file(".\\resource\\info\\userInfo.json");
file.open(QIODevice::ReadWrite | QIODevice::Text);
QByteArray userData = file.readAll();//读取文件
file.close();
QJsonDocument jsonD = QJsonDocument::fromJson(userData);
QVariantList jsonList = jsonD.toVariant().toList();//将数组中的每个json转换
//遍历
for (size_t i = 0; i < jsonList.count(); i++)
{
QVariantMap map = jsonList[i].toMap();
userId.append(map["id"].toString());
userPwd.append(decodePwd(map["pwd"].toString()));
rememberPwd.append(map["remember"].toString());
if (i == 0)
if (map["logIn"] == "yes")
log_in = true;
else
log_in = false;
}
发送消息的格式也为JSON:
void LoginWindow::makeSenderJson()
{
sendJson.clear();
if (m_ui->sw->currentIndex() == 0)//判断为登录
{
QJsonObject jsonO;
jsonO.insert("type", "logIn");
jsonO.insert("id", m_ui->le_user->text());
jsonO.insert("pwd", m_ui->le_password->text());
QJsonDocument jsonD;
jsonD.setObject(jsonO);
sendJson = jsonD.toJson();
}
else if (m_ui->sw->currentIndex() == 2)//自动登录
{
QJsonObject jsonO;
jsonO.insert("type", "logIn");
jsonO.insert("id", userId.at(0));
jsonO.insert("pwd", userPwd.at(0));
QJsonDocument jsonD;
jsonD.setObject(jsonO);
sendJson = jsonD.toJson();
}
else if (m_ui->sw->currentIndex() == 3)//注册
{
QJsonObject jsonO;
jsonO.insert("type", "register");
jsonO.insert("id", m_ui->le_registerId->text());
jsonO.insert("pwd", m_ui->le_registerPwd->text());
QJsonDocument jsonD;
jsonD.setObject(jsonO);
sendJson = jsonD.toJson();
}
}
解密:
QString LoginWindow::decodePwd(QString encryptPwd)
{
QStringList tmpList = encryptPwd.split("-");
QString decode;
std::for_each(tmpList.begin(), tmpList.end(), [&](QString pwd) {
int tmp = pwd.toInt();
char _tmp = tmp;
decode.append(_tmp);
});
return decode;
}
TCP通讯
维护了一个新对象,用于之后的其他功能:
UserManage::UserManage(QObject *parent)
: QObject(parent)
{
m_socket = new QTcpSocket(this);
}
UserManage::~UserManage()
{
}
void UserManage::startConnect(QByteArray message)
{
m_message = message;
m_socket->connectToHost(QHostAddress("127.0.0.1"), 8000);
//m_socket->connectToHost(QHostAddress("192.168.8.1"), 8000);
connect(m_socket, &QTcpSocket::connected, [=]() {
connectSucceed();
});
}
void UserManage::connectSucceed()
{
m_socket->write(m_message);
connect(m_socket, &QTcpSocket::readyRead, [=]() {
readMessage();
});
}
void UserManage::readMessage()
{
QString message= m_socket->readAll();
emit newData(message);
}
有情链接与致谢
界面设计过程中,参读了大佬们的文章受益匪浅。我只是站在巨人的肩膀上模仿前人的成就。
@花狗Fdog
@我只是一个单纯的2
本文介绍了客户端登录模块的效果图,包括界面绘制的标准化、自定义下拉框、系统托盘功能,以及与之相关的登录、注册流程和TCP通讯。展示了如何通过lambda表达式连接信号槽,以及密码验证、自动登录和用户信息管理的详细代码实现。
1293

被折叠的 条评论
为什么被折叠?



