QT学习之QQ聊天案例

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

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值