Qt群聊demo

        跟着视频学习很多年前的一个小qt项目,简单还原的qq的群聊功能,过程中也是踩了一些小坑坑.

        结果展示

        本demo实现了基本的群聊功能和设置了聊天框的下划线,加粗,字体,颜色等等,最后还结合了文件的操作,可以将聊天记录进行保存.

        群聊的用户

        

        聊天界面及功能

实现过程

        首先进行ui设计,在ui设计模式下面进行界面设计.

        然后进行代码设计.

        先对群聊部分进行代码设计,明确需求,这个界面需要显示可以参与到群聊里面的用户,以及实现点击用户,就进入弹出群聊界面,并且要传递一些参数进入界面,方便界面显示,代码中采用for循环放入用户,采用QVector容器保存按钮进行处理,isShow是标识,处理点击同一个用户按钮弹出多个相同用户框的情况,代码如下:

        

DialogList::DialogList(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::DialogList)
{
    ui->setupUi(this);
    this->resize(250,700);
    this->setWindowIcon(QPixmap(":/images/qq.png"));
    this->setWindowTitle("MyselfQQ");

    //创建9个按钮存放到QVector容器中
    QVector<QToolButton *> vToolBtn; //声明存放QtoolButton的容器

    //准备图标
        QList<QString>nameList;
        nameList << "斧头帮主" << "忆梦如澜" <<"北京出版人"<<"Cherry"<<"淡然"
                 <<"娇娇girl"<<"落水无痕"<<"青墨暖暖"<<"无语";


        QStringList iconNameList; //图标资源列表
        iconNameList << "ymrl"<< "ymrl" <<"qq" <<"Cherry"<< "qq"
                     <<"jj"<<"lswh"<<"qmnn"<<"qq";


    //循环放入按钮
    for(int i = 0;i<8;i++){
        QToolButton * btn = new QToolButton;

               btn->setText(nameList[i]);
               //设置头像
               QString str = QString(":/images/%1.png").arg(iconNameList.at(i));
               btn->setIcon(QPixmap(str));
               //设置头像大小
               btn->setIconSize(QPixmap(str).size());
               //设置按钮风格 透明
               btn->setAutoRaise(true);
               //设置文字和图片一起显示
               btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
               //加到 垂直布局中
               ui->verticalLayout->addWidget(btn);
               //容器保存住9个按钮,方便以后再次操作
               vToolBtn.push_back(btn);
               //9个标示默认初始化  防止点击同一个人出现多个窗口
               isShow.push_back(false);

    }

    //将聊天的列表窗口和具体聊天的信息窗口进行关联  点击按钮弹出窗口
    for(int i = 0;i<vToolBtn.size();i++){ //这里体现了上诉 使用容器保存九个按钮的作用
        connect(vToolBtn[i],&QToolButton::clicked,this,[=](){

            if(isShow[i]){
                QMessageBox::warning(this," 警告",QString("用户1%的窗口已经弹出").arg(vToolBtn[i]->text()));
                return;
            }

            isShow[i] = true;


            //这是直接打开 , 尝试创建窗口是传入用户名
            Widget * chatWidget = new Widget(0,vToolBtn[i]->text());
            //设置窗口的标题
            chatWidget->setWindowTitle(vToolBtn[i]->text());
            //设置ICON
            chatWidget->setWindowIcon(vToolBtn[i]->icon());
            chatWidget->show();

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

}

        群聊部分实现过后,进行聊天界面部分的设计,这里也是采用udp通信,可以参考我之前写的udp demo进行研究,只不过群聊使用的是广播模式.这里进行设计的时候要一个功能一个功能的完成.

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


     ui->label->setText(QString("在线用户:1%人").arg(ui->tableWidget->rowCount()));


    //点击发送按钮发送消息
    connect(ui->send,&QPushButton::clicked,[=](){
        sendMsg(Msg);
    });
    connect(ui->exit,&QPushButton::clicked,this,[=]()
       {
           this->close();
       });

    //监听发送的数据
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMessage);

    //字体设置
    connect(ui->fontComboBox,&QFontComboBox::currentFontChanged,[=](const QFont &font){
            ui->textEdit->setCurrentFont(font);
            ui->textEdit->setFocus();
        });
    //字号
    void(QComboBox::* cbxsignal)(const QString &text) = &QComboBox::currentIndexChanged;
    connect(ui->comboBox,cbxsignal,[=](const QString &text){
        ui->textEdit->setFontPointSize(text.toDouble());
        ui->textEdit->setFocus();
    });
    //加粗

        connect(ui->l1,&QToolButton::clicked,this,[=](){
            static int x = 1;
          //  qDebug()<<"ddd";

            /*if(isCheck)
            {
                qDebug()<<"选择加粗";
                ui->textEdit->setFontWeight(100);
            }
            else
            {
                ui->textEdit->setFontWeight(25);
                qDebug()<<"选择不加粗";
            }*/

            //上诉方法不晓得为什么不行 换成toggled点击没有反应 所以考虑使用奇偶次数来判断是否加粗
            //看了一下帮助文档好像是点击默认里面的checked是false


             if(x%2 == 1){
                // qDebug()<<"选择加粗";
                 ui->textEdit->setFontWeight(99);
                 x++;

             }
             else{
                 ui->textEdit->setFontWeight(25);
                 //qDebug()<<"选择不加粗";
                 x++;
             }
        });

        //倾斜
        connect(ui->toolButton_2,&QToolButton::clicked,[=](){
            /*qDebug()<<"ddd";
            ui->textEdit->setFontItalic(check);*/
            static int x = 1;
            if(x%2 == 1){
                ui->textEdit->setFontItalic(true);
                x++;

            }
            else{
                ui->textEdit->setFontItalic(false);
                x++;
            }
        });

        //这个按钮加上就不是默认的false了
        ui->toolButton_3->setChecked(true);

        //下划线
        connect(ui->toolButton_3,&QToolButton::clicked,[=](bool check){
            //clicked默认里面是是false 但是设置toggled点击了根本没进入
            //已经解决
            //原因   没有选中设置toolButton的checked 和 checkable属性

            ui->textEdit->setFontUnderline(check);
        });

        //字体颜色
        connect(ui->toolButton_4,&QToolButton::clicked,[=](){
            QColor color = QColorDialog::getColor(Qt::red);
            ui->textEdit->setTextColor(color);

        });

        connect(ui->toolButton_5,&QToolButton::clicked,this,[=](){
                    if(ui->textBrowser->document()->isEmpty())
                    {
                        QMessageBox::warning(this,"警告","聊天记录为空,无法保存!",QMessageBox::Ok);
                    }
                    else
                    {
                        QString fName = QFileDialog::getSaveFileName(this,"保存聊天记录","聊天记录","(*.txt)");
                        if(!fName.isEmpty())
                        {
                            //保存名称不为空再做保存操作
                            QFile file(fName);
                            file.open(QIODevice::WriteOnly | QFile::Text);

                            QTextStream stream(&file);
                            stream << ui->textBrowser->toPlainText();
                            file.close();
                        }
                    }
                });
        connect(ui->toolButton_6,&QToolButton::clicked,[=](){
                ui->textBrowser->clear();
        });


}



void  Widget:: sendMsg(MsgType type){//广播UDP消息
    //发送的消息分为三种类型
    //发送的数据做分段处理  第一段是类型 第二段用户名  第三段是具体内容

    //使用数据流 往array中写数据
    QByteArray array;
    QDataStream stream(&array,QIODevice::WriteOnly);
    //第一段内容添加到流
    stream<<type<<getUsr();

    switch (type) {
        case Msg://普通消息发送
        if(ui->textEdit->toPlainText() == ""){
            QMessageBox::warning(0,"警告","发送的内容不能为空",QMessageBox::Ok);
            return;
        }
        stream<<getMsg();
        break;
    case UsrEnter:

        break;
    case UserLeft:
        break;
    default:
        break;
    }

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

}

//获取聊天信息
QString Widget:: getMsg(){
    QString str = ui->textEdit->toHtml();

    //清空输入框
    ui->textEdit->clear();
    ui->textEdit->setFocus();
    return str;
}

//接收信息
void Widget:: ReceiveMessage(){

    qint64 size = udpSocket->pendingDatagramSize();
    //拿到数据报文
    QByteArray array =  QByteArray(size,0);
    udpSocket->readDatagram(array.data(),size);

    //解析数据
    //第一段类型
    QDataStream stram(&array,QIODevice::ReadOnly);

    int magType;
    QString usrName;
    QString msg;

    //获取当前时间
    QString time =    QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

    //读取到类型
    stram >>magType;
    switch (magType) {

        case Msg://普通聊天
        stram>>usrName>>msg;
        //追加聊天记录
        ui->textBrowser->setTextColor(Qt::blue);
        ui->textBrowser->append("[" +usrName +"]" + time);
        ui->textBrowser->append(msg);
        break;
    case UsrEnter:
        stram>>usrName;
        usrEnter(usrName);

        break;
    case UserLeft:
        stram>>usrName;
        usrLeft(usrName,time);
        break;
    default:
        break;
    }
}

void Widget:: closeEvent(QCloseEvent * e){
    //跨指窗口 进行操作
    emit this->closeWidget(); //发送自定义的信号
    sendMsg(UserLeft);
    //断开套接字
    udpSocket->close();
    udpSocket->destroyed();

    QWidget::closeEvent(e);
}

QString Widget::getUsr(){
    return  this->uName;
}
void  Widget:: usrEnter(QString username){
    //更新右侧的TableWidget
    //stram>>usrName;
    if(ui->tableWidget->findItems(username,Qt::MatchExactly).isEmpty()){
        QTableWidgetItem * usr = new QTableWidgetItem(username);
        //插入行
        ui->tableWidget->insertRow(0);
        ui->tableWidget->setItem(0,0,usr);

        //追加聊天记录
        ui->textBrowser->setTextColor(Qt::gray);
        ui->textBrowser->append(QString("%1 上线了").arg(username));

        //在线人数更新
        ui->label->setText(QString("在线用户:%1人").arg(ui->tableWidget->rowCount()));

        //把自己的信息广播出去
        sendMsg(UsrEnter);
}

}

void Widget:: usrLeft(QString username, QString time){

    if(!ui->tableWidget->findItems(username,Qt::MatchExactly).isEmpty()){
        int rowNum = ui->tableWidget->findItems(username, Qt::MatchExactly).first()->row();
            ui->tableWidget->removeRow(rowNum);
            ui->textBrowser->setTextColor(Qt::gray);
            ui->textBrowser->setCurrentFont(QFont("Times New Roman", 10));
            ui->textBrowser->append(QString("%1 于 %2 离开!").arg(username).arg(time));
            ui->label->setText(QString("在线人数:%1").arg(ui->tableWidget->rowCount()));
    }

}

        最后对这次踩的最无语的坑进行总结,在上诉代码之中的注释我也有说明,就是关于按钮点击的clicked信号.

        这里看到帮助文档之中,信号后面的bool值默认的是false.

        要想取消这个默认值,而是由我们点击判断状态值,需要设计按钮的checked属性.

        或者还可以再ui界面中进行设置 

        上图的checked勾上即可 ,toggled设置的便是checkable.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值