基于QT Creator 5.14的仿QQ聊天系统【UDP通讯】

一、使用工具

        本次整个项目的开发基于Qt Creator 5.14.2进行的开发与调试,主要通信功能采用UDPSocket完成,是我学习Qt过程中联系的小案例,过程中遇到的bug和完整代码会尽量展现出来,关于Qt以及其安装流程在优快云里面有很多大佬写的教程,十分详细!!!

 二、软件实例效果图

 三、完整代码解读:

dialoglist.h

#ifndef DIALOGLIST_H
#define DIALOGLIST_H
#include <QVector>
#include <QWidget>


namespace Ui {
class DialogList;
}

class DialogList : public QWidget
{
    Q_OBJECT

public:
    explicit DialogList(QWidget *parent = nullptr);
    ~DialogList();
    QVector<bool> isshow;  //判断当前窗口是否已被显示出



private:
    Ui::DialogList *ui;
};

#endif // DIALOGLIST_H

 dialoglist.cpp

#include "dialoglist.h"
#include "ui_dialoglist.h"
#include <QToolButton>
#include <widget.h>
#include <QString>
#include <QList>
#include <QMessageBox>


DialogList::DialogList(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::DialogList)
{
    ui->setupUi(this);
    setWindowIcon(QPixmap(":/images/校标.png"));
    //容器保存10个toolbutton
    QVector<QToolButton*> toolvector;
    //记录头像图名加入泛型集合
    QList<QString> name;
    name<<"copy.Image"<<"cut.Image"<<"Hearts"<<"new.Image"<<"open.Image"<<"paste.Image"<<"print.Image"
       <<"printPreview.Image"<<"save.Image"<<"校标";


    //在toolbox容器中动态生成toolbutton按钮
    for (int i=0;i<name.size();i++)
    {
        QToolButton *btn = new QToolButton;
        btn->setText(name[i]);
        QString str = QString(":/images/"+name[i]+".png");
        btn->setIcon(QPixmap(str));
        btn->setIconSize(QSize(72,72));
        btn->setAutoRaise(true);
        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        ui->vLayout->addWidget(btn);
        //将创建的10个toolbutton全部放入容器保存
        toolvector.push_back (btn);
        isshow.push_back(false);
    }
    //对10个toolbutton添加信号和槽函数
    for (int i=0;i<toolvector.size();i++)
    {
        connect (toolvector[i],&QToolButton::clicked,[=](){
            if(isshow[i]==true)
            {
                QMessageBox::warning(this,"警告","当前窗口已被打开");
                return;
            }
            else
            {
                isshow[i]=true;
                Widget *widget = new Widget(0,toolvector[i]->text());
                widget->setWindowTitle(toolvector[i]->text());
                widget->setWindowIcon(toolvector[i]->icon());
                widget->show();

                connect (widget,&Widget::closeWidget,[=](){
                    isshow[i]=false;
                });
            }
        });

    }


}

DialogList::~DialogList()
{
    delete ui;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include<QString>
#include <QWidget>
#include <QUdpSocket>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent,QString name);
    ~Widget();

private:
    Ui::Widget *ui;


signals:
    //关闭窗口发送关闭信号
    void closeWidget();
public:
    //窗口关闭事件
    void closeEvent(QCloseEvent *);

public:
    enum MsgType{Msg,UsrEnter,UsrLeft};
    void sendMsg(MsgType type); //广播UDP消息
    void usrEnter(QString usrname); //新用户进入
    void usrLeft(QString usrname,QString time); //处理用户离开
    QString getUsr(); //获取用户名
    QString getMsg(); //获取聊天信息
private:
    QUdpSocket *udpSocket; //UDP套接字
    qint16 port;  //端口
    QString Uname; //用户名
    void ReceiveMsg(); //接收UDP消息

};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include <QDateTime>
#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=9999;
    //绑定端口号  共享地址 和 断线重连
    udpSocket->bind(port,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

    //发送新用户进入
    sendMsg(UsrEnter);

    //点击按钮发送信息
    connect (ui->sendbtn,&QPushButton::clicked,[=](){
        sendMsg(Msg);
    });
    //监听别人发送的数据
    connect (udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMsg);

    //退出程序
    connect (ui->quitbtn,&QPushButton::clicked,[=](){
        this->close();
    });

    /辅助功能///
    //设置字体
    connect(ui->fontComboBox,&QFontComboBox::currentFontChanged,[=](const QFont &font){
        ui->msgtextEdit->setFont(font);
    });
    //设置字号[应用函数指针]
    void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
    connect(ui->sizecomboBox, p ,[=](const QString &text){
        ui->msgtextEdit->setFontPointSize(text.toDouble());
        ui->msgtextEdit->setFocus();
    });
    //加粗
    connect(ui->Boldbtn,&QToolButton::clicked,[=](bool checked){
        if (checked)
        {
           ui->msgtextEdit->setFontWeight(QFont::Bold);
        }
        else
        {
           ui->msgtextEdit->setFontWeight(QFont::Normal);
        }
    });
    //倾斜
    connect(ui->Itabtn,&QToolButton::clicked,[=](bool checked){
        if (checked)
        {
           ui->msgtextEdit->setFontItalic(true);
        }
        else
        {
           ui->msgtextEdit->setFontItalic(false);
        }
    });
    //下划线
    connect(ui->underlinebtn,&QToolButton::clicked,[=](bool checked){
        if (checked)
        {
           ui->msgtextEdit->setFontUnderline(true);
        }
        else
        {
           ui->msgtextEdit->setFontUnderline(false);
        }
    });
    //字体颜色
    connect(ui->colorbtn,&QToolButton::clicked,[=](){
        QColor color = QColorDialog::getColor(Qt::red);
        ui->msgtextEdit->setTextColor(color);
    });
    //清空删除聊天记录
    connect(ui->deletebtn,&QToolButton::clicked,[=](){
        ui->msgBrowser->clear();
    });
    //保存聊天记录
    connect(ui->savebtn,&QToolButton::clicked,[=](){
        if (ui->msgBrowser->document ()->isEmpty())
        {
            QMessageBox::warning(this,"警告","聊天记录不能为空");
            return;
        }
        else
        {
            QString savepath =  QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
            if (savepath.isEmpty())
            {
                QMessageBox::warning(this,"警告","路径不能为空");
                return;
            }
            else
            {
                QFile file(savepath);
                file.open(QIODevice::WriteOnly | QIODevice::Text);
                QTextStream stream(&file);
                stream << ui->msgBrowser->toPlainText();
                file.close();
                QMessageBox::information (this,"Tip","保存成功");
            }
        }
    });

}

Widget::~Widget()
{
    delete ui;
}

//窗口关闭事件
void Widget::closeEvent(QCloseEvent *)
{
    emit this->closeWidget();
    //发送用户离开
    sendMsg(UsrLeft);
    //关闭套接字
    udpSocket->close ();
    udpSocket->destroyed ();
}
//广播UDP消息
void Widget::sendMsg(MsgType type)
{
    QByteArray array=QByteArray(10000,0);
    QDataStream stream(&array,QIODevice::WriteOnly);
    //第一段 获取信息类型 第二段 用户名
    stream<<type<<getUsr();
    switch (type)
    {
    case Msg:
        //如果输入框为空,则提示警告
        if (ui->msgtextEdit->toPlainText()=="") {
            QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
            return;
        }
        //第三段 合并获取到的消息
        stream<<getMsg();
        break;
    case UsrEnter:  //用户进入信息

        break;
    case UsrLeft:  //用户离开信息

        break;
    default:
        break;
    }

    //书写报文 广播发送
    udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
}

//新用户进入
void Widget::usrEnter(QString usrname)
{
    bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
    if (isempty)
    {
        ui->msgBrowser->setTextColor (Qt::gray);
        ui->msgBrowser->append (usrname+" 上线了!");
        QTableWidgetItem *usr = new QTableWidgetItem(usrname);
        ui->usrtableWidget->insertRow(0);
        ui->usrtableWidget->setItem(0,0,usr);
        QString online = QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount());
        ui->label->setText (online);
        //把自身信息广播出去
        sendMsg (UsrEnter);
    }

}

//处理用户离开
void Widget::usrLeft(QString usrname,QString time)
{
    bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
    if (!isempty)
    {
        int r = ui->usrtableWidget->findItems(usrname,Qt::MatchExactly).first()->row();
        ui->usrtableWidget->removeRow(r);
        //追加聊天记录
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->append (QString("%1 at %2 离开").arg (usrname).arg(time));
        //在线人数更新
        ui->label->setText (QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount()));
    }

}
//获取用户名
QString Widget::getUsr()
{
    return this->Uname;
}
//获取聊天信息
QString Widget::getMsg()
{
    QString str = ui->msgtextEdit->toHtml();
    ui->msgtextEdit->clear();
    ui->msgtextEdit->setFocus();
    return str;
}
//接收UDP消息
void Widget::ReceiveMsg()
{
    //拿到数据报文
    qint64 size = udpSocket->pendingDatagramSize();
    QByteArray array=QByteArray(10000,0);
    udpSocket->readDatagram(array.data(),size);
    //解析数据
    QDataStream stream(&array,QIODevice::ReadOnly);
    int msgType;
    QString usrName;
    QString msg;
    QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    stream>>msgType;
    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;
        QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
        usrLeft(usrName,time);
        break;
    }
    default:
        break;
    }
}

 ★UDP通信使用流程如下:

 


关键核心易出错部分,解析:

1.针对有重载的信号或槽函数,使用时要注意用到函数指针 void(作用域::*p)(int a) =&...

//设置字号[应用函数指针]
    void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
    connect(ui->sizecomboBox, p ,[=](const QString &text){
        ui->msgtextEdit->setFontPointSize(text.toDouble());
        ui->msgtextEdit->setFocus();
    });

 2.写文本文件到本地,将指定文本保存到本地:

QString savepath =  QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
            if (savepath.isEmpty())
            {
                QMessageBox::warning(this,"警告","路径不能为空");
                return;
            }
            else
            {
                QFile file(savepath);
                file.open(QIODevice::WriteOnly | QIODevice::Text);
                QTextStream stream(&file);
                stream << ui->msgBrowser->toPlainText();
                file.close();
                QMessageBox::information (this,"Tip","保存成功");
            }

3. 广播UDP信息:

//广播UDP消息
void Widget::sendMsg(MsgType type)
{
    QByteArray array=QByteArray(10000,0);
    QDataStream stream(&array,QIODevice::WriteOnly);
    //第一段 获取信息类型 第二段 用户名
    stream<<type<<getUsr();
    switch (type)
    {
    case Msg:
        //如果输入框为空,则提示警告
        if (ui->msgtextEdit->toPlainText()=="") {
            QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
            return;
        }
        //第三段 合并获取到的消息
        stream<<getMsg();
        break;
    case UsrEnter:  //用户进入信息

        break;
    case UsrLeft:  //用户离开信息

        break;
    default:
        break;
    }

    //书写报文 广播发送
    udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
}

4. 接收DUP信息:

//接收UDP消息
void Widget::ReceiveMsg()
{
    //拿到数据报文
    qint64 size = udpSocket->pendingDatagramSize();
    QByteArray array=QByteArray(10000,0);
    udpSocket->readDatagram(array.data(),size);
    //解析数据
    QDataStream stream(&array,QIODevice::ReadOnly);
    int msgType;
    QString usrName;
    QString msg;
    QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    stream>>msgType;
    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;
        QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
        usrLeft(usrName,time);
        break;
    }
    default:
        break;
    }
}

5. 向控件TableWidget中插入内容(以下例子为插入指定的usrname):

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);
    }

                                                     后续其他练习实例同步更新,完结!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NCUTer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值