Qt 简易版群聊器小功能

1. 群成员界面设计:

 

在 signin.cpp  中的构造函数中 创建按钮

// 1. 设置窗口名字、图标

    this->setWindowTitle("QQ群聊器");

    this->setWindowIcon(QIcon(":/img/qq_副本.png"));


    // 创建两个容器 分别存放网名 图片名

    QList<QString> nameList;    // 网名列表

    nameList << "星光大道" << "朝天阙" << "大路朝天" << "沙生欲夺" << "云上飞宵" << "塞尚不屈" << "罗黄泉"

             << "升星夺命" << "九曲殒命";

    QStringList iconNameList;   // 图标列表

    iconNameList << "1" << "2" << "3" << "4"

                 << "5" << "6" << "7" << "8" << "9";

    // 2. 设置九个按钮 九个用户

    QVector<QToolButton*> vector;   // 按钮的容器

    // 循环创建9个按钮 贴图、网名、透明化

    for(int i = 0; i < 9; i++)

    {

        // 创建按钮对象

        QToolButton* btn = new QToolButton(this);

        // 设置图片路径

        QString str = QString(":/img/%1.jpeg").arg(iconNameList.at(i));

        btn->setIcon(QPixmap(str)); // 给按钮设置图片

        btn->setText(nameList[i]);

        // 设置图片大小

        btn->setIconSize(QSize(80, 70));

        // 透明化按钮

        btn->setAutoRaise(true);

        // 设置图片和按钮网名显示的方式

        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);

        // 将按钮放到 vertricalLayout 布局中去

        ui->verticalLayout->addWidget(btn);

        // 将按钮存放到容器中去

        vector.push_back(btn);

}

 创建一个聊天界面类 ChatWidget

2.     点击按钮弹出聊天对话框

    // 3. 点击按钮弹出聊天窗口
    for(int i = 0; i < vector.size(); i++)
    {
        // 将按钮与聊天窗口关联起来
        connect(vector[i], &QToolButton::clicked, [=]()
        {
            // 判断是否已经打开聊天窗口
            if(isShow[i])
            {
                QMessageBox::warning(this, "警告", "该聊天窗口已打开,无法再次打开");
                return; // 退出函数
            }
            // 将标记是否已经打开聊天窗口 置成true
            isShow[i] = true;
            // 实例化聊天窗口对象
            ChatWidget* widget = new ChatWidget(nullptr, vector[i]->text());
            widget->setWindowTitle(vector[i]->text());
            widget->setWindowIcon(QIcon(vector.at(i)->icon()));
            widget->show(); // 显示窗口
            // 解决关闭聊天窗口之后无法再次打开的Bug
            // 接收关闭窗口信号
            connect(widget, &ChatWidget::closeWidget, [=]()
            {
                isShow[i] = false;  // 将标记置成false
            });
        });
    }

 3. 聊天界面窗口布局

4. 用UDP 收发消息 

  • chatwidget.h 头文件中:

  • public:
        // 3. 枚举 分别代表 普通信息,用户进入信息,用户离开信息
        enum MsgType{MSG, USERENTER, USERLEFT};
        // 4. 广播udp 信息 将发送的信息显示到控件 TextBrowser
        void sendMsg(MsgType type);  // 发送普通信息,用户进入信息,用户下线信息
        // 5. 获取名字
        // 6. 接收UDP 消息
        void ReceivMessage();
        // 7. 获取聊天信息 获取编辑控件中的要发送的消息
        QString getMsg();
    private:
        // 用户名 网名
        QString myName;
        // 端口号
        quint16 port;
        // 套接字
    QUdpSocket * udpSocket;
    
  • 在 chatwidget.cpp 中的构造函数 实例化套接字; 绑定端口; 连接接收信息

  •     // 获取用户名(网名)通过构造函数获取
        this->myName = name;
        // 实例化套接字对象
        this->udpSocket = new QUdpSocket(this);
        // 设置端口号
        this->port = 8989;
        // 2.绑定地址与端口号
        // 采用 ShareAddress 模式 (允许其他的服务连接到相同的地址和端口)
        // 特别是用在多客户端监听同一个服务器端口号等时,特别有效, 和ReuseAddressHint 模式(重新连接服务器)
        this->udpSocket->bind(this->port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
        // 3. 监听信号
        connect(this->udpSocket, &QUdpSocket::readyRead, this, &ChatWidget::ReceivMessage);
        // 4. 点击发送按钮 将信息发送出去
        connect(ui->sendBtn, &QPushButton::clicked, [=]()
        {
            sendMsg(MSG);
        });
    
  • 广播信息

  • // 4. 广播udp 信息 将发送的信息显示到控件 TextBrowser
    void ChatWidget::sendMsg(MsgType type)
    {
        // QDataStream 是数据流,相当于数据管道,屏蔽了数据转换过程
        QByteArray array;   // 创建一个数组 装数据
        // 创建流 好处是可以分段 参数1:流入地址, 参数2:以只写的方式
        QDataStream stream(&array, QIODevice::WriteOnly);
        // 流入类型和用户名
        stream << type << this->myName;
        // 分类 类型消息处理
        switch (type)
        {
        case MSG:
            // 判断编辑框输入的消息是否为空
            if(ui->msgTextEdit->toPlainText() == "")
            {
                QMessageBox::warning(this, "警告", "发送的信息不能为空");
                return; // 退出函数
            }
            stream << this->getMsg();   // 流入信息 普通的聊天信息
            break;
        case USERENTER:
            break;
        case USERLEFT:
            break;
        default:
            break;
        }
        // 发送数据报 书写报文 QHostAddress::Broadcast --> 发送给所有人
        udpSocket->writeDatagram(array.data(), array.size(), QHostAddress::Broadcast, this->port);
    }
    
  • 接收消息

  • // 6. 接收UDP 消息
    void ChatWidget::ReceivMessage()
    {
        // 获取 第一个挂起的UDP 数据报的大小
        qint64 size = udpSocket->pendingDatagramSize();
        // 将 qint64 转换成 int
        int mysize = static_cast<int>(size);
        // 定义一个数组用于接收数据流的数据 将元素全部置成0
        QByteArray array = QByteArray(mysize, 0);   // 大小为mysize 第一个挂起的数据报的大小
        /* QUdpSocket::readDatagram 接收数据报
         * 接收不大与 maxSize 字节的数据并存储在数据中,发送方的主机地址和端口号存储在 *地址和*端口号中(除非指针为0)
         * 成功返回数据报的大小; 否则返回-1
         * 如果 maxsize 太小,则数据报的其余部分将丢失。为了避免数据丢失,请在尝试读取挂起的数据报之前
         * 调用 penddingDatagramSize() 确定关起的数据报的大小,如果 maxSize 为0, 则数据报将被丢弃
        */
        // 接收数据报
        udpSocket->readDatagram(array.data(), size);
        // 以只读的方式将信息流入到数组中去
        QDataStream stream(&array, QIODevice::ReadOnly);
        // 声明一个变量 通过 数据流的方式获取 消息类型
        int msgtype;
        // 流入类型 读取类型
        stream >> msgtype;
        // 声明 用户名 聊天信息
        QString name, msg;
        // 格式化时间类型
        QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        // 分类处理
        switch(msgtype)
        {
        case MSG:   // 普通消息
            // 流入 网名和聊天信息
            stream >> name >> msg;
            // 将信息追加到控件 TextBorwser 中去(显示聊天信息的控件)
            ui->msgBrowser->setTextColor(Qt::blue); // 设置显示信息的颜色
            ui->msgBrowser->setCurrentFont(QFont("方正粗黑宋简体", 11));
            // 追加网名 和 时间
            ui->msgBrowser->append("[" + name + "]" + time);
            // 追加聊天内容
            ui->msgBrowser->append(msg);
            break;
        case USERENTER: // 用户进入
            // 流入用户名
            stream >> name;
    
            break;
        case USERLEFT:  // 用户离开  下线
            // 流入用户名
            stream >> name;
    
            break;
        }
    }
    
  • 从编辑框控件中获取输入的信息

  • // 7. 获取聊天信息 获取编辑控件中的要发送的消息
    QString ChatWidget::getMsg()
    {
        // 从编辑控件中获取输入的信息
        // toPlainText()【简单文本】 和 toHtml() 的区别是,toHtml()可以设置特殊标点,分段等(富文本)
        QString msg = ui->msgTextEdit->toHtml();
        // 清空编辑框控件
        ui->msgTextEdit->clear();
        // 设置光标
        ui->msgTextEdit->setFocus();    // 重置编辑框的鼠标光标
        // 返回获取到的信息
        return msg;
    }
    
  • 将用户进入的信息显示到控件中去

  • 用户下线消息

  • 控件功能实现

 5. 完整代码:

signin.h

#ifndef SIGNIN_H
#define SIGNIN_H
#include <QWidget>

namespace Ui {
class SignIn;
}

class SignIn : public QWidget
{
    Q_OBJECT

public:
    explicit SignIn(QWidget *parent = 0);
    ~SignIn();

    QVector<bool> isShow;    // 标记是否已经打开了聊天窗口
private:
    Ui::SignIn *ui;
};

#endif // SIGNIN_H

signin.cpp

#include "signin.h"
#include "ui_signin.h"

#include <QMessageBox>
#include <QToolButton>
#include "chatwidget.h"

SignIn::SignIn(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SignIn)
{
    ui->setupUi(this);

    // 1. 设置窗口名字、图标
    this->setWindowTitle("QQ群聊器");
    this->setWindowIcon(QIcon(":/img/qq_副本.png"));

    // 创建两个容器 分别存放网名 图片名
    QList<QString> nameList;    // 网名列表
    nameList << "星光大道" << "朝天阙" << "大路朝天" << "沙生欲夺" << "云上飞宵" << "塞尚不屈" << "罗黄泉"
             << "升星夺命" << "九曲殒命";
    QStringList iconNameList;   // 图标列表
    iconNameList << "1" << "2" << "3" << "4"
                 << "5" << "6" << "7" << "8" << "9";
    // 2. 设置九个按钮 九个用户
    QVector<QToolButton*> vector;   // 按钮的容器
    // 循环创建9个按钮 贴图、网名、透明化
    for(int i = 0; i < 9; i++)
    {
        // 创建按钮对象
        QToolButton* btn = new QToolButton(this);
        // 设置图片路径
        QString str = QString(":/img/%1.jpeg").arg(iconNameList.at(i));
        btn->setIcon(QPixmap(str)); // 给按钮设置图片
        btn->setText(nameList[i]);
        // 设置图片大小
        btn->setIconSize(QSize(80, 70));
        // 透明化按钮
        btn->setAutoRaise(true);
        // 设置图片和按钮网名显示的方式
        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        // 将按钮放到 vertricalLayout 布局中去
        ui->verticalLayout->addWidget(btn);
        // 将按钮存放到容器中去
        vector.push_back(btn);
        // 将标记是否打开聊天窗口 置成 false
        isShow.push_back(false);
    }
    // 3. 点击按钮弹出聊天窗口
    for(int i = 0; i < vector.size(); i++)
    {
        // 将按钮与聊天窗口关联起来
        connect(vector[i], &QToolButton::clicked, [=]()
        {
            // 判断是否已经打开聊天窗口
            if(isShow[i])
            {
                QMessageBox::warning(this, "警告", "该聊天窗口已打开,无法再次打开");
                return; // 退出函数
            }
            // 将标记是否已经打开聊天窗口 置成true
            isShow[i] = true;
            // 实例化聊天窗口对象
            ChatWidget* widget = new ChatWidget(nullptr, vector[i]->text());
            widget->setWindowTitle(vector[i]->text());
            widget->setWindowIcon(QIcon(vector.at(i)->icon()));
            widget->show(); // 显示窗口
            // 解决关闭聊天窗口之后无法再次打开的Bug
            // 接收关闭窗口信号
            connect(widget, &ChatWidget::closeWidget, [=]()
            {
                isShow[i] = false;  // 将标记置成false
            });
        });
    }
}

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

chatwidget.h

#ifndef CHATWIDGET_H
#define CHATWIDGET_H

#include <QWidget>
#include <QUdpSocket>


namespace Ui {
class ChatWidget;
}

class ChatWidget : public QWidget
{
    Q_OBJECT

public:
    ChatWidget(QWidget *parent, QString name);

    ~ChatWidget();

    // 1.重写关闭窗口事件
    void closeEvent(QCloseEvent *event);

signals:
    // 2.自定义关闭窗口
    void closeWidget();

public:
    // 3. 枚举 分别代表 普通信息,用户进入信息,用户离开信息
    enum MsgType{MSG, USERENTER, USERLEFT};
    // 4. 广播udp 信息 将发送的信息显示到控件 TextBrowser
    void sendMsg(MsgType type);  // 发送普通信息,用户进入信息,用户下线信息
    // 5. 获取名字
    // 6. 接收UDP 消息
    void ReceivMessage();
    // 7. 获取聊天信息 获取编辑控件中的要发送的消息
    QString getMsg();

    // 8. 用户进入  参数:用户名(网名)
    void userEnter(QString username);
    // 9. 用户下线 参数1:用户名,参数2:用户下线的时间
    void userLeft(QString username, QString time);


private:
    // 用户名 网名
    QString myName;
    // 端口号
    quint16 port;
    // 套接字
    QUdpSocket * udpSocket;

private:
    Ui::ChatWidget *ui;
};

#endif // CHATWIDGET_H

chatwidget.cpp

#include "chatwidget.h"
#include "ui_chatwidget.h"
#include <QColorDialog>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>




ChatWidget::ChatWidget(QWidget *parent, QString name) :
    QWidget(parent),
    ui(new Ui::ChatWidget)
{
    ui->setupUi(this);

    // 获取用户名(网名)通过构造函数获取
    this->myName = name;
    // 实例化套接字对象
    this->udpSocket = new QUdpSocket(this);
    // 设置端口号
    this->port = 8989;
    // 2.绑定地址与端口号
    // 采用 ShareAddress 模式 (允许其他的服务连接到相同的地址和端口)
    // 特别是用在多客户端监听同一个服务器端口号等时,特别有效, 和ReuseAddressHint 模式(重新连接服务器)
    this->udpSocket->bind(this->port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    // 3. 监听信号
    connect(this->udpSocket, &QUdpSocket::readyRead, this, &ChatWidget::ReceivMessage);
    // 4. 点击发送按钮 将信息发送出去
    connect(ui->sendBtn, &QPushButton::clicked, [=]()
    {
        // 广播普通聊天消息
        sendMsg(MSG);
    });
    // 5. 广播用户上线消息
    sendMsg(USERENTER);
    // 6. 点击退出按钮关闭窗口
    connect(ui->quitBtn, &QPushButton::clicked, this, &ChatWidget::close);

    // 按键功能设置
    // 1.字体设置
    connect(ui->fontCbx, &QFontComboBox::currentFontChanged, [=](const QFont& font)
    {
        ui->msgTextEdit->setCurrentFont(font);
        /* void QWidget::setFocus()
         * 如果此小部件或其父窗口之一是活动窗口,
         * 则将键盘输入焦点提供给此小部件(或其焦点代理)
        */
        ui->msgTextEdit->setFocus();
    });
    // 2.设置字体大小
    // 指针函数
    void (QComboBox::*cbxsingal)(const QString &text)=&QComboBox::currentTextChanged;
    connect(ui->sizeCbx, cbxsingal, [=](const QString &text)
    {
        ui->msgTextEdit->setFontPointSize(text.toDouble());
        ui->msgTextEdit->setFocus();
    });
    // 3.字体加粗
    connect(ui->boldBtn, &QPushButton::clicked, this, [=](bool checked)
    {
        if(checked) // 点击加粗按钮 加粗字体
        {
            ui->msgTextEdit->setFontWeight(QFont::Bold);
        }
        else    // 没有点击加粗按钮 字体不加粗
        {
            ui->msgTextEdit->setFontWeight(QFont::Normal);
        }
    });
    // 4.字体倾斜
    connect(ui->italicBtn, &QPushButton::clicked, this, [=](bool checked)
    {
        ui->msgTextEdit->setFontItalic(checked);
        ui->msgTextEdit->setFocus();
    });
    // 5.下划线
    connect(ui->undetlineBtn, &QPushButton::clicked, this, [=](bool checked)
    {
        ui->msgTextEdit->setFontUnderline(checked);
        ui->msgTextEdit->setFocus();
    });
    // 6.设置文字颜色
    connect(ui->colorBtn, &QPushButton::clicked, this, [=]()
    {
        QColor color = QColorDialog::getColor(color, this);
        ui->msgTextEdit->setTextColor(color);
    });
    // 7.清空聊天记录
    connect(ui->clearBtn, &QPushButton::clicked, [=]()
    {
        ui->msgBrowser->clear();
    });
    // 8.保存聊天信息 打开文件 写文件
    connect(ui->savdBtn, &QPushButton::clicked, [=]()
    {
        // 判断要保存的聊天信息为空
        /*  document : QTextDocument *
         * 此属性保存文本编辑器的基础文档。
           注意:编辑器不拥有文档的所有权,除非它是文档的父对象。
           所提供文档的父对象仍然是该对象的所有者。如果先前指定的文档是编辑器的子文档,
            则将删除该文档
        */
        if(ui->msgBrowser->document()->isEmpty())
        {
            QMessageBox::warning(this, "警告", "要保存的信息不能为空!");
        }
        else
        {
            QString fileName = QFileDialog::getSaveFileName(this, "保存聊天记录", "聊天记录", "(*.txt)");
            if(!fileName.isEmpty())
            {
                // 保存名称不能为空
                QFile file(fileName);
                file.open(QIODevice::WriteOnly | QFile::Text);
                QTextStream stream(&file);
                stream << ui->msgBrowser->toPlainText();
                file.close();
            }
        }
    });
}

ChatWidget::~ChatWidget()
{
    delete ui;
}
// 重写关闭窗口事件 将关闭窗口信号发生出去
void ChatWidget::closeEvent(QCloseEvent *event)
{
    emit this->closeWidget();
    // 广播下线消息
    sendMsg(USERLEFT);
    // 关闭套接字
    udpSocket->close();
    // 销毁套接字
    udpSocket->destroyed();
}
// 4. 广播udp 信息 将发送的信息显示到控件 TextBrowser
void ChatWidget::sendMsg(MsgType type)
{
    // QDataStream 是数据流,相当于数据管道,屏蔽了数据转换过程
    QByteArray array;   // 创建一个数组 装数据
    // 创建流 好处是可以分段 参数1:流入地址, 参数2:以只写的方式
    QDataStream stream(&array, QIODevice::WriteOnly);
    // 流入类型和用户名
    stream << type << this->myName; // 将网名流入到数据流中去
    //qDebug() << "测试 << this->myName : " << this->myName;

    // 分类 类型消息处理
    switch (type)
    {
    case MSG:
        // 判断编辑框输入的消息是否为空
        if(ui->msgTextEdit->toPlainText() == "")
        {
            QMessageBox::warning(this, "警告", "发送的信息不能为空");
            return; // 退出函数
        }
        stream << this->getMsg();   // 流入信息 普通的聊天信息
        //qDebug() << "测试 << this->getMsg() : " << this->getMsg();
        break;
    case USERENTER:
        break;
    case USERLEFT:
        break;
    default:
        break;
    }
    // 发送数据报 书写报文 QHostAddress::Broadcast --> 发送给所有人
    udpSocket->writeDatagram(array.data(), array.size(), QHostAddress::Broadcast, this->port);
}
// 6. 接收UDP 消息
void ChatWidget::ReceivMessage()
{
    // 获取 第一个挂起的UDP 数据报的大小
    qint64 size = udpSocket->pendingDatagramSize();
    // 将 qint64 转换成 int
    int mysize = static_cast<int>(size);
    // 定义一个数组用于接收数据流的数据 将元素全部置成0
    QByteArray array = QByteArray(mysize, 0);   // 大小为mysize 第一个挂起的数据报的大小
    /* QUdpSocket::readDatagram 接收数据报
     * 接收不大与 maxSize 字节的数据并存储在数据中,发送方的主机地址和端口号存储在 *地址和*端口号中(除非指针为0)
     * 成功返回数据报的大小; 否则返回-1
     * 如果 maxsize 太小,则数据报的其余部分将丢失。为了避免数据丢失,请在尝试读取挂起的数据报之前
     * 调用 penddingDatagramSize() 确定关起的数据报的大小,如果 maxSize 为0, 则数据报将被丢弃
    */
    // 接收数据报
    udpSocket->readDatagram(array.data(), size);
    // 以只读的方式将信息流入到数组中去
    QDataStream stream(&array, QIODevice::ReadOnly);
    // 声明一个变量 通过 数据流的方式获取 消息类型
    int msgtype;
    //从数据流中流出类型 读取类型
    stream >> msgtype;  // 获取消息类型
    //qDebug() << "测试 QDataStream ----> " << msgtype;
    // 声明 用户名 聊天信息
    QString name, msg;
    // 格式化时间类型
    QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    // 分类处理
    switch(msgtype)
    {
    case MSG:   // 普通消息
        // 从数据流中流出 网名和聊天信息
        stream >> name >> msg;  // 获取网名 消息
//        qDebug() << "测试name -->" << name;
//        qDebug() << "测试msg -->" << msg;
        // 将信息追加到控件 TextBorwser 中去(显示聊天信息的控件)
        ui->msgBrowser->setTextColor(Qt::blue); // 设置显示信息的颜色
        ui->msgBrowser->setCurrentFont(QFont("方正粗黑宋简体", 11));
        // 追加网名 和 时间
        ui->msgBrowser->append("[" + name + "]" + time);
        // 追加聊天内容
        ui->msgBrowser->append(msg);
        break;
    case USERENTER: // 用户进入
        // 从数据流中流出 用户名
        stream >> name;
        userEnter(name);
        break;
    case USERLEFT:  // 用户离开  下线
        // 从数据流中流出 用户名
        stream >> name;
        userLeft(name, time);
        break;
    }
}
// 7. 获取聊天信息 获取编辑控件中的要发送的消息
QString ChatWidget::getMsg()
{
    // 从编辑控件中获取输入的信息
    // toPlainText()【简单文本】 和 toHtml() 的区别是,toHtml()可以设置特殊标点,分段等(富文本)
    QString msg = ui->msgTextEdit->toHtml();
    // 清空编辑框控件
    ui->msgTextEdit->clear();
    // 设置光标
    ui->msgTextEdit->setFocus();    // 重置编辑框的鼠标光标
    // 返回获取到的信息
    return msg;
}
// 8. 用户进入  参数:用户名(网名)
void ChatWidget::userEnter(QString username)
{
    // 查询 item 项中有没有匹配的
    bool isEmpty = ui->tableWidget->findItems(username, Qt::MatchExactly).isEmpty();
    if(isEmpty) // 为空时,显示新进入的用户(网名)
    {
        QTableWidgetItem * user = new QTableWidgetItem(username);
        // 插入行
        ui->tableWidget->insertRow(0);
        // 设置 item 用户名
        ui->tableWidget->setItem(0, 0, user);
        // 将上线的用户名显示到聊天控件中
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->append(username + " 用户以上线");
        // 显示在线人数到便签中去
        ui->useNumlabel->setText(QString("在线人数:%1人").arg(ui->tableWidget->rowCount()));
        // 发送消息 广播用户上线消息
        sendMsg(USERENTER);
    }
}
// 9. 用户下线 参数1:用户名,参数2:用户下线的时间
void ChatWidget::userLeft(QString username, QString time)
{
    // 1.显示用户控件QTaleWidget 中的删除下线用户所在的行
    // 2.显示用户下线消息,用户名+时间
    // 查询控件 QTableWidget 中项有没有匹配到
    bool isEmpty = ui->tableWidget->findItems(username, Qt::MatchExactly).isEmpty();
    if(!isEmpty)
    {
        // 寻找行
        int row = ui->tableWidget->findItems(username, Qt::MatchExactly).first()->row();
        // 移除该行
        ui->tableWidget->removeRow(row);
        // 将下线的用户 显示聊天信息的控件中去
        ui->msgBrowser->setTextColor(Qt::green);
        ui->msgBrowser->setFont(QFont("叶根友毛笔行书2.0"));
        ui->msgBrowser->append(username + "用户名于" + time + "下线");
        // 显示在线人数的便签
        ui->useNumlabel->setText(QString("在线人数:%1人").arg(ui->tableWidget->rowCount()));
    }
}

程序运行结果:

 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值