27.Qt-MyChat07 用户离开

本文详细介绍了一个基于Qt的聊天室应用程序的实现过程,包括用户交互界面设计、UDP消息广播与接收、用户在线状态更新及聊天记录管理等功能。通过具体的代码示例,展示了如何使用QUdpSocket进行网络通信,实现用户之间的实时聊天。

用户离开
    提供用户处理函数
    更新右侧tablewidget
    更新聊天记录
    更新在线用户
    在closeEvent事件中发送用户离开的消息
    端口套接字关闭
    离开按钮关闭窗户

main.cpp

#include "widget.h"
#include <QApplication>
#include "dialoglist.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //Widget w;
    //w.show();
    DialogList list;
    list.show();

    return a.exec();
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDataStream>
#include <QMessageBox>
#include <QDateTime>

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(this->port,QUdpSocket::ShareAddress |QUdpSocket::ReuseAddressHint);

    //发送新用户进入
    sndMsg(UsrEnter);
    //点击发送按钮发送消息
    connect(ui->sendBtn,&QPushButton::clicked,[=](){
        sndMsg(Msg);
    });

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

    //点击退出按钮 实现关闭窗口
    connect(ui->exitBtn,&QPushButton::clicked,[=](){
        this->close();
    });
}

//接受UDP的消息
void Widget::ReceiveMessage()
{
    //拿到数据报文
    //获取长度
    qint64 size = udpSocket->pendingDatagramSize();
    QByteArray array = QByteArray(size,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:
            //更新右侧TableWidget
            stream >> usrName;
            usrEnter(usrName);

            break;
        case UsrLeft:
            stream >> usrName;
            usrLeft(usrName,time);
            break;
        default:
            break;
    }
}

void Widget::usrLeft(QString usrname,QString time)
{
    //更新右侧tableWidget
    bool isEmpty = ui->usrTblWidget->findItems(usrname,Qt::MatchExactly).isEmpty();
    if(!isEmpty)
    {
        int row = ui->usrTblWidget->findItems(usrname,Qt::MatchExactly).first()->row();
        ui->usrTblWidget->removeRow(row);

        //追加聊天记录
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->append(QString("%1 于 %2 离开").arg(usrname).arg(time));
        //在线人数更新
        ui->usrNumLbl->setText(QString("在线用户:%1人").arg(ui->usrTblWidget->rowCount()));

    }

}

//处理新用户加入
void Widget::usrEnter(QString username)
{
    //更新右侧TableWidget

    //判断右侧的列表是不是空的,空的就添加内容
    bool isEmpty1 = ui->usrTblWidget->findItems(username,Qt::MatchExactly).isEmpty();
    if(isEmpty1)
    {
        QTableWidgetItem * usr = new QTableWidgetItem(username);

        //插入行
        ui->usrTblWidget->insertRow(0);
        ui->usrTblWidget->setItem(0,0,usr);

        //追加聊天记录
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->append( QString("%1 上线了").arg(username));
        //在线人数更新
        ui->usrNumLbl->setText(QString("在线用户:%1人").arg(ui->usrTblWidget->rowCount()));

        //把自身信息广播出去
        sndMsg(UsrEnter);
    }

}

//广播UDP消息
void Widget::sndMsg(MsgType type)
{
    //发送的消息分为三种数据类型
    //发送的数据 做分段处理 第一段:类型  第二段:用户名 第三段 具体内容
    QByteArray array;
    QDataStream stream(&array, QIODevice::WriteOnly);

    stream << type << getUsr() ; //第一段内容 添加到流中 第二段 用户名
    switch(type){
    case Msg://发送普通消息
        if(ui->msgTxtEdit->toPlainText() == "") //判断如果用户没有输入内容,不发任何消息
                {
                    QMessageBox::warning(this,"警告","发送内容不能为空");
                    return ;
                }
        //第三段数据,具体说的话
        stream <<getMsg();
        break;
    case UsrEnter://发送新用户进入的消息
        break;
    case UsrLeft://发送用户离开的消息
        break;
    default:
        break;
    }

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

//获取到用户名
QString Widget::getUsr()
{
    return this->uName;
}

//获取聊天信息
QString Widget::getMsg()
{
    QString str = ui->msgTxtEdit->toHtml();
    //清空输入框
    ui->msgTxtEdit->clear();
    ui->msgTxtEdit->setFocus();

    return str;
}

//重写关闭窗口的事件
void Widget::closeEvent(QCloseEvent * e)
{

    emit this->closeWidget();
    sndMsg(UsrLeft);
    //断开套接字
    udpSocket->close();
    udpSocket->destroyed();

    QWidget::closeEvent(e);
}

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

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

    //分别代表 聊天信息、新用户加入、用户退出
    enum MsgType {Msg,UsrEnter,UsrLeft};

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

private:
    Ui::Widget *ui;

signals:
    //关闭窗口发送关闭信息
    void closeWidget();

public:
    //关闭事件
    void closeEvent(QCloseEvent *);

public:
   void sndMsg(MsgType type); //广播UDP消息
   void usrEnter(QString username);//处理新用户加入
   void usrLeft(QString usrname,QString time); //处理用户离开
   QString getUsr(); //获取用户名
   QString getMsg(); //获取聊天信息
private:
   QUdpSocket * udpSocket; //udp套接字
   qint16 port; //端口
   QString uName; //用户名

    void ReceiveMessage();   //接受UDP消息
};

#endif // WIDGET_H

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("MyChat");
    //设置图标
    setWindowIcon(QPixmap(":/images/qq.png"));

    //准备图标
    QList<QString>nameList;
    nameList << "aaaaaa1111" << "aaaaaa2222" <<"aaaaaa3333"<<"aaaaaa4444"<<"aaaaaa5555"
             <<"aaaaaa6666"<<"aaaaaa7777"<<"aaaaaa8888"<<"aaaaaa9999";
    QStringList iconNameList; //图标资源列表
        iconNameList << "ftbz"<< "ymrl" <<"qq" <<"Cherry"<< "dr"
                     <<"jj"<<"lswh"<<"qmnn"<<"wy";

    QVector <QToolButton *> vToolBtn;

    for(int i = 0; i < 9; i++)
    {
        //设置头像
        QToolButton * btn = new QToolButton;
        //设置文字
        //btn->setText("aaaaaaa");
        btn->setText(nameList[i]);
        //设置头像
        //btn->setIcon(QPixmap(":/images/ftbz.png"));
        //设置头像
        QString str = QString(":/images/%1.png").arg(iconNameList.at(i));
        btn->setIcon(QPixmap(str));
        //设置头像大小
        btn->setIconSize(QPixmap(":/images/ftbz.png").size());
        //设置按钮的风格  透明
        btn->setAutoRaise(true);
        //设置文字和图片一起显示
        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        //加到垂直布局中
        ui->vLayout->addWidget(btn);
        //将创建的button加入到容器中,保存储存的按钮
        vToolBtn.push_back(btn);
        //9个标示默认初始化
        isShow.push_back(false);
    }

    //对9个按钮 进行添加信号槽
    for(int i = 0 ; i < vToolBtn.size();i++)
    {
        connect(vToolBtn[i],&QToolButton::clicked,[=](){
            //如果被打开了,就不要再次打开
            if(isShow[i])
            {
                QString str = QString("%1窗口已经被打开了").arg(vToolBtn[i]->text());
                QMessageBox::warning(this,"警告",str);
                return;
            }

            isShow[i] = true;

            //弹出聊天对话框
            //构造聊天窗口时候 告诉这个窗口他的名字 参数1  顶层方式弹出 参数2  窗口名字
            //注意! Widget构造函数 并没有这两个参数,需要重新写构造函数,增加一个新的参数QString name
            Widget * widget = new Widget(0,vToolBtn[i]->text());
            //设置窗口标题
            widget->setWindowTitle(vToolBtn[i]->text());
            widget->setWindowIcon(vToolBtn[i]->icon());
            widget->show();

            //监听窗口关闭的信息,发送者是widget发送的
            connect(widget,&Widget::closeWidget,[=](){

                    isShow[i] = false;
            });

        });

    }
}



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

dialoglist.h

#ifndef DIALOGLIST_H
#define DIALOGLIST_H

#include <QWidget>

namespace Ui {
class DialogList;
}

class DialogList : public QWidget
{
    Q_OBJECT

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

private:
    Ui::DialogList *ui;
    QVector< bool> isShow;
};

#endif // DIALOGLIST_H

 

下载前必看:https://pan.quark.cn/s/a4b39357ea24 在本资料中,将阐述如何运用JavaScript达成单击下拉列表框选定选项后即时转向对应页面的功能。 此种技术适用于网页布局中用户需迅速选取并转向不同页面的情形,诸如网站导航栏或内容目录等场景。 达成此功能,能够显著改善用户交互体验,精简用户的操作流程。 我们须熟悉HTML里的`<select>`组件,该组件用于构建一个选择列表。 用户可从中选定一项,并可引发一个事件来响应用户的这一选择动作。 在本次实例中,我们借助`onchange`事件监听器来实现当用户在下拉列表框中选定某个选项时,页面能自动转向该选项关联的链接地址。 JavaScript里的`window.location`属性旨在获取或设定浏览器当前载入页面的网址,通过变更该属性的值,能够实现页面的转向。 在本次实例的实现方案里,运用了`eval()`函数来动态执行字符串表达式,这在现代的JavaScript开发实践中通常不被推荐使用,因为它可能诱发安全问题及难以排错的错误。 然而,为了本例的简化展示,我们暂时搁置这一问题,因为在更复杂的实际应用中,可选用其他方法,例如ES6中的模板字符串或其他函数来安全地构建和执行字符串。 具体到本例的代码实现,`MM_jumpMenu`函数负责处理转向逻辑。 它接收三个参数:`targ`、`selObj`和`restore`。 其中`targ`代表要转向的页面,`selObj`是触发事件的下拉列表框对象,`restore`是标志位,用以指示是否需在转向后将下拉列表框的选项恢复至默认的提示项。 函数的实现通过获取`selObj`中当前选定的`selectedIndex`对应的`value`属性值,并将其赋予`...
你当前位于一个 WordPress 插件目录 `wp-im-plugin`,从文件结构来看,这是一个名为 **WP-IM Plugin** 的插件,可能用于实现即时通讯(Instant Messaging)功能。你想通过终端命令来“测试”或“探测”这个插件有哪些功能。 虽然不能像浏览器那样直接运行插件的功能,但你可以使用终端工具分析代码、查看入口点、依赖关系和潜在行为。以下是你可以执行的终端命令及其用途,帮助你了解该插件的功能: --- ### ✅ 1. 查看主插件文件(入口) ```bash cat wp-im-plugin.php ``` 这是 WordPress 插件的主文件,通常包含插件信息头(如名称、版本、作者)以及钩子注册逻辑。 #### 示例输出片段: ```php <?php /** * Plugin Name: WP IM Plugin * Description: Real-time instant messaging for WordPress. * Version: 1.0.0 * Author: Some Dev */ ``` 这会告诉你插件的基本功能描述。 --- ### ✅ 2. 搜索关键功能关键词(如 AJAX、WebSocket、API 路由等) #### 查找是否使用了 WebSocket 或后端服务: ```bash grep -r "im-server.php" . ``` 你已经看到有 `im-server.php` 文件,可能是独立运行的 WebSocket/长连接服务。 查看其内容: ```bash cat im-server.php ``` 很可能是一个基于 `ReactPHP`、`Ratchet`(PHP WebSocket 库)的服务脚本。例如: ```php require_once 'vendor/autoload.php'; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; $server = IoServer::factory( new HttpServer(new WsServer(new MyChat())), 8080 ); $server->run(); ``` 如果是这样,说明它启动了一个实时通信服务器。 --- ### ✅ 3. 查看 composer.json 看用了哪些库 ```bash cat composer.json ``` 常见输出示例: ```json { "require": { "cboden/ratchet": "^0.4", "react/socket": "^1.8" } } ``` → 表明使用了 **Ratchet**,即 PHP 的 WebSocket 实现 → 插件支持 **实时聊天功能**。 --- ### ✅ 4. 检查是否有 CLI 可执行脚本(比如启动服务) ```bash find . -type f -name "*.php" | xargs grep -l "php\\s*\\-q" ``` 或者手动检查 `im-server.php` 是否可作为守护进程运行: ```bash php im-server.php ``` 尝试运行它(在后台): ```bash nohup php im-server.php > im.log 2>&1 & ``` 然后检查日志: ```bash tail -f im.log ``` 如果成功监听某个端口(如 `0.0.0.0:8080`),则说明它是独立的消息服务器。 --- ### ✅ 5. 检查权限敏感文件:`.secret.key` ```bash cat .secret.key ``` ⚠️ 注意:这个文件只有 `rw-------`(仅所有者可读写),说明它存储了密钥、token 或加密盐值。 输出可能是类似: ``` a1b2c3d4e5f6g7h8 ``` 这通常用于签名 JWT token 或验证客户端身份。 --- ### ✅ 6. 分析 vendor 目录中的依赖 ```bash ls vendor/ ``` 结合 `composer.json` 和 `vendor/composer/autoload_psr4.php` 可以看出加载了哪些类。 --- ### ✅ 7. 全局搜索功能关键词 #### 查看是否与用户登录集成: ```bash grep -r "current_user" . | grep -v vendor ``` #### 查看是否添加了短代码(shortcode): ```bash grep -r "add_shortcode" . ``` #### 查看是否创建了数据库表或设置 API: ```bash grep -r "wpdb" . grep -r "rest_api" . grep -r "register_rest_route" . ``` 这些能判断是否提供了 REST 接口供前端调用。 --- ### ✅ 8. 测试插件是否提供 Web API 接口 如果有 `register_rest_route`,说明可以通过 HTTP 请求访问功能。 你可以用 `curl` 测试: ```bash curl -X GET http://yfw_szrengjing_com/wp-json/im/v1/status ``` 替换为你实际发现的路由路径。 --- ### ✅ 总结:根据你的目录结构推测的功能 | 文件/特征 | 功能推断 | |----------------------|--------| | `im-server.php` | 启动 WebSocket 服务,实现即时通讯 | | `.secret.key` | 安全认证密钥,防止非法连接 | | `composer.json` + `vendor` | 使用第三方库(如 Ratchet)处理实时通信 | | `wp-im-plugin.php` | WordPress 插件入口,注册脚本、样式、短代码或 REST API | | `assets/` | 前端资源(JS/CSS),可能包含聊天界面 | | `includes/` | 包含业务逻辑,如消息处理、用户状态管理 | --- ### 🔍 最终建议操作流程: ```bash # 1. 查看主文件 cat wp-im-plugin.php # 2. 查看服务器脚本 cat im-server.php # 3. 查看依赖 cat composer.json # 4. 检查是否启用 REST API grep -r "register_rest_route" . # 5. 检查短代码 grep -r "add_shortcode" . # 6. 尝试运行服务(注意不要阻塞终端) php im-server.php ``` 若 `im-server.php` 正常运行并监听端口,则此插件为 **WordPress 实时聊天插件**,需确保 PHP 扩展(如 `ext-sockets`, `pcntl`)已安装,并且防火墙开放对应端口(如 8080)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值