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.mzph.cn/bicheng/59369.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB实现图像恢复设计报告

设计目标及需求分析 设计目标&#xff1a;希望通过matlab设计一个软件来实现对CT图像的模糊再恢复的过程&#xff0c;是对现实中CT图像复原的一个简单仿真。 需求分析&#xff1a;随着网络和通信技术的发展&#xff0c;数字图像处理与分析技术已经在科学研究、工业生产、医疗…

Python毕业设计选题:基于django+vue的4S店客户管理系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 员工信息管理 个人中心 车辆信息管理 售后服务管理 售后安排管理 车辆信…

Java基础06(代码运行时的内存图)

目录 一、引入 二、Java下的内存分配 1.类信息常量池和静态常量池 2. 栈和字符串常量池 &#xff08;引进&#xff09;线程 3.⭐程序计数器作用⭐&#xff08;程序计数器配合栈使用&#xff09; 总结Java内存&#xff1a; &#xff08;引进&#xff09;驱动 三、引用传…

论文阅读-用于点云分析的自组织网络

目前存在的问题&#xff1a; 原始的SOM&#xff08;1&#xff09;训练结果与初始节点高度相关&#xff08;2&#xff09;样本更新规则取决于输入点的顺序3D 卷积神经网络&#xff08;需要将数据转换为体素&#xff0c;存在分辨率损失和计算成本上涨的问题&#xff09;、PointN…

数据结构之二叉树前序,中序,后序习题分析(递归图)

1.比较相同的树 二叉树不能轻易用断言&#xff0c;因为树一定有空 2.找结点值 3.单值二叉树 4.对称二叉树 5.前序遍历

【JavaScript】JavaScript开篇基础(4)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

运动控制 直流有刷电机

文章目录 一、简介二、组成三、特点四、电机参数4.1 额定电压4.2 额定电流4.3 额定转速4.4 额定扭矩4.5 减速比 五、工作原理5.1 左手定则5.2 工作原理 六、测速原理6.1 磁电式编码器6.2 光电式编码器 一、简介 直流有刷电机是一种内含电刷装置能将直流电转换为机械能的电机&a…

关于模拟方法建模的一份介绍

有些时候&#xff0c;我们无法直接收集大量数据&#xff0c;即对于对象的行为直接观测或重复实验可能是不行的&#xff0c;所以此时就需要通过模拟的技术来收集数据&#xff0c;然后建模。这样的一种策略就是模拟方法建模&#xff0c;而模拟方法建模中最常用的一种方法就是蒙特…

Java 入门

目录 Java简介 Java JDK开发环境配置 第一个Java程序 Java标识符与关键字 Java注释 Java常量 Java变量的定义和使用 Java简介 Java简介&#xff1a; Java是由Sun Microsystems公司于1995年推出的一门面向对象的高级程序设计语言&#xff0c;可以运行于多个平台&#xff0c;其…

CSS--两列网页布局,三列布局和多行多列布局

两列网页布局 两列网页布局实验 先将一个未运用浮动效果的网页结构写出来 <style>header{/* 给页眉设置宽高和样式 */width:1000px;height: 40px;background-color: gray;border: 3px brown solid;margin-bottom: 5px;}article{width:1000px;height: 600px;background-c…

开源与商业的碰撞TPFLOW与Gadmin低代码的商业合作

随着数字化转型的浪潮席卷全球&#xff0c;企业对于高效、灵活的软件开发需求愈发迫切。低代码开发平台应运而生&#xff0c;为企业提供了简化开发流程、缩短开发周期的解决方案。在众多低代码开发平台中&#xff0c;Gadmin企业级低代码平台、TPFLOW工作流和SFDP超级表单脱颖而…

Edge浏览器提示“无法安全下载”

Edge浏览器在下载某些文件时&#xff0c;会提示“无法安全下载”。 注意事项&#xff1a;如果确实需要下载该文件&#xff0c;首先核对网址&#xff0c;确保下载文件的安全性&#xff0c;并在下载完成后进行必要的病毒查杀。 解决方法&#xff1a; 点击右侧的3个点&#xff0…

微服务系列三:微服务核心——网关路由

目录 前言 一、登录存在的问题归纳 二、*微服务网关整体方案 三、认识微服务网关 四、网关鉴权实现 五、OpenFeign微服务间用户标识信息传递实现 六、微服务网关知识追问巩固 前言 本篇文章具体讲解微服务中网关的实现逻辑、用于解决什么样的问题。其中标题中标注* 涉…

Docker入门系列——网络

Docker 通过容器化应用程序&#xff0c;彻底改变了我们构建、分发和运行应用程序的方式。然而&#xff0c;有效使用 Docker 的一个关键方面是理解容器如何相互通信以及与外界通信。 1. 什么是 Docker 网络&#xff1f; Docker 网络允许容器相互通信以及与外部资源通信。默认情况…

2024年大厂AI大模型面试题精选与答案解析

前言 随着AI市场&#xff0c;人工智能的爆火&#xff0c;在接下来的金九银十招聘高峰期&#xff0c;各大科技巨头和国有企业将会对AGI人才的争夺展开一场大战&#xff0c;为求职市场注入了新的活力。 为了助力求职者在面试中展现最佳状态&#xff0c;深入理解行业巨头的选拔标…

Nico,从零开始干掉Appium,移动端自动化测试框架实现

开头先让我碎碎念一波~去年差不多时间发布了一篇《 UiAutomator Nico&#xff0c;一个基于纯 adb 命令实现的安卓自动化测试框》&#xff08;https://testerhome.com/topics/37042&#xff09;&#xff0c; 由于种种原因 (详见此篇帖子) 当时选择了用纯 adb 命令来实现安卓自动…

RTP和RTCP的详细介绍及其C代码示例

RTP和RTCP的详细介绍及其C代码示例 RTP和RTCP简介RTP协议详解RTCP协议详解RTP和RTCP之间的关系C代码示例RTP和RTCP简介 RTP(Real-time Transport Protocol,实时传输协议)和RTCP(Real-time Transport Control Protocol,实时传输控制协议)是流媒体传输中常用的两个协议。R…

国内能用的Docker镜像源【2024最新持续更新】

国内能用的Docker镜像源【2024最新持续更新】 Docker 镜像加速列表&#xff08;2024年11月已更新&#xff09;配置方式1&#xff1a;临时使用配置方式2&#xff1a;长久有效 在国内使用 Docker 的朋友们&#xff0c;可能都遇到过配置镜像源来加速镜像拉取的操作。然而&#xff…

队列(Queue)的介绍与实现

文章目录 队列队列的概念及结构 队列的实现初始化队列销毁队列队尾入队列队头出队列获取队列头部元素检测队列是否为空获取队列中有效元素个数 队列 队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。队列遵…

3.1 快速启动Flink集群

文章目录 1. 环境配置2. 本地启动3. 集群启动4. 向集群提交作业4.1 提交作业概述4.2 添加打包插件4.3 将项目打包4.4 在Web UI上提交作业4.5 命令行提交作业 在本实战中&#xff0c;我们将快速启动Apache Flink 1.13.0集群&#xff0c;并在Hadoop集群环境中提交作业。首先&…