Qt聊天室项目

目录

项目要求

项目背景

技术分析

架构设计

 服务器架构

 模块划分

 模块之间的交互

 客户端架构

 模块划分

模块之间交互

 项目展示

项目实现

服务器

ui

server.pro

dialog.h

dialog.cpp

客户端

ui

cient.pro 

dialog.h

dialog.cpp

打包步骤不做演示

视频演示


项目要求

【必做内容】

1. 多人在线聊天室公聊

2. 聊天记录保存与搜索

3. 界面设计与美化

4. 打包

【选做内容】

1. 私聊

2. 群文件上传与下载

3. 账户管理

4. 登录人脸检测

......

项目背景

       在当今互联网时代,即时通讯已经成为人们日常生活中不可或缺的一部分。为了掌握网络编程和跨平台GUI开发的技能,我决定使用Qt框架完成一个多人聊天工作室项目。该项目旨在实现一个简单而实用的多人聊天应用,允许多个用户在同一时间进行文字交流等。

        为了实现这一目标,我选择了Qt框架作为开发工具。Qt提供了丰富的网络编程库和易用的GUI开发工具,同时具有跨平台特性,这使得它成为我的首选。在开始项目之前,我对Qt框架的相关知识进行了深入学习,并熟悉了其网络模块、信号与槽机制以及界面设计工具。

        总的来说,多人聊天室是一种常见的网络应用,它允许多个用户之间进行实时的文字交流,并且我也额外附加了一些功能,在这个项目中,我使用了Qt框架,结合了其强大的网络通信和图形界面开发功能,实现了一个简单、美观、实用的多人聊天工作室应用。

技术分析


注册 登录:在实现注册和登录功能时,我利用了Qt框架的网络编程库和数据存储技术。用户注册时,我采用了用户名和密码的方式进行账户信息的创建并将其发到服务器,将用户信息存储在服务器的数据库中。相同账户注册时,会从服务器进行查询,如果查到账号已存在则不能完成二次注册,而在登录时,我通过验证用户输入的用户名和密码与数据库中存储的信息是否匹配来实现用户的身份认证。
ip及端口选择:使用复选框以及相关信号槽机制等,完成用户可自主设置选择ip和端口,增强了系统的完整性和灵活性。
群发信息:每当一个客户端链接时,服务器会将其保存到表中,客户端发送信息给服务器,服务器遍历转发。从而实现一对多的信息交互。
历史记录查找:服务端将收到的消息存储到服务器端的数据库中,以便后续查找使用。用户可以调用相关函数接口完成对消息记录的查询。


架构设计

 服务器架构


 模块划分


网络模块: 负责监听客户端的连接请求,接受客户端的消息,并将消息转发给其他客户端。
用户管理模块: 负责管理用户的登录、退出,以及在线用户列表的维护。
消息处理模块: 负责对接收到的消息进行解析和处理,比如区分做什么指令等的处理逻辑。
持久化模块: 负责将用户信息、聊天记录等数据持久化到数据库中,以便实现用户信息的持久存储。

 模块之间的交互


网络模块与消息处理模块: 网络模块接收到消息后,将消息传递给消息处理模块进行解析和处理,然后再根据消息类型进行相应的转发操作。
用户管理模块与持久化模块: 当用户登录或登出时,用户管理模块需要与持久化模块进行交互,将用户信息同步到数据库中。

 客户端架构

 模块划分


用户界面模块: 负责展示用户界面,包括登录注册页面、聊天页面、查询记录界面等。
网络通信模块: 负责与服务器端进行通信,发送消息、接收消息等操作。
用户行为响应模块: 监听用户在界面上的操作,比如发送消息、点击选择发送对象、文件传输等,然后触发相应的响应动作。

模块之间交互


用户界面模块与网络通信模块: 用户界面模块需要通过网络通信模块来发送用户输入的消息,并接收服务器端返回的消息。
用户界面模块与用户行为响应模块: 用户界面上的各种操作会触发用户行为响应模块中的相应函数,从而实现用户行为与系统操作的响应。

 项目展示

项目实现

服务器

ui

server.pro

#-------------------------------------------------
#
# Project created by QtCreator 2024-10-22T16:49:57
#
#-------------------------------------------------QT       += core gui network sql
RC_FILE += icon_config.rc
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = server
TEMPLATE = appSOURCES += main.cpp\dialog.cppHEADERS  += dialog.hFORMS    += dialog.uiRESOURCES += \res.qrcDISTFILES += \icon_config.rc

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QTcpServer> // 服务器管理类
#include <QDebug>
#include <QDateTime>
#include <QTcpSocket> // 网络连接类
#include <QTextStream> // 文本流#include <QButtonGroup>
#include <QMessageBox>
#include <QSqlDatabase> // 数据库连接类
#include <QSqlError> // 错误信息类
#include <QSqlQuery> // 数据库操作类
#include<QPixmap>
#include<QPainter>//画家对象namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();protected://创建画家对像  注意就是这个名字 发生倾斜就是对了void paintEvent(QPaintEvent *);private:Ui::Dialog *ui;QTcpServer* server;QTcpSocket* socket = NULL; // 下的蛋QList<QTcpSocket*>clients;void printMsg(QString); // 在公屏显示信息void connect2DB();QSqlDatabase db; // 数据库连接对象void createChatTable(); // 建聊天记录表void createLoginTable();//建登录信息表void selectTime(QString,int i);void selectNickname(QString);void selectMsg(QString);void selectAll();bool isDataExists(QString id); // 判断某个id的数据在不在bool isLoginExists(QString id,QString password); //判断账号密码信息private slots:void newConnectionSlot(); // 建立新连接的槽函数!void readyReadSlot(); // 准备读的槽函数void disconnSlot();  //连接断开的槽函数};#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include <QThread>Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{ui->setupUi(this);// 界面置顶setWindowFlags(Qt::WindowStaysOnTopHint);server = new QTcpServer(this);// 开启监听bool result = server->listen(QHostAddress::Any,7456);if(result){printMsg("监听开启成功,端口号7456");connect(server,SIGNAL(newConnection()),this,SLOT(newConnectionSlot()));}elseprintMsg("监听开启失败!");//连接数据库connect2DB();
}Dialog::~Dialog()
{// 关闭server->close();// 如果数据库连接还在打开,则关闭if(db.isOpen())db.close();delete ui;
}void Dialog::paintEvent(QPaintEvent *)
{///创建画家对象/// 参数为哪儿画,继承QPaintDevice的类对象才能被绘制QPainter painter(this);//创建一个要画的图片对象QPixmap pic(":/new/prefix1/bd.jpg");//开始绘制//参数1/2:绘制的坐标;//参数3&4:绘制的宽高;//参数5:绘制的内容;painter.drawPixmap(0,0,this->width(),this->height(),pic);//使lineEdit控件透明ui->textBrowser->setStyleSheet("QTextBrowser { background-color: transparent; }");ui->lineEditCount->setStyleSheet("QLineEdit { background-color: transparent; }");
}
/*** @brief Dialog::printMsg 在公屏上显示信息* @param msg 要显示的信息*/
void Dialog::printMsg(QString msg)
{// 获得日期时间QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");// 显示到公屏上ui->textBrowser->append(dt);ui->textBrowser->append(msg);
}//创建并连接数据库
void Dialog::connect2DB()
{// 创建连接对象db = QSqlDatabase::addDatabase("QSQLITE");// 设置数据库文件名称db.setDatabaseName("chat_msg.db");// 打开数据库连接if(db.open()){qDebug() << "连接成功!";// 建表createLoginTable();createChatTable();}else{// 获得错误细信息封装类QSqlError info = db.lastError();// 提取错误信息QString text = info.text();// 展示错误QMessageBox::critical(this,"错误",text);}
}//创建注册登录表
void Dialog::createLoginTable()
{QString sql = "CREATE TABLE login_msg(user_name TEXT,password TEXT);";// 创建数据库操作类对象QSqlQuery sq;if(sq.exec(sql)) // 如果执行成功{qDebug() << "建表成功!";}else //失败{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString text = info.text();qDebug() << "建表失败!" << text;}
}//创建聊天表
void Dialog::createChatTable()
{QString sql = "CREATE TABLE chat(dt TEXT,nickname TEXT,msg TEXT);";// 创建数据库操作类对象QSqlQuery sq;if(sq.exec(sql)) // 如果执行成功{qDebug() << "建表成功!";}else //失败{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString text = info.text();qDebug() << "建表失败!" << text;}
}//新连接
void Dialog::newConnectionSlot()
{socket=server->nextPendingConnection();QTextStream output(socket);if(clients.size()>=20){qDebug()<<"客户端已满";output<<QString("客户端已满,连接失败!");socket->close();}printMsg("新连接已建立!");clients.append(socket);//显示连接数ui->lineEditCount->setText(QString::number(clients.size()));// 读取消息的信号槽connect(socket,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));//掉线的信号槽connect(socket,SIGNAL(disconnected()),this,SLOT(disconnSlot()));// 拿到对面的IP地址和端口号并展示QString ip = socket->peerAddress().toString();quint16 port = socket->peerPort();QString text = ip.append(":") + QString::number(port);printMsg(text.append(" 已上线"));ui->textBrowser->append("");//给客户端打招呼output << QString("欢迎来到聊天室!");
}//读
void Dialog::readyReadSlot()
{socket=(QTcpSocket*)sender();for(int i=0;i<clients.size();i++){
//        if(socket==clients.at(i))if(clients.at(i)->isReadable() && clients.at(i)->bytesAvailable()>0){//建立读通道QTextStream input(clients.at(i));QString text = input.readAll();//建立写通道QTextStream output(socket);//将读到的信息格式处理QStringList list = text.split(":");//注册信息,服务器接收通过“:”拆分长度为2if(list.size()==2){QString id=list.value(0);QString password=list.value(1);if(isDataExists(id)){//账号已存在,不能注册output << QString("账号已存在");qDebug()<<"账号已存在";}else{// 预处理的SQL语句QString sql = "INSERT INTO login_msg VALUES(?,?)";QSqlQuery sq;sq.prepare(sql); // 预处理// 绑定参数sq.addBindValue(id);sq.addBindValue(password);// 执行绑定后的SQL语句if(sq.exec()){output<<QString("注册成功");qDebug()<<"注册数据插入成功!";}else //失败{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("数据插入操作失败!");QMessageBox::warning(this,"通知",msg);}}}//服务器接收通过“:”拆分长度为3if(list.size()==3){if(list.value(0)=="按时间查询"){QString datatime=list.value(1);selectTime(datatime,i);}else if(list.value(0)=="按昵称查询"){QString nickname=list.value(1);selectNickname(nickname);}else if(list.value(0)=="按消息内容查询"){QString msg=list.value(1);selectMsg(msg);}else{//登录QString user_name = list.value(0);QString password = list.value(1);//账号密码正确,登陆成功if(isLoginExists(user_name,password)){output << QString("登录成功");}else{output<<QString("登录失败");}}}//收到聊天信息,服务器接收通过“:”拆分长度为4if(list.size()==4){// 获得用户输入数据QString dt= list.value(0);QString nickname = list.value(1);QString msg = list.value(2);// 预处理的SQL语句QString sql = "INSERT INTO chat VALUES(?,?,?)";QSqlQuery sq;sq.prepare(sql); // 预处理// 绑定参数sq.addBindValue(dt);sq.addBindValue(nickname);sq.addBindValue(msg);// 执行绑定后的SQL语句if(sq.exec()){qDebug()<<"聊天数据插入成功!";//发送到所有用户聊天界面for(int m=0;m<clients.size();m++){if(m==i)continue;socket=clients.at(m);QTextStream output2(clients.at(m));output2<<dt<<":"<<nickname<<":"<<msg;//客户端接收通过“:”拆分长度为3}}else //失败{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("聊天数据插入操作失败!");QMessageBox::warning(this,"通知",msg);}}//客户端接收通过“:”拆分长度为1if(list.size()==1){//查询所有聊天记录selectAll();}}}
}//判断id存不存在
bool Dialog::isDataExists(QString id)
{//    QString idText = QString::number(id);QString sql = "SELECT * FROM login_msg WHERE user_name=";sql.append(id); // 拼接,只为证明也可以,但不建议QSqlQuery sq;if (sq.exec(sql)){bool found = false;  //通过设定状态验证时候查询到数据while (sq.next())   //查询不到消息,sq.next()为零{if (!found){found = true;}return true;}if (!found){return false;}}
}//查询账号密码
bool Dialog::isLoginExists(QString user_name, QString password)
{QString sql = "SELECT * FROM login_msg WHERE user_name=? AND password=?";QSqlQuery sq;sq.prepare(sql);sq.addBindValue(user_name);sq.addBindValue(password);if(sq.exec()) // 查询成功{return sq.next(); // 直接返回有无数据}else{return false;}
}//按时间查询
void Dialog::selectTime(QString dt,int i)
{QTextStream output(clients.at(i));QString sql = "SELECT * FROM chat WHERE dt LIKE?";QSqlQuery sq;sq.prepare(sql);sq.addBindValue(dt);if (sq.exec()){bool found = false;  //通过设定状态验证时候查询到数据while (sq.next())   //查询不到消息,sq.next()为零{if (!found){found = true;}QString time = sq.value(0).toString();QString nickname = sq.value(1).toString();QString msg = sq.value(2).toString();output << time << ":" << nickname << ":" << msg << ":" << "end";}if (!found){qDebug() << "未查询到符合条件的记录。";output<<QString("查询失败");     //客户端接收通过“:”拆分长度为1}}else{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("查询操作失败!");QMessageBox::warning(this,"通知",msg);}
}//按昵称查询
void Dialog::selectNickname(QString nickname)
{QTextStream output(socket);// 预处理的SQLQString sql = "SELECT * FROM chat WHERE nickname LIKE ?";QSqlQuery sq;// 预处理sq.prepare(sql);sq.addBindValue(nickname);if (sq.exec()){bool found = false;while (sq.next()){if (!found){found = true;}// 取出一条记录中的每个字段值QString time = sq.value(0).toString();QString nickname = sq.value(1).toString();QString msg = sq.value(2).toString();output<<time<<":"<<nickname<<":"<<msg<<":"<<"end";}if (!found){qDebug() << "未查询到符合条件的记录。";output<<QString("查询失败");  //客户端接收通过“:”拆分长度为1}}else{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("查询操作失败!");QMessageBox::warning(this,"通知",msg);}
}//按消息内容查询
void Dialog::selectMsg(QString msg)
{QTextStream output(socket);// 预处理的SQLQString sql = "SELECT * FROM chat WHERE msg LIKE ?";QSqlQuery sq;// 预处理sq.prepare(sql);sq.addBindValue(msg.prepend("%").append("%")); // 通配符if (sq.exec()){bool found = false;while (sq.next()){if (!found){found = true;}// 取出一条记录中的每个字段值QString time = sq.value(0).toString();QString nickname = sq.value(1).toString();QString message = sq.value(2).toString();output<<time<<":"<<nickname<<":"<<message<<":"<<"end";}if (!found){qDebug() << "未查询到符合条件的记录。";output<<QString("查询失败");}}else{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("查询操作失败!");QMessageBox::warning(this,"通知",msg);}
}//查询全部聊天记录
void Dialog::selectAll()
{QTextStream output(socket);// 预处理QString sql = "SELECT * FROM chat";QSqlQuery sq;if (sq.exec(sql)){bool found = false;while (sq.next()){if (!found){found = true;}// 取出一条记录中的每个字段值QString time = sq.value(0).toString();QString nickname = sq.value(1).toString();QString message = sq.value(2).toString();output<<time<<":"<<nickname<<":"<<message<<":"<<"end";}if (!found){qDebug() << "未查询到符合条件的记录。";output<<QString("查询失败");}}else{// 获得错误细信息封装类QSqlError info = sq.lastError();// 提取错误信息QString msg = info.text();msg.prepend("查询操作失败!");QMessageBox::warning(this,"通知",msg);}
}//某一客户端退出
void Dialog::disconnSlot()
{socket=(QTcpSocket*)sender();for(int i=0;i<clients.size();i++){if(socket==clients.at(i)){// 拿到对面的IP地址和端口号并展示QString ip = clients.at(i)->peerAddress().toString();quint16 port = clients.at(i)->peerPort();QString text = ip.append(":") + QString::number(port);printMsg(text.append(" 已下线"));clients.removeAt(i);//及时修改在线人数ui->lineEditCount->setText(QString::number(clients.size()));}}
}

客户端

ui

cient.pro 

#-------------------------------------------------
#
# Project created by QtCreator 2024-10-22T16:51:03
#
#-------------------------------------------------QT       += core gui network
RC_FILE +=icon_config.rcgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = client
TEMPLATE = appSOURCES += main.cpp\dialog.cppHEADERS  += dialog.hFORMS    += dialog.uiRESOURCES += \res.qrcDISTFILES += \icon_config.rc

dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QTcpSocket> // TCP连接
#include <QMessageBox>
#include <QTextStream> // 文本流
#include <QDateTime>
#include<QPixmap>
#include<QPainter>//画家对象namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();protected://创建画家对像  注意就是这个名字 发生倾斜就是对了void paintEvent(QPaintEvent *);private:Ui::Dialog *ui;QTcpSocket* client; // 客户端连接对象private slots:void btnConnClickedSlot();//连接槽函数void connectedSlot(); // 连接成功的槽函数void disconnectedSlot(); // 连接断开的槽函数void registerSlot();   //注册槽函数void loginSlot();    //登录槽函数void btnSendClickedSlot();//发送聊天信息槽函数void readyReadSlot();   //读信号槽void btnHistoryCickedSlot();//查询历史记录槽函数void onComboBoxTextChanged( QString ); //选择查询方式的槽函数void btnChagepageSlot();  //退回到第一界面槽函数void btnChagepageSlot2(); //退回到第二界面槽函数
};#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{ui->setupUi(this);// 界面置顶setWindowFlags(Qt::WindowStaysOnTopHint);//设置默认界面ui->stackedWidget->setCurrentIndex(0);//点击连接connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnClickedSlot()));client = new QTcpSocket(this);// 连接状态监测信号槽connect(client,SIGNAL(connected()),this,SLOT(connectedSlot()));connect(client,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));//点击注册connect(ui->pushButtonRegister,SIGNAL(clicked()),this,SLOT(registerSlot()));//点击登录connect(ui->pushButtonLogin,SIGNAL(clicked()),this,SLOT(loginSlot()));//点击发送connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));//点击查询历史记录connect(ui->pushButtonHis,SIGNAL(clicked()),this,SLOT(btnHistoryCickedSlot()));//点击切换前一界面connect(ui->pushButtonEnd,SIGNAL(clicked()),this,SLOT(btnChagepageSlot()));connect(ui->pushButtonEHis,SIGNAL(clicked()),this,SLOT(btnChagepageSlot2()));
}Dialog::~Dialog()
{// 断开信号槽disconnect(client,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));// 主动断开if(client->isOpen())client->close();delete ui;
}void Dialog::paintEvent(QPaintEvent *)
{///创建画家对象/// 参数为哪儿画,继承QPaintDevice的类对象才能被绘制QPainter painter(this);//创建一个要画的图片对象QPixmap pic(":/new/prefix1/bd.jpg");//开始绘制//参数1/2:绘制的坐标;//参数3&4:绘制的宽高;//参数5:绘制的内容;painter.drawPixmap(0,0,this->width(),this->height(),pic);//使lineEdit控件透明ui->lineEditPassword->setStyleSheet("QLineEdit { background-color: transparent; }");ui->lineEdit_Id->setStyleSheet("QLineEdit { background-color: transparent; }");ui->lineEdit_IP->setStyleSheet("QLineEdit { background-color: transparent; }");ui->textBrowser->setStyleSheet("QTextBrowser { background-color: transparent; }");}
void Dialog::btnConnClickedSlot()
{// 获取用户输入的IP地址和端口号// 【升级】验证ip地址输入格式的有效性QString ip = ui->lineEdit_IP->text();if(ip==""){QMessageBox::warning(this,"提示","请输入ip!");return;}quint16 port = ui->spinBox_2->value();if(port==0){QMessageBox::warning(this,"提示","请输入端口号!");return;}// 连接到服务器client->connectToHost(ip,port);
}//连接
void Dialog::connectedSlot()
{// 屏蔽连接按键,释放发送按键ui->pushButtonConn->setEnabled(false);ui->pushButtonConn->setText("已连接");//打开读功能connect(client,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));
}//断开连接
void Dialog::disconnectedSlot()
{// 释放连接按键ui->pushButtonConn->setEnabled(true);ui->pushButtonConn->setText("连接");QMessageBox::warning(this,"提示","连接已断开!");
}//读
void Dialog::readyReadSlot()
{QTextStream input(client);QString text = input.readAll();QStringList list = text.split(":");if(list.size()<4){if(text=="欢迎来到聊天室!")QMessageBox::information(this,"连接成功",text);if(text=="账号已存在")QMessageBox::warning(this,"提示","账号已存在,请重新注册!");if(text=="注册成功")QMessageBox::information(this,"成功","注册成功!请登录!!");if(text=="登录失败")QMessageBox::warning(this,"提示","用户名或密码错误!");if(text=="查询失败")QMessageBox::warning(this,"提示","查不到相关信息!");if(text=="登录成功")//切换到第二界面ui->stackedWidget->setCurrentIndex(1);//将所有用户聊天信息显示到公屏上if(list.size()==3){QString dt=list.value(0);QString nickname=list.value(1);QString msg=list.value(2);QString Msg=nickname+":"+msg;ui->textBrowser->append(dt);ui->textBrowser->append(Msg);}}else{QStringList list2 = text.split("end",QString::SkipEmptyParts);for(int j=0;j<list2.size();j++){QStringList list3 = list2.value(j).split(":");QString time=list3.value(0);QString name=list3.value(1);QString msg=list3.value(2);QString xinxi=time.append("\n").append(name).append(":").append(msg);ui->textBrowserHis->append(xinxi);}}
}//注册
void Dialog::registerSlot()
{//检查是否连接if(ui->pushButtonConn->text()=="连接"){QMessageBox::warning(this,"提示","请先连接!!!");return;}//获取输入的账号密码QString id=ui->lineEdit_Id->text();if(id == ""){QMessageBox::warning(this,"提示","请输入账号!");return;}QString password=ui->lineEditPassword->text();if(password == ""){QMessageBox::warning(this,"提示","请输入密码!");return;}//发送到服务器存入数据库QTextStream output(client);output << id<<":"<<password;  //服务器接收通过“:”拆分长度为2
}
//登录
void Dialog::loginSlot()
{//检查是否连接if(ui->pushButtonConn->text()=="连接"){QMessageBox::warning(this,"提示","请先连接!!!");return;}//获取输入的账号密码QString id=ui->lineEdit_Id->text();if(id == ""){QMessageBox::warning(this,"提示","请输入账号!");return;}QString password=ui->lineEditPassword->text();if(password == ""){QMessageBox::warning(this,"提示","请输入密码!");return;}//发给服务器验证QTextStream output(client);output << id<<":"<<password<<":"<<"end";  //服务器接收通过“:”拆分长度为3
}//发送聊天消息
void Dialog::btnSendClickedSlot()
{// 获得用户输入的消息QString user = ui->lineEditUser->text();if(user == ""){QMessageBox::warning(this,"提示","请输入昵称!");return;}QString msg = ui->plainTextEdit->toPlainText();if(msg == ""){QMessageBox::warning(this,"提示","请输入消息内容!");return;}if(msg.size() > 128){QMessageBox::warning(this,"提示","消息内容过长!");return;}// 获得日期时间QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm");// TODO 给服务器发信息QTextStream output(client);output << dt<<":"<<user<<":" << msg<<":"<<"end"; //服务器接收通过“:”拆分长度为4QString xinxi=dt.append("\n").append(user).append("(本机):").append(msg);ui->textBrowser->append(xinxi);ui->plainTextEdit->clear();
}//查询历史记录
void Dialog::btnHistoryCickedSlot()
{//切换为第三页面ui->stackedWidget->setCurrentIndex(2);//选择查询类型connect(ui->comboBox, SIGNAL(activated(QString)),this, SLOT(onComboBoxTextChanged(QString)));
}//查询类型的槽函数
void Dialog::onComboBoxTextChanged(QString  text)
{//清空上一次查询的内容ui->textBrowserHis->clear();;//建立写通道QTextStream output(client);if(text=="查询所有聊天记录"){output<<text;       //服务器接收通过“:”拆分长度为1}else{//获取查询的内容QString msg=ui->plainTextEditHis->toPlainText();if(msg==""){QMessageBox::warning(this,"提示","请输入查询内容!");return;}output<<text<<":"<<msg<<":"<<"end"; //服务器接收通过“:”拆分长度为3}
}
//切换到第一界面
void Dialog::btnChagepageSlot()
{ui->stackedWidget->setCurrentIndex(0);
}
//切换到第二界面
void Dialog::btnChagepageSlot2()
{ui->stackedWidget->setCurrentIndex(1);
}

打包步骤不做演示

视频演示

http://【Qt聊天室】 https://www.bilibili.com/video/BV1uW1MYbEZz/?share_source=copy_web&vd_source=989c862553a9fc170dee4fdcac7a0592icon-default.png?t=O83Ahttp://【Qt聊天室】 https://www.bilibili.com/video/BV1uW1MYbEZz/?share_source=copy_web&vd_source=989c862553a9fc170dee4fdcac7a0592

————源码已上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/2959.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

node.js模块化分析

什么是Node.js模块化 Node.js中的模块化‌是指将一个大文件拆分成独立且相互依赖的多个小模块。每个JS文件被视为一个独立的模块&#xff0c;模块之间是互相不可见的。如果一个模块需要使用另一个模块&#xff0c;则需要使用指定的语法来引入该模块&#xff0c;并且只能使用模块…

sql练习专场(一) (1-5)

这是总结的一些sql题目&#xff0c;共25道题&#xff0c;每个博客会写5道题 第一题 这道题需要找出连续活跃3天以上的用户&#xff0c;其中每个用户每天可以连续登录多次。 create table sql1_1(uid string,dt string );insert into sql1_1 values(A,2023-10-01),(A,2023…

自动化立体仓库:详细设计方案

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 这份文件是关于自动化立体仓库设计方案的详细介绍&#xff0c;包括了自动化立体仓库的重要性、特点、设计程序、机械部分设计方案、系统硬件设计、系统软件设计以及系统调试等关键部分…

windows运行ffmpeg的脚本报错:av_ts2str、av_ts2timestr、av_err2str => E0029 C4576

问题描述 我目前的环境是&#xff1a; 编辑器&#xff1a; Microsoft Visual Studio Community 2022 (64 位) 运行的脚本是ffmpeg自带的remux样例&#xff0c;只不过我想用c语言执行这个样例。在执行的过程中报错如下图&#xff1a; C4576 后跟初始值设定项列表的带圆括…

CentOS 文件系统扩容与缩容

一、 概述 理解Linux文件系统的管理&#xff0c;需要了解以下的一张图&#xff1a; 一般使用LVM (Logical Volume Manager) 管理磁盘存储&#xff0c;该工具允许用户更灵活地分配和管理存储空间。主要有以下几个概念&#xff1a; PV&#xff08;Physical Volume&#xff0c;物…

Linux系统使用第三方邮件客户端发送邮件

文章目录 安装第三方邮件客户端&#xff08;s-nail&#xff09;S-nail的简单介绍重要的特性差异 配置邮件服务配置文件 (以QQ邮箱为例)获取QQ邮箱授权码获取QQ服务器证书使用 OpenSSL 获取 QQ 邮箱服务器的证书安装OpenSSL连接到 QQ 邮箱的 SMTP 服务器并下载证书保存证书验证证…

家常菜点餐|基于java和小程序的家庭大厨家常菜点餐系统设计与实现(源码+数据库+文档)

家常菜点餐系统 目录 基于java和小程序的家庭大厨家常菜系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&am…

利士策分享,青年暴富难守,因何在?

利士策分享&#xff0c;青年暴富难守&#xff0c;因何在? 在人生的长河中&#xff0c;有些人似乎被命运特别眷顾&#xff0c;在年轻之时便轻易地获得了财富。 然而&#xff0c;令人遗憾的是&#xff0c;这些早年得志、财富易得的人&#xff0c;往往难以长久地守住这份来之不…

Echarts环形图引线设置

直接上图吧 直接上代码吧 let labelArr [直接访问, 邮件营销, 联盟广告, 视频广告, 搜索引擎]; let valueArr [{ value: 335, name: 直接访问 },{ value: 310, name: 邮件营销 },{ value: 234, name: 联盟广告 },{ value: 135, name: 视频广告 },{ value: 154, name: 搜索引…

Java8->Java19的初步探索

导读 最近网上开始了大量的关于Java19的讨论&#xff0c;我也想着用了Java8这么久该接受一点新的东西了&#xff0c;于是便开始研究了起来 Java 19 Java19是一个免费版本。下面是JDK19的支持图 image.png &#xff08;来源&#xff1a; https://www.bilibili.com/video/BV1V84…

软件设计师-上午题-15 计算机网络(5分)

计算机网络题号一般为66-70题&#xff0c;分值一般为5分。 目录 1 网络设备 1.1 真题 2 协议簇 2.1 真题 3 TCP和UDP 3.1 真题 4 SMTP和POP3 4.1 真题 5 ARP 5.1 真题 6 DHCP 6.1 真题 7 URL 7.1 真题 8 浏览器 8.1 真题 9 IP地址和子网掩码 9.1 真题 10 I…

nodejs批量修改word文档目录样式

工作中遇到一个需求:写个nodejs脚本,对word文档(1000+个)的目录页面进行美化。实现过程遇到不少麻烦,在此分享下。 整体思路 众所周知,Docx格式的Word文档其实是个以xml文件为主的zip压缩包,所以,页面美化整体思路是:先将文档后缀名改为zip并解压到本地,然后将关键的…

MathType在Word中的安装与配置记录

一、记录过程 1.MathType安装包下载 可直接下载本人已经安装过的安装包&#xff0c;亲测可以使用&#xff0c;下载链接如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1g-iOgKIqzSNz0E5rEUryug 提取码&#xff1a;1kb3 2.安装后配置 word中会出现mathtype的选项…

无人机之中继通信技术篇

一、定义与原理 无人机中继通信技术是指通过无人机搭载中继设备&#xff0c;将信号从一个地点传输到另一个地点&#xff0c;从而延长通信距离并保持较好的通信质量。其原理类似于传统的中继通信&#xff0c;即在两个终端站之间设置若干中继站&#xff0c;中继站将前站送来的信号…

轴流风机和后倾式风机的安装要求

后向离心风机风压大&#xff0c;风量足&#xff0c;安装方便。因为不需要蜗壳&#xff0c;所以风道往往需要自行设计&#xff0c;而风道的合理与否&#xff0c;大大影响了后向离心风机的效率。那么后向离心风机的安装技巧有哪些&#xff1f;怎样达到风机的最佳使用效果呢&#…

植物神经紊乱不用怕,这些维生素来帮你!

你是否经常感到身体疲惫、情绪波动大、心悸、胸闷&#xff1f;这可能是植物神经紊乱在作祟。别担心&#xff0c;通过合理的维生素补充&#xff0c;可以有效缓解症状&#xff0c;提升生活质量。今天&#xff0c;我们就来聊聊植物神经紊乱患者应该补充哪些维生素。 &#x1f50d…

使用C语言进行信号处理:从理论到实践的全面指南

1. 引言 在现代操作系统中&#xff0c;信号是一种进程间通信机制&#xff0c;它允许操作系统或其他进程向一个进程发送消息。信号可以用来通知进程发生了一些重要事件&#xff0c;如用户请求终止进程、硬件异常、定时器超时等。掌握信号处理技术对于开发健壮、高效的系统程序至…

LabVIEW配电产品精度测试系统

开发了一种基于LabVIEW平台的配电产品精度测试系统&#xff0c;通过自动化测试流程实现更高的测试准确性与效率。系统采用串口和TCP通信技术&#xff0c;与多功能交流采样变送器和配电设备无缝数据交互&#xff0c;提升了测试工作的可靠性和一致性。 一、项目背景 在配电产品…

基于JAVA SpringBoot和Vue社区网格化管理服务平台设计

摘要 本文旨在设计并实现一个基于Java SpringBoot和Vue技术的社区网格化管理服务平台。该平台主要包括用户功能和管理员功能两大部分&#xff0c;用户功能涵盖单位管理、问卷调查、论坛讨论、公告查看等&#xff1b;管理员功能则包括单位管理、基础数据维护、帖子和公告类型管…

鸢尾博客项目开源

1.博客介绍 鸢尾博客是一个基于Spring BootVue3 TypeScript ViteJavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限&#xff0c;服务健康…