• 40.qt quick- 高仿微信实现局域网聊天V4版本(支持gif动图表情包、消息聊天、拖动缩放窗口、支持Linux编译)


    在上章37.qt quick- 高仿微信实现局域网聊天V3版本(添加登录界面、UDP校验登录、皮肤更换、3D旋转),我们已经实现了:

    • 添加登录界面、
    • UDP校验登录、
    • 皮肤更换、
    • 3D旋转(主界面和登录界面之间切换) 、

    所以本章实现:

    • 支持拖动和更改窗口大小、
    • 可以单独聊天、也可以在聊天室所有人聊天、
    • 支持收发gif表情包(支持粘贴复制)、
    • 自动刷新当前好友在线人数等、
    • 同时支持Linux和Windows编译运行

    1.界面展示

    界面布局如下所示:

    界面截图如下所示:

    效果图如下所示:

    有点大,可能加载不了,不过已经上传到bilibili了https://www.bilibili.com/video/BV1Ao4y1S7zX

    由于代码量有点多,所以讲解重点的部分

    2.支持Linux编译

    我这里是交叉编译到树莓派运行,在Linux下则取消了3D旋转,如下图所示:

     动图就不上传了,视频已经上传到bilibili了 https://www.bilibili.com/video/BV1JU4y1H7xc/

    3.Text中的gif管理

    3.1 在Text中加入一个gif

    // 添加一个gif
    void GifTextHandler::inset(QString fileName)
    {
        if (!m_documnt)
            return;
    
        QTextCursor  cursor = QTextCursor(m_documnt->textDocument());
        cursor.setPosition(m_cursorStart);
        if (m_cursorStart > m_cursorEnd) {
            cursor.setPosition(0);
        } else if (m_cursorStart != m_cursorEnd)
            cursor.setPosition(m_cursorEnd, QTextCursor::KeepAnchor);
    
        addMovie(fileName); // 通过QMovie加载一个gif
        QTextImageFormat imageFormat;
        imageFormat.setName(fileName);
        imageFormat.setWidth(m_width);
        imageFormat.setHeight(m_height);
        cursor.insertImage(imageFormat, QTextFrameFormat::InFlow);
    
    }

    添加一个gif后,又如何去管理这个gif,假如我当前文本把这个gif删除了,那么这个QMovie也应该释放才行,否则就内存溢出啦.

    3.2 gif动态释放与管理

    所以每隔1秒就去读取下文本,gif是否还存在,如果不存在则释放QMovie:

            QList<QString> list = m_movies.keys();
            for(int i=0;i<list.length();i++) {
                 m_movies[list[i]]->setProperty("status",false);
            }
    
            QTextBlock block = m_documnt->textDocument()->firstBlock();
            QVector<QTextFormat> allFormats = m_documnt->textDocument()->allFormats();
            while(block.isValid()) {
                 QTextBlockFormat blockFmt = block.blockFormat();
                 for(QTextBlock::iterator it = block.begin(); !it.atEnd(); it++) {
                    QTextCharFormat charFmt = it.fragment().charFormat();   // fragment是存放文字,位置的类
                    if (charFmt.objectType() == QTextFormat::ImageObject) {
                        QString file = charFmt.property(QTextFormat::ImageName).toString();
                        if (!m_movies.contains(file)) {
                            addMovie(file);
                        }
                        m_movies[file]->setProperty("status",true);
                       // qDebug()<<"status: "<<file;
                    }
                 }
                 block = block.next();
            }
    
            for(int i=0;i<list.length();i++) {
                if (m_movies[list[i]]->property("status") == false) {
                        qDebug()<<"释放gif:"<<i<<list[i];
                        delete m_movies[list[i]];
                        m_movies.remove(list[i]);
                        continue;
                }
            }

    如果文本释放了,那么就遍历整个gif表,释放所有:

    GifTextHandler::~GifTextHandler()   // 释放资源
    {
        QList<QString> list = m_movies.keys();
        for(int i=0;i<list.length();i++) {      // 释放所有GIF
                delete m_movies[list[i]];
        }
        m_movies.clear();
    }

    3.3 文本发送

    由于文本中包含了gif图,所以发送的时候,我们需要将文本内容(包含gif)进行编码,转换成字符串,代码如下所示:

    QString GifTextHandler::coding()
    {
        QString ret("");
        QTextBlock block = m_documnt->textDocument()->firstBlock();
        QVector<QTextFormat> allFormats = m_documnt->textDocument()->allFormats();
    
        while(block.isValid()) {
             for(QTextBlock::iterator it = block.begin(); !it.atEnd(); it++) {
                QTextCharFormat charFmt = it.fragment().charFormat();   // fragment是存放文字,位置的类
                if (charFmt.objectType() == QTextFormat::ImageObject) {
                 ret.append("["+charFmt.property(QTextFormat::ImageName).toString().remove(GIF_PREFIXDIR)+"]");
                } else {
                    ret.append(it.fragment().text());
                }
             }
             if (block.next().isValid())    // QTextBlock以换行为分割成每个块,所以每次块遍历完后需要加换行符
                ret.append("
    ");
    
             block = block.next();
        }
        return ret;
    }

    3.4 文本接收

    由于接收到的数据是一段字符串,所以我们需要解码,将gif标志显示成一个gif动图,代码如下所示:

    void GifTextHandler::encoding(QString text)
    {
        if (!m_documnt) {
            qDebug()<<"encoding: m_documnt == nullptr";
            return;
        }
        QTextCursor  cursor = QTextCursor(m_documnt->textDocument());
        cursor.setPosition(0);
        QRegularExpression re("\[\d+\.gif\]");
        QRegularExpressionMatch match;
        int offset = 0;
        int fileNum;
        QString file;
        QString ret;
        while (offset < text.length()) {
            match = re.match(text,offset);
            if (match.hasMatch()) {
                sscanf(match.captured(0).toLocal8Bit(),"[%d.gif]", &fileNum);
                file = QString(GIF_PREFIXDIR+"%1.gif").arg(fileNum);
                cursor.insertText(text.mid(offset, match.capturedStart(0) - offset));   // 添加前面的
                ret.append(text.mid(offset, match.capturedStart(0) - offset));
                addMovie(file);
                QTextImageFormat imageFormat;
                imageFormat.setName(file);
                imageFormat.setWidth(m_width);
                imageFormat.setHeight(m_height);
                cursor.insertImage(imageFormat, QTextFrameFormat::InFlow);
                offset = match.capturedEnd(0);
            } else {
                ret.append(text.mid(offset, text.length() - offset));
                cursor.insertText(text.mid(offset, text.length() - offset));   // 添加前面的
                break;
            }
        }
    }

    4.C++ QAbstractListModel类

    qml中使用的是ListView,而model数据是使用C++ model(继承于QAbstractListModel).

    所以当有好友上线时,我们需要往model中添加一行好友数据,并通知ListView刷新局部数据,代码如下所示:

    void FriendModel::addFriend(MessageDesc* msg)
    {
        for (int i=1; i < m_data.count(); i++) {
            if (m_data[i]->title() == msg->srcUser) {
                return;
            }
        }
        // 将头像 Arr数据转换成jpg文件
        QPixmap pixmap;
        QString str = AppCfg::getInstance()->read(AppCfg::FriendHeadDir).remove("file:///")+msg->srcUser+".jpg";
        qDebug()<<msg->headArr.length()<<pixmap.loadFromData((uchar *)msg->headArr.data(), msg->headArr.length());
        bool ret = pixmap.save(str);
        AppCfg::getInstance()->fileLogWrite(QString("addFriend str%1  str%2 ret%3").arg(str).arg(QUrl::fromLocalFile(str).toString()).
                                            arg(ret));
    
        beginInsertRows(QModelIndex(), 1, 1);   // 由于聊天室始终顶置,所以只能插入到第2行
        m_data.insert(1, new FriendChatData(msg->srcUser, QUrl::fromLocalFile(str).toString()));
        endInsertRows();
        hintInsetChatRoom("""+msg->srcUser+"" 于"+QDateTime::currentDateTime().toString(" hh:mm ")+"上线!");
    }

    当好友下线时,则需要删除好友数据,代码如下所示:

    void FriendModel::removeFriend(MessageDesc* msg)
    {
        for (int i=1; i < m_data.count(); i++) {
            if (m_data[i]->title() == msg->srcUser) {
                beginRemoveRows(QModelIndex(), i, i);
    
                // 假如聊天界面的model等于当前下线的好友,那么需要释放聊天model数据,并刷新视图
                if (m_chat != NULL && m_chat->data() == m_data[i]) {
                    delete m_chat;
                    m_chat = NULL;
                    emit removeFriendcloseChat();
                }
                delete m_data[i];
                m_data.removeAt(i);
                endRemoveRows();
                hintInsetChatRoom("""+msg->srcUser+"" 于"+QDateTime::currentDateTime().toString(" hh:mm ")+"下线!");
                break;
            }
        }
    }

    其它的好友收发消息,则依葫芦画瓢,通通实现一遍即可.

     

     


    人间有真情,人间有真爱。

    如果您喜欢这里,感觉对你有帮助,并且有多余的软妹币的话,不妨投个食吧,赞赏的时候,留下美句和你的博客地址哦~   戳这里看谁投食了


  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/lifexy/p/15065339.html
Copyright © 2020-2023  润新知