简介: 自我熟练qt
的widget
的使用,熟悉使用qt
的network
相关模块写一个仿QQ
的QQ简洁版2019
群聊项目。哇伊,这是我大学之处一直想写的IM
即时通讯系统的,模仿写一个QQ
的项目,但是因为时间等关系,断断续续的只是写了一些IM_QQ
的部分相关功能的知识,每回写一个核心功能,但到了现在这会,感觉基本几大核心功能(登录,单人聊天,群聊功能,数据库设计)已经写好了,后续有时间,就将其完整的写成一个完整版的QQ
。
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
开发环境:
编程环境: win10 x64 专业版
编程软件: visual studio 2015
, Qt Creator 4.8.2 (Enterprise)
, Qt 5.9.7
项目简介:
使用qt
实现QQ
的核心功能之一:群聊功能。
功能实现:
群聊功能
群成员上线,下线会有自动提示
群成员在线动态列表
联系人好友列表
聊天记录保存到*.txt
清空聊天界面
聊天字体的变化:字体、字号、颜色、加粗、倾斜、下划线
项目特色:
写这个,不仅仅是一个单独的小功能,而是在学习的前进的路上,逐步完善核心功能,最后独立写一个完整版的即时通讯IM,准备以QQ
为参考。若是以后写出来了,是会发表称为系列博客,供大家开源学习与交流的
仿QQ项目的IM即时通讯,已经实现的相关的功能:
QQ的单人聊天: 项目实战:Qt5/C++:TCP的C/S的聊天小程序
QQ的登录界面: 项目实战:Qt5/Quick:模仿扣扣登录界面
QQ的群聊功能:本文
QQ的数据库设计:已经设计好,还未整理为博客
任重而道远,后续还有服务器的高并发等学习,为我的IM奠定基础,不慌不躁,心虽急,但是干活不可急,始终坚信:
慢而不出差错,就是快
运行效果:
视频演示效果:
图片演示效果:
- 联系人列表:
群聊界面:
设计思路:
- 输入准备发送的消息到控件
- 槽函数获获取控件消息,转换换成
QString
存储 - 使用
qt
的network
模块,用QUdpSocket
发送广播,构建群聊功能 - 对于感兴趣的消息截取,
- 解析协议,获取发送的数据报的内容
- 再次在其它的控件显示出来
- 时刻更新显示
部分源码:
核心的群聊功能实现如下:
#include "DlgGroupChat.h"
#include "ui_DlgGroupChat.h"
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>
#include <QColorDialog>
#include <QFileDialog>
DlgGroupChat::DlgGroupChat(QWidget *parent, QString name) :
QWidget(parent),
ui(new Ui::DlgGroupChat)
{
ui->setupUi(this);
//初始化
m_pUdpSocket = new QUdpSocket;
m_strUserName = name;
m_nPort = 9999;
m_pUdpSocket->bind(this->m_nPort, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint); //共享地址+断开重连
connect(ui->pbSend, &QPushButton::clicked, [=](){
sendMsg(UserMsg);
});
sendMsg(UserEnter);
connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &DlgGroupChat::recvMsg);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//字体
connect(ui->fcbFont, &QFontComboBox::currentFontChanged, [=](const QFont &font){
ui->teChatInput->setCurrentFont(font);
ui->teChatInput->setFocus();
});
//字号
void (QComboBox:: *cbxsingal)(const QString &text) = &QComboBox::currentIndexChanged;
connect(ui->comboBox, cbxsingal, [=](const QString &text){
ui->teChatInput->setFontPointSize(text.toDouble());
ui->teChatInput->setFocus();
});
//加粗
connect(ui->tbFontBold, &QToolButton::clicked, [=](bool isCheck){
if(isCheck)
ui->teChatInput->setFontWeight(QFont::Bold);
else
ui->teChatInput->setFontWeight(QFont::Normal);
ui->teChatInput->setFocus();
});
//倾斜
connect(ui->tbFontTilt, &QToolButton::clicked, [=](bool Check){
ui->teChatInput->setFontItalic(Check);
ui->teChatInput->setFocus();
});
//下划线
connect(ui->tbFontUnderline, &QToolButton::clicked, [=](bool Check){
ui->teChatInput->setFontUnderline(Check);
ui->teChatInput->setFocus();
});
//字体颜色
connect(ui->tbMark, &QToolButton::clicked, [=](){
QColor color = QColorDialog::getColor(Qt::red);
ui->teChatInput->setTextColor(color);
ui->teChatInput->setFocus();
});
//保存聊天记录
connect(ui->tbChatSave, &QToolButton::clicked, [=](){
QString path = QFileDialog::getSaveFileName(this, "保存记录", "聊天记录", "(*.txt)");
if(path.isEmpty() || ui->tbChat->document()->isEmpty())
{
QMessageBox::warning(this, "警告", "路径或者内容不能为空");
return;
}
else
{
QFile file(path);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&file);
stream << ui->tbChat->toPlainText();
file.close();
}
ui->teChatInput->clear();
ui->teChatInput->setFocus();
});
//清空聊天记录
connect(ui->tbChatClean, &QToolButton::clicked, [=](){
ui->tbChat->clear();
ui->teChatInput->setFocus();
});
}
DlgGroupChat::~DlgGroupChat()
{
delete ui;
}
void DlgGroupChat::closeEvent(QCloseEvent* e)
{
emit this->closeDlgGroupChat();
sendMsg(UserLeft);
close();
}
void DlgGroupChat::sendMsg(DlgGroupChat::MsgType typeMsg)
{
QByteArray array;
QDataStream stream(&array,QIODevice::WriteOnly);
stream << typeMsg; //将类型加入到 流中
switch (typeMsg)
{
case UserMsg:{
if(ui->teChatInput->toPlainText().isEmpty())
{
QMessageBox::warning(this, "警告", "发送的消息不能为空");
return;
}
//报文协1:类型+姓名+时间+内容
QString time = QDateTime::currentDateTime().toString("yyyy-mm-dd hh:mm:ss");
QString msg = ui->teChatInput->toHtml();
stream <<m_strUserName<<time<< msg;
ui->teChatInput->clear();
ui->teChatInput->setFocus();
}break;
case UserEnter:{
//报文协议2:类型+姓名
stream <<m_strUserName;
}break;
case UserLeft:{
//报文协议3:类型+姓名
stream <<m_strUserName;
}break;
default:
break;
}
//书写报文,广播发送
m_pUdpSocket->writeDatagram(array, QHostAddress::Broadcast, m_nPort);
}
void DlgGroupChat::userEnter(QString userName)
{
bool isEmpty = ui->twUser->findItems(userName, Qt::MatchExactly).isEmpty();
if(isEmpty) //不存在才能添加显示
{
QTableWidgetItem* item = new QTableWidgetItem(userName);
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QTableWidgetItem* timeItem = new QTableWidgetItem(time);
//插入行
ui->twUser->insertRow(0);
ui->twUser->setItem(0, 0, item);
ui->twUser->setItem(0, 1, timeItem);
//追加聊天记录
ui->tbChat->setTextColor(Qt::gray);
ui->tbChat->append(QString("%1 于 %2 上线了").arg(userName).arg(time));
//在线人数更新
ui->labCount->setText(QString("在线用户:%1人").arg(ui->twUser->rowCount()));
sendMsg(UserEnter);
}
}
void DlgGroupChat::userLeft(QString userName)
{
bool isEmpty = ui->twUser->findItems(userName, Qt::MatchExactly).isEmpty();
if(!isEmpty) //存在才能离开显示
{
int nRow = ui->twUser->findItems(userName, Qt::MatchExactly).first()->row();
ui->twUser->removeRow(nRow);
QString time = QDateTime::currentDateTime().toString("yyyy-mm-dd hh:mm:ss");
//追加聊天记录
ui->tbChat->setTextColor(Qt::gray);
ui->tbChat->append(QString("%1 于 %2 下线了").arg(userName).arg(time));
//在线人数更新
ui->labCount->setText(QString("在线用户:%1人").arg(ui->twUser->rowCount()));
}
}
void DlgGroupChat::recvMsg()
{
qint64 size = m_pUdpSocket->pendingDatagramSize(); //获取接收报文的长度
QByteArray array = QByteArray(size, 0);
m_pUdpSocket->readDatagram(array.data(), size);
//解析报文协议:类型+姓名+时间+内容
QString name;
QString time;
int typeMsg;
QString Msg;
QDataStream stream(&array, QIODevice::ReadOnly);
stream >> typeMsg >> name >> time >> Msg;
qDebug()<<size<<" recv=>"<<QString::fromLocal8Bit(array);
switch (typeMsg)
{
case UserMsg:{
ui->tbChat->setTextColor(Qt::blue);
ui->tbChat->append("[" + name +"]" + time);
ui->tbChat->append(Msg);
}break;
case UserEnter:{
userEnter(name);
}break;
case UserLeft:{
userLeft(name);
}break;
default:
break;
}
}
void DlgGroupChat::on_pbExit_clicked()
{
sendMsg(UserLeft);
close();
}
源码下载:
源码:IM_QQ
相关资源:
提供一些QQ
的文源文件共享:QQ界面资源分享
QQemoji小表情:
QQ登录界面图片:
QQ各种钻vip图标:
QQ群聊界面: