1、实现功能
本程序模拟实现QQ群聊功能,采用UDP通信方式,可以设置字体,保存聊天记录等,实时显示上线离开人数以及具体人员。可视为简化版的腾讯QQ。目前只有群聊,未实现一对一单独聊天。
完整源代码见:https://download.csdn.net/download/weixin_44618297/85076196
2、效果图
3、程序结构
4、部分程序源码
dialoglist.cpp
#include "dialoglist.h"
#include "ui_dialoglist.h"
#include <QToolButton>
#include "widget.h"
#include <QMessageBox>
dialoglist::dialoglist(QWidget *parent) :
QWidget(parent),
ui(new Ui::dialoglist)
{
ui->setupUi(this);
//设置标题
setWindowTitle("QQ2022");
//设置图标
setWindowIcon(QIcon(":/images/qq.png"));
//准备图标和名字
QList<QString> namelist;
//QStringList namelist; //这种方式也可实现
namelist<<"Cherry"<<"jj"<<"wy"<<"dr"<<"ymrl"<<"spqy"<<"lswh"<<"qmnn";
//维护创建出来的每个对象
QVector<QToolButton*> vtoolbtn;
for(int i = 0; i<8; i++)
{
//创建头像
QToolButton *btn = new QToolButton;
QString str = QString(":/images/%1.png").arg(namelist.at(i));
//设置文字
btn->setText(namelist[i]);
//设置头像图片
btn->setIcon(QPixmap(str));
//设置头像大小
btn->setIconSize(QPixmap(str).size());
//加到垂直布局中
ui->vLayout->addWidget(btn);
//容器保存住按钮对象,方便以后对其操作
vtoolbtn.push_back(btn);
isShow.push_back(false);//初始状态每个窗口都未打开
//设置按钮风格为透明
btn->setAutoRaise(true);
//设置文字和图片一起显示
btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
}
//对8个按钮添加信号和槽
for(int i = 0; i < vtoolbtn.size(); i++)
{
//点击头像弹出聊天对话框
connect(vtoolbtn.at(i), &QToolButton::clicked, [=](){
//如果被打开了就不要重复打开
if(isShow[i])
{
QString str = QString("%1 window already opened").arg(vtoolbtn[i]->text());
QMessageBox::warning(this, "warn", str);
return;
}
isShow[i] = true;
//构造聊天窗口,参数1:顶层方式弹出 参数2:窗口名字
Widget * window = new Widget(0, vtoolbtn[i]->text());
//设置窗口标题
window->setWindowTitle(vtoolbtn[i]->text());
window->setWindowIcon(vtoolbtn[i]->icon());
window->show();
//自己监听自己的关闭事件
connect(window, &Widget::closewidget, [=](){
isShow[i] = false;
});
});
}
}
dialoglist::~dialoglist()
{
delete ui;
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDataStream>
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>
#include <QColorDialog>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
/********************核心功能为群聊,即把所有人发的消息都发到公屏上,没有发给特定的人***********************/
Widget::Widget(QWidget *parent, QString name)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化操作
udpSocket = new QUdpSocket(this);
//获取用户名
uName = name;
this->port = 1111;
//绑定端口号 绑定模式 共享地址 断线重连
udpSocket->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
//点击发送按钮发送消息
connect(ui->sendbtn, &QPushButton::clicked, [=](){
sendMsg(Msg);
});
//监听别人发送的数据
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::ReceiveMessage);
//构造一个窗口,就发送新用户进入
sendMsg(UsrEnter);
//点击按钮关闭窗口
connect(ui->exitbtn, &QPushButton::clicked, this, &QWidget::close);
/*******************************辅助功能*********************************/
//字体
connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, [=](const QFont &font){
ui->msgTextEdit->setCurrentFont(font);
ui->msgTextEdit->setFocus();
});
//字号
//涉及到了函数重载,要用函数指针指定使用哪一个函数
void (QComboBox:: *cbxsingal)(const QString &test) = &QComboBox::currentIndexChanged;
connect(ui->sizeComboBox, cbxsingal, [=](const QString &text){
ui->msgTextEdit->setFontPointSize(text.toDouble());
ui->msgTextEdit->setFocus();
});
//加粗
//设置选中按钮生效,点击一次发送TRUE,再次点击发送FALSE
ui->boldbtn->setCheckable(true);
connect(ui->boldbtn, &QToolButton::clicked, [=](bool ischeck){
if(ischeck)
{
ui->msgTextEdit->setFontWeight(QFont::Bold);
}
else
{
ui->msgTextEdit->setFontWeight(QFont::Normal);
}
});
//倾斜
ui->italicbtn->setCheckable(true);
connect(ui->italicbtn, &QToolButton::clicked, [=](bool ischeck){
ui->msgTextEdit->setFontItalic(ischeck);
});
//下划线
ui->underline->setCheckable(true);
connect(ui->underline, &QToolButton::clicked, [=](bool ischeck){
ui->msgTextEdit->setFontUnderline(ischeck);
});
//字体颜色
connect(ui->colorbtn, &QToolButton::clicked, [=](){
QColor color = QColorDialog::getColor(Qt::red);
ui->msgTextEdit->setTextColor(color);
});
//清空聊天记录
connect(ui->clearbtn, &QToolButton::clicked, [=](){
ui->msgBrowser->clear();
});
//保存聊天记录
connect(ui->savebtn, &QToolButton::clicked, [=](){
if(ui->msgBrowser->document()->isEmpty())//聊天记录可能为空
{
QMessageBox::warning(this,"warn","the text is empty!");
}
else
{
QString path = QFileDialog::getSaveFileName(this, "save record", "chat record", "(*.txt)");
QFile file(path);
//打开模式加换行操作 QIODevice::Text
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&file);
stream << ui->msgBrowser->toPlainText();
file.close();
}
});
}
void Widget::ReceiveMessage()
{
//拿到数据报文
//获取报文长度
qint64 size = udpSocket->pendingDatagramSize();
QByteArray array = QByteArray(size, 0);
udpSocket->readDatagram(array.data(), size);
//解析数据 第一段类型 第二段 用户名 第三段 具体内容
QDataStream stream (&array, QIODevice::ReadOnly);
int msgtype;
stream >> msgtype;//读取到类型
QString usrname;
QString msg;
//获取时间
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
switch(msgtype)
{
case Msg://普通消息
stream >> usrname >> msg;//读取到用户名 读取到消息内容
ui->msgBrowser->setTextColor(Qt::blue);
ui->msgBrowser->append("[" + usrname + "]" + time);
ui->msgBrowser->append(msg);
break;
case UsrEnter://新用户进入消息
stream >> usrname;
usrEnter(usrname);
break;
case UsrLeft:// 用户离开消息
stream >> usrname;
//包括用户离开的时间
usrLeft(usrname, time);
break;
default:
break;
}
}
void Widget::sendMsg(MsgType type)//广播UDP消息
{
//发送的消息分为三种类型, 做分段处理 第一段类型 第二段 用户名 第三段 具体内容
QByteArray array;
QDataStream stream(&array, QIODevice::WriteOnly);//分段发送数据
stream << type << getUsr();//第一段内容添加到流中 发送的消息类型 第二段 用户名
switch(type)
{
case Msg: //普通消息发送
if(ui->msgTextEdit->toPlainText() == "")// 判断用户没有输入任何内容,不发任何消息
{
QMessageBox::warning(this, "warn", "send msg is empty");
return;
}
stream << getMsg();//第三段内容 消息的内容
break;
case UsrEnter://新用户进入消息
break;
case UsrLeft:// 用户离开消息
break;
default:
break;
}
//完成报文书写,广播发送
udpSocket->writeDatagram(array, QHostAddress::Broadcast, port);
}
//处理新用户加入
void Widget::usrEnter(QString usrname)
{
//更新右侧table
bool isempty = ui->usrtableWidget->findItems(usrname, Qt::MatchExactly).isEmpty();//判断你要添加的这个用户是否存在
if(isempty)
{
QTableWidgetItem * usr = new QTableWidgetItem(usrname);
//插入行
ui->usrtableWidget->insertRow(0);
ui->usrtableWidget->setItem(0, 0, usr);
//追加聊天记录
ui->msgBrowser->setTextColor(Qt::gray);
ui->msgBrowser->append(QString("%1 login").arg(usrname));
//在线人数更新
ui->usrNumlbl->setText(QString("online usr: %1").arg(ui->usrtableWidget->rowCount()));
//把自身信息广播出去(前边已经判断了该用户没有在列表中才添加呢)
sendMsg(UsrEnter);
}
}
//处理用户离开
void Widget::usrLeft(QString usrname, QString time)
{
//更新右侧table
bool isempty = ui->usrtableWidget->findItems(usrname, Qt::MatchExactly).isEmpty();//判断你要添加的这个用户是否存在
if(!isempty)
{
//删除行
int row = ui->usrtableWidget->findItems(usrname, Qt::MatchExactly).first()->row();
ui->usrtableWidget->removeRow(row);
//追加聊天记录
ui->msgBrowser->setTextColor(Qt::gray);
ui->msgBrowser->append(QString("%1 logout time:%2").arg(usrname).arg(time));
//在线人数更新
ui->usrNumlbl->setText(QString("online usr: %1").arg(ui->usrtableWidget->rowCount()));
}
}
//获取用户名
QString Widget::getUsr()
{
return this->uName;
}
//从消息输入框获取消息
QString Widget::getMsg()
{
QString str = ui->msgTextEdit->toHtml();//因为有一些加粗设置,所以需要把内容转为html
ui->msgTextEdit->clear();
ui->msgTextEdit->setFocus();//让光标回到原处
return str;
}
//点击关闭窗口系统自动调用该函数
void Widget::closeEvent(QCloseEvent *e)
{
emit this->closewidget();
//发送用户离开消息
sendMsg(UsrLeft);
//断开套接字
udpSocket->close();
udpSocket->destroyed();
//其它事件交给父类去处理
QWidget::closeEvent(e);
}
Widget::~Widget()
{
delete ui;
}