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,再次点击发送FALSEui->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::Textfile.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)
{//更新右侧tablebool 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)
{//更新右侧tablebool 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();//因为有一些加粗设置,所以需要把内容转为htmlui->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;
}