简介: 自我熟练qt的widget的使用,熟悉使用qt的network相关模块写一个仿QQ的QQ简洁版2019群聊项目。哇伊,这是我大学之处一直想写的IM即时通讯系统的,模仿写一个QQ的项目,但是因为时间等关系,断断续续的只是写了一些IM_QQ的部分相关功能的知识,每回写一个核心功能,但到了现在这会,感觉基本几大核心功能(登录,单人聊天,群聊功能,数据库设计)已经写好了,后续有时间,就将其完整的写成一个完整版的QQ。
自我熟练qt的widget的使用,熟悉使用qt的network相关模块写一个仿QQ的QQ简洁版2019群聊项目。哇伊,这是我大学之处一直想写的IM即时通讯系统的,模仿写一个QQ的项目,但是因为时间等关系,断断续续的只是写了一些IM_QQ的部分相关功能的知识,每回写一个核心功能,但到了现在这会,感觉基本几大核心功能(登录,单人聊天,群聊功能,数据库设计)已经写好了,后续有时间,就将其完整的写成一个完整版的QQ。
编程环境: win10 x64 专业版
编程软件: 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奠定基础,不慌不躁,心虽急,但是干活不可急,始终坚信:
慢而不出差错,就是快
视频演示效果:
https://www.bilibili.com/video/av59692500 (csdn貌似不支持插入bilibili视频,反正设置了无效),文末同步博文链接可以查看效果
图片演示效果:
联系人列表:
群聊界面:
核心的群聊功能实现如下:
#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 感兴趣的话 欢迎 star 和 fork 这个项目,给它提交贡献,进行合并
提供一些QQ的文源文件共享:[QQ界面资源分享]
Chat_IM_QQ
QQemoji小表情:
QQ登录界面图片:
QQ各种钻vip图标:
QQ群聊界面:
QtExamples
欢迎 star 和 fork 这个系列的 QT / DTK 学习,附学习由浅入深的目录。
偕臧x 认证博客专家 架构 Qt/C Linux 看待世界始终保持着好奇;期待与各位的邂逅,比较喜欢Linux、C++、Qt和与技术无关的生活相关,不时折腾一下新技术,欢迎来此处https://ifmet.cn 找我玩