【Qt聊天室客户端】消息功能--发布程序

1. 获取文件内容

主要目标是实现获取内容二进制数据的接口,主要是为后面的消息功能提供服务

具体实现

客户端发送请求

服务端处理请求,同时支持三种数据类型

客户端处理服务端的响应

2. 发送图片消息

客户端与服务端的通信约定

客户端从服务器中获取图片消息的时候的,仅返回消息中的文件ID,而不是直接包含文件内容。如果需要文件的实际内容则需要客户端进行二次请求来获取相应的数据

设计目的为了减少初始消息传输体积,从而提高传输效率。客户端和服务端通信,直接传输问价的时候,可能会影响性能,通过文件ID二次获取内容,确保消息的基本信息与文件数据分开传输,从而避免占用过多带宽

服务器和服务器之间,直接附带文件内容,而不会单独的通过文件ID请求。因为服务器的网络环境是相对稳定的,传输文件不会造成较大的性能问题

客户端界面发送图片消息实现

整体流程首先是初始化显示图片的控件,然后配置其样式。然后异步加载图片,如果图片数据没有加载那么就异步获取图片内容;如果获取了图片数据,那么就出发updateUI进行页面更新。最后根据父组件的大小在页面上进行绘制

MessageImageLabel::MessageImageLabel(const QString &fileId, const QByteArray &content, bool isLeft):fileId(fileId),content(content),isLeft(isLeft)
{this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);imageBtn = new QPushButton(this);imageBtn->setStyleSheet("QPushButton { border: none; }");if(content.isEmpty()){DataCenter* dataCenter = DataCenter::genInstance();connect(dataCenter, &DataCenter::getSingleFileDone, this, &MessageImageLabel::updateUI);dataCenter->getSingleFileAsync(fileId);}
}void MessageImageLabel::updateUI(const QString &fileId, const QByteArray &content)
{//不是当前FileIDif(this->fileId != fileId){return;}this->content = content;this->update();
}void MessageImageLabel::paintEvent(QPaintEvent *event)
{(void)event;//1.根据父控件计算图片最大宽度QObject* object = this->parent();if(!object->isWidgetType()){return;}QWidget* parent = dynamic_cast<QWidget*>(object);int width = parent->width()*0.6;//最大宽度设置为父控件的0.6倍//2.加载图片数据QImage image;if(content.isEmpty()){//图片数据为空的时候,加载默认图片QByteArray tmpContent = loadFileToByteArray(":/resource/image/image.png");image.loadFromData(tmpContent);}else{image.loadFromData(content);}//3.根据父控件宽度缩放照片int height = 0;if(image.width() > width){height = static_cast<int>(((double)image.height() / image.width()) * width);}else{width = image.width();height = image.height();}//4.将QImage转换为QPixmapQPixmap pixmap = QPixmap::fromImage(image);imageBtn->setIconSize(QSize(width,height));imageBtn->setIcon(QIcon(pixmap));//5.动态调整父组件高度parent->setFixedHeight(height + 50);//6.根据消息类型调整按钮位置if(isLeft){//左侧消息靠左显示imageBtn->setGeometry(10,0,width,height);}else{//右侧消息靠右显示int leftPos = this->width() - width -10;imageBtn->setGeometry(leftPos,0,width,height);}}

websocket推送图片消息实现

总体逻辑仍然通过按钮触发信号,然后通过服务器槽函数进行处理,向客户端推送图片消息,最后客户端对接收到的响应进行处理即可(处理响应已经在获取文件内容进行了统一处理)

3. 发送文件消息

具体实现

 点击图片按钮触发该处点击逻辑

通过客户端发送请求到服务端

在消息显示区中,将文件信息显示上去

4. 语音消息

4.1 录制音频

 具体实现

实现鼠标按下录制,松开完成录制的功能

发送语音逻辑

4.2 播放音频

具体实现

点击语音消息的时候触发该处逻辑

更新UI

4.3 语音转文字

 具体实现

补充:音频代码

#ifndef SOUNDRECORDER_H
#define SOUNDRECORDER_H#include <QObject>
#include <QStandardPaths>
#include <QFile>
#include <QAudioSource>
#include <QAudioSink>
#include <QMediaDevices>class SoundRecorder : public QObject
{Q_OBJECT
public:const QString RECORD_PATH = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/sound/tmpRecord.pcm";const QString PLAY_PATH = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/sound/tmpPlay.pcm";public:static SoundRecorder* getInstance();//// 录制语音语音/// 开始录制void startRecord();// 停止录制void stopRecord();private:static SoundRecorder* instance;explicit SoundRecorder(QObject *parent = nullptr);QFile soundFile;QAudioSource* audioSource;//// 播放语音/
public:// 开始播放void startPlay(const QByteArray& content);// 停止播放void stopPlay();private:QAudioSink *audioSink;QMediaDevices *outputDevices;QAudioDevice outputDevice;QFile inputFile;signals:// 录制完毕后发送这个信号void soundRecordDone(const QString& path);// 播放完毕发送这个信号void soundPlayDone();};#endif // SOUNDRECORDER_H
#include "soundrecorder.h"
#include <QDir>
#include <QMediaDevices>#include "model/data.h"
#include "toast.h"/
/// 单例模式
/
SoundRecorder* SoundRecorder::instance = nullptr;SoundRecorder *SoundRecorder::getInstance()
{if (instance == nullptr) {instance = new SoundRecorder();}return instance;
}// 播放参考 https://www.cnblogs.com/tony-yang-flutter/p/16477212.html
// 录制参考 https://doc.qt.io/qt-6/qaudiosource.html
SoundRecorder::SoundRecorder(QObject *parent): QObject{parent} {// 1. 创建目录QDir soundRootPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));soundRootPath.mkdir("sound");// 2. 初始化录制模块soundFile.setFileName(RECORD_PATH);QAudioFormat inputFormat;inputFormat.setSampleRate(16000);inputFormat.setChannelCount(1);inputFormat.setSampleFormat(QAudioFormat::Int16);QAudioDevice info = QMediaDevices::defaultAudioInput();if (!info.isFormatSupported(inputFormat)) {LOG() << "录制设备, 格式不支持!";return;}audioSource = new QAudioSource(inputFormat, this);connect(audioSource, &QAudioSource::stateChanged, this, [=](QtAudio::State state) {if (state == QtAudio::StoppedState) {// 录制完毕if (audioSource->error() != QAudio::NoError) {LOG() << audioSource->error();}}});// 3. 初始化播放模块outputDevices = new QMediaDevices(this);outputDevice = outputDevices->defaultAudioOutput();QAudioFormat outputFormat;outputFormat.setSampleRate(16000);outputFormat.setChannelCount(1);outputFormat.setSampleFormat(QAudioFormat::Int16);if (!outputDevice.isFormatSupported(outputFormat)) {LOG() << "播放设备, 格式不支持";return;}audioSink = new QAudioSink(outputDevice, outputFormat);connect(audioSink, &QAudioSink::stateChanged, this, [=](QtAudio::State state) {if (state == QtAudio::IdleState) {LOG() << "IdleState";this->stopPlay();emit this->soundPlayDone();} else if (state == QAudio::ActiveState) {LOG() << "ActiveState";} else if (state == QAudio::StoppedState) {LOG() << "StoppedState";if (audioSink->error() != QtAudio::NoError) {LOG() << audioSink->error();}}});
}void SoundRecorder::startRecord() {soundFile.open( QIODevice::WriteOnly | QIODevice::Truncate );audioSource->start(&soundFile);
}void SoundRecorder::stopRecord() {audioSource->stop();soundFile.close();emit this->soundRecordDone(RECORD_PATH);
}void SoundRecorder::startPlay(const QByteArray& content) {if (content.isEmpty()) {Toast::showMessage("数据加载中, 请稍后播放");return;}// 1. 把数据写入到临时文件model::writeByteArrayToFile(PLAY_PATH, content);// 2. 播放语音inputFile.setFileName(PLAY_PATH);inputFile.open(QIODevice::ReadOnly);audioSink->start(&inputFile);
}void SoundRecorder::stopPlay() {audioSink->stop();inputFile.close();
}

5. 历史消息调整

补充之前历史消息的遗漏问题,历史消息可以显示文本消息和语音消息,其中点击文件消息可以出触发保存操作;点击语音消息可以触发播放操作 

 具体实现

调用地方在历史消息显示窗口,其中通过判断不同消息类型进行创建

图片历史消息

  • 初始化的时候,如果图片内容存在就直接显示;如果图片为空,那么就通过DataCenter请求图片数据
  • 图片数据从网络中加载完成后,通过更新界面的方法显示到界面上,同时根据窗口大小进行调整

文件历史消息

基本逻辑与图片消息相同,只是多了一个重写鼠标点击操作,点击触发另存为操作

语音历史消息

逻辑和文件消息相同,点击语音消息可以触发播放操作

 

6. 发布程序

借助Qt下的windeployqt.ext实现程序自动获得依赖文件

 release版本中添加外部依赖库

创建好的文件结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/15900.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【CSS】opacity 影响 z-index 不生效

准备知识 一般来说&#xff0c;z-index 不生效的原因有&#xff1a; 父元素的 position 属性&#xff1a; z-index 只对 position 属性为 relative、absolute 或 fixed 的元素有效。 其他元素的 z-index&#xff1a; 如果页面中有其他元素也设置了较高的 z-index&#xff0c;…

Elasticsearch基本概念及使用

Elasticsearch 是一个开源的、分布式的全文搜索和分析引擎&#xff0c;基于 Apache Lucene 构建。它提供了快速的搜索能力&#xff0c;支持大规模的数据分析&#xff0c;广泛应用于日志分析、全文搜索、监控系统和商业智能等领域。ES操作指令是基于restAPI构建&#xff0c;也就…

C语言入门到精通(第六版)——第十六章

16、网络套接字编程 16.1、计算机网络基础 计算机网络技术是计算机技术和通信技术相结合的产物&#xff0c;代表计算机的一个重要发展方向。了解计算机的网络结构&#xff0c;有助于用户开发网络应用程序。 16.1.1、IP地址 为了使网络上的计算机能够彼此识别对方&#xff0c;…

Electron教程1-初学入门

玩转Electron Electron 是什么注意事项环境安装安装 vscode安装 git 第一个实例第二个实例第二个实例解读 总结问题解答 Electron 是什么 Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个…

柠乐音乐 1.3.87 | 界面优美支持无损音乐下载的音乐播放器

柠乐音乐app提供丰富的音乐资源&#xff0c;涵盖流行、摇滚、古典等多种类型音乐&#xff0c;并且全部免费。支持FLAC无损音质音乐免费高速下载。内置独特推荐算法&#xff0c;可根据用户喜好智能推荐音乐。还包括电台播放资源、歌单同步&#xff08;支持网易云音乐和QQ音乐&am…

【资料】网络安全风险评估报告,风险管理报告,网络安全风险管理计划,网络安全网络安全能力验证报(Word原件)

一、概述 1.1工作方法 1.2评估依据 1.3评估范围 1.4评估方法 1.5基本信息 二、资产分析 2.1 信息资产识别概述 2.2 信息资产识别 三、评估说明 3.1无线网络安全检查项目评估 3.2无线网络与系统安全评估 3.3 ip管理与补丁管理 3.4防火墙 四、威胁细类分析 4.1威胁…

change buffer:到底应该选择普通索引还是唯一索引

文章目录 引言第一章&#xff1a;普通索引和唯一索引在查询逻辑与效率上的对比1.1 查询逻辑分析1.2 查询效率对比 第二章&#xff1a;普通索引和唯一索引在更新逻辑与效率上的对比2.1 更新逻辑分析2.2 更新效率对比 第三章&#xff1a;底层原理详解 - 普通索引和唯一索引的区别…

软件工程师简历(精选篇)

【#软件工程师简历#】 一份专业而精准的软件工程师简历&#xff0c;不仅能够全面展示技术实力和项目经验&#xff0c;更是赢得理想工作机会的重要敲门砖。那么&#xff0c;如何撰写一份令人印象深刻的软件工程师简历呢&#xff1f;以下是幻主简历整理的软件工程师简历&#xf…

深度学习推荐系统的工程实现

参考自《深度学习推荐系统》——王喆&#xff0c;用于学习和记录。 介绍 之前章节主要从理论和算法层面介绍了推荐系统的关键思想。但算法和模型终究只是“好酒”&#xff0c;还需要用合适的“容器”盛载才能呈现出最好的味道&#xff0c;这里的“容器”指的就是实现推荐系统…

前缀和技巧解析

前缀和技巧解析 前缀和&#xff08;Prefix Sum&#xff09;是一种常用的算法技巧&#xff0c;用于高效地处理一系列连续子数组和的问题。通过构建一个额外的数组来存储从数组起始位置到当前位置的累计和&#xff0c;可以在常数时间内快速计算任意区间的和。 前缀和应用的典型…

(undone) MIT6.S081 2023 学习笔记 (Day4: LAB3 page tables)

LAB 网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/pgtbl.html 任务1&#xff1a;Speed up system calls 根据网页&#xff0c;操作系统可以通过把部分数据放入用户空间的页表&#xff0c;来使得部分系统调用不用进入内核空间&#xff0c;从而提高速度。我们的…

CSS:怎么把网站都变成灰色

当大家看到全站的内容都变成了灰色&#xff0c;包括按钮、图片等等。这时候我们可能会好奇这是怎么做到的呢&#xff1f; 有人会以为所有的内容都统一换了一个 CSS 样式&#xff0c;图片也全换成灰色的了&#xff0c;按钮等样式也统一换成了灰色样式。但你想想这个成本也太高了…

探索Python文档自动化的奥秘:`python-docx`库全解析

文章目录 探索Python文档自动化的奥秘&#xff1a;python-docx库全解析1. 背景&#xff1a;为何选择python-docx&#xff1f;2. python-docx是什么&#xff1f;3. 如何安装python-docx&#xff1f;4. 简单库函数使用方法创建文档添加段落添加标题添加表格插入图片 5. 应用场景自…

OCP证书如何下载?

访问Oracle CertView网站&#xff1a; 打开网址 https://certview.oracle.com/ &#xff0c;这是Oracle官方提供的证书查询平台 。 登录账号&#xff1a; 使用您的Oracle账号和密码登录CertView。如果您不记得密码&#xff0c;可以通过注册账号时预留的邮箱重置密码 。 查看成…

OBOO鸥柏“触摸屏广告一体机交互”亮相2024中国珠海航展

2024年11月12日&#xff0c;第十五届中国国际航空航天博览会&#xff08;简称中国航展或珠海航展&#xff09;在珠海拉开帷幕。展会现场&#xff0c;既有OBOO鸥柏一大批高精尖液晶显示产品集体亮相&#xff0c;也有航天相关科技领域及飞行表演队炫技蓝天等。在中国航展的各个科…

【智能分子动力学】深度学习驱动分子动力学方法概述

深度学习驱动分子动力学&#xff08;Deep Learning-driven Molecular Dynamics&#xff0c;简称DLDMD&#xff09;方法是将深度学习技术应用于分子动力学模拟中的一种创新方法。这种方法通过深度学习模型来提升传统分子动力学模拟的效率和精度&#xff0c;尤其是在复杂系统的建…

(69)基于Hilbert(希尔伯特)变换的调相信号解调的MATLAB仿真

文章目录 前言一、希尔伯特变换二、相位调制1.基本原理2.调制特点3.应用 三、使用希尔伯特变换进行相位解调的原理1. 解调原理2.算法优点 四、MATLAB仿真1. 仿真代码2. 仿真结果 总结 前言 本文首先介绍了相位调制技术&#xff0c;然后说明了使用希尔伯特变换进行调相信号解调…

ISUP协议视频平台EasyCVR视频设备轨迹回放平台智慧农业视频远程监控管理方案

在当今快速发展的农业领域&#xff0c;智慧农业已成为推动农业现代化、助力乡村全面振兴的新手段和新动能。随着信息技术的持续进步和城市化进程的加快&#xff0c;智慧农业对于监控安全和智能管理的需求日益增长。 视频设备轨迹回放平台EasyCVR作为智慧农业视频远程监控管理方…

Python——NumPy库的简单用法,超级详细教程使用

一、什么是NumPy库 NumPy&#xff1a;它是python的一个科学计算库函数&#xff0c;它是由c语言编写的 它应用于数据处理、机器学习、图像处理、文件操作等等 二、array函数 这里导入库numpy&#xff0c;命名为np&#xff0c;后面的np都是代表着是numpy函数 array函数表示创建…

【postman】怎么通过curl看请求报什么错

获取现成的curl方式&#xff1a; 1&#xff0c;拿别人给的curl 2&#xff0c;手机app界面通过charles抓包&#xff0c;点击接口复制curl 3&#xff0c;浏览器界面-开发者工具-选中接口复制curl 拿到curl之后打开postman&#xff0c;点击import&#xff0c;粘贴curl点击send&am…